Skip to main content

Auth & Device Registration

Authentication in the boilerplate has two distinct layers that work in sequence:

  1. Auth0 — proves who the user is (email address, identity)
  2. Device registration — proves this specific device is authorized to make MPC requests

Both layers are required before any key generation or signing can happen.

The full flow

Sign-in with Auth0

The sign-in flow lives in api/auth/useSignIn.ts. It uses the react-native-auth0 SDK to open the Auth0 Universal Login, gets credentials, registers the user with auth-svc, and stores the user's identity in the auth store.

// api/auth/useSignIn.ts
const { authorize, clearCredentials } = useAuth0();

await authorize({
scope: `openid profile email offline_access ${auth0Config.scope}`,
additionalParameters: { prompt: 'login' },
audience: auth0Config.audience,
});

// Retrieve the access token from the credentials manager
const cred = await auth0Client.credentialsManager.getCredentials();

// Register the user with auth-svc
await APIClient.registerUser({ access_token: cred.accessToken });

// Get user info and save to store
const user = await auth0Client.auth.userInfo({ token: cred.accessToken, tokenType: TokenType.bearer });
signIn({ email: user.email, id: user.sub });

The offline_access scope enables refresh tokens, so returning users are silently re-authenticated on app startup without seeing the login screen again. This is handled in provider/AuthProvider.tsx.

Device registration

Every device that wants to perform MPC operations must be registered with auth-svc. The device is identified by an ECDSA public key stored in the platform's secure hardware:

  • iOS — iOS Secure Enclave (Keychain)
  • Android — Android Keystore with StrongBox

The device registration challenge-response is handled in libs/secure-key.ts and the registration API calls:

// libs/secure-key.ts

// 1. Create the device keypair in TEE (no-op if it already exists)
SecureKey.createIfNotExistSecureKey(secureKeyAlias);

// 2. MessageSigner — signs all messages from silent-shard-sdk to the cloud node
// The cloud node verifies device identity using this key's public key
export const getMessageSigner = () => {
return SecureKey.createMessageSigner(secureKeyAlias);
};

Learn more about the MessageSigner — how the TEE key is used for transport-level authentication between the SDK and the cloud node.

→ Message Signer docs

// Device registration (api/auth/useRegisterDevice.ts)

// 1. Get the device ID (ECDSA public key hex)
const { publicKeyHex } = getSecureKey();

// 2. Fetch a challenge from auth-svc
const { challenge } = await APIClient.getChallenge(publicKeyHex);

// 3. Sign the challenge with the TEE private key
const { sign } = getSecureKey();
const signature = sign(challenge);

// 4. Register the device — auth-svc stores the device_id and links it to the user
await APIClient.registerDevice({ device_id: publicKeyHex, signature });

The device private key never leaves the secure hardware. Only the signature is transmitted. Even if network traffic is intercepted, it is useless without the device.

Silent sign-in on startup

When the app relaunches, provider/AuthProvider.tsx silently checks for existing credentials. If a valid (or refreshable) token exists, the user is restored without seeing the login screen. If not, they are redirected to sign-in.