Skip to content

Conversation

@afeight
Copy link
Contributor

@afeight afeight commented Dec 5, 2025

feat: add DeviceService and OnboardingService for device onboarding

Summary

Adds device identity key management and onboarding functionality to the Swift SDK, translating the StartOnboardingEventHandler behavior from the open-signer TypeScript implementation.

New Components:

  • DeviceService - Actor that generates P-256 ECDH identity keys, stores them in UserDefaults, computes device ID (SHA-256 hash of public key, hex encoded), and provides serialized public key (base64 encoded)
  • OnboardingService - Actor that orchestrates the startOnboarding API call with device ID and encryption context
  • AuthData - Struct containing JWT and apiKey for authenticated requests

Note: The getMasterSecret step is intentionally skipped per request - this can be added in a follow-up PR.

Review & Testing Checklist for Human

  • Public key format verification: Verify that rawRepresentation from CryptoKit P-256 matches what the backend expects. The open-signer uses Web Crypto's exportKey("raw", publicKey) - confirm these produce identical byte sequences.
  • Device ID hash verification: Confirm the SHA-256 hash of the raw public key bytes produces the same hex string as the TypeScript implementation. Byte ordering differences between platforms could cause mismatches.
  • API endpoint path: Verify /ncs/v1/signers/start-onboarding is the correct endpoint path for the backend.
  • UserDefaults storage: Confirm UserDefaults is acceptable for storing private keys in this context. Note: UserDefaults is not encrypted - this was explicitly requested but may need reconsideration for production.

Test Plan

  1. Run swift test to execute unit tests (CI will handle this)
  2. For integration testing, create a DeviceService instance, call getId() and getSerializedIdentityPublicKey(), and verify the formats match expected values
  3. Initialize OnboardingService with a real CrossmintService and DeviceService, call startOnboarding(authId:authData:) with valid credentials, and verify the API call succeeds

Notes

- Add DeviceService actor for managing device identity keys (P-256 ECDH)
  - Generate and store identity keys using UserDefaults
  - Compute device ID as SHA-256 hash of public key (hex encoded)
  - Get serialized public key (base64 encoded)
- Add AuthData struct for JWT and apiKey authentication
- Add OnboardingService actor for handling startOnboarding API calls
  - Implements the startOnboarding flow from open-signer
  - Sends device ID and encryption context (public key) to API

Co-Authored-By: [email protected] <[email protected]>
@devin-ai-integration
Copy link

Original prompt from [email protected]
you've done great so far in the crossmint-swift-sdk repo, now let’s define a new PR that builds on top of PR #22 that you’ve made
next, I’d like to replicate the behaviour of this handler in the open-signer repo: 

`StartOnboardingEventHandler`
this first generates identity keys:

const keys = await crypto.subtle.generateKey(
ECDH_KEY_SPEC,
false, // non-extractable
["deriveBits", "deriveKey"], // to derive encryption keys
);

and then saves the result. For now, we can do so using UserDefaults
then, it makes the following call to our api to start onboarding:

await this.services.api.startOnboarding(
{
...payload.data,
encryptionContext: {
publicKey:
await this.services.device.getSerializedIdentityPublicKey(),
},
deviceId,
},
payload.authData,
);

`deviceId` is a hashed version of our identity keys, demonstrated with this code from `DeviceService.getId():`

const { publicKey } = await this.getIdentityKeys();
const bytes = await crypto.subtle.exportKey("raw", publicKey);
const hash = await crypto.subtle.digest(HASH_ALGORITHM, bytes);
return encodeBytes(new Uint8Array(hash), "hex");

`authData` is a struct: `{ jwt: string, apiKey: string }` that will be passed in from the caller
skip the step `await this.services.secret.getMasterSecret` for now

@devin-ai-integration
Copy link

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants