Skip to main content
This guide explains how to register and authenticate your application’s end users so they can use Dfns wallets.
This is different from SSO for your organization, which allows your team members to access the Dfns Dashboard using enterprise identity providers like Okta or Entra ID.

Overview

Dfns supports two methods for end user authentication:
MethodDescriptionUse case
Delegated registrationYour service account creates Dfns users on behalf of end usersFull control, works with any auth system
Social registrationUsers authenticate directly with Google, JWT goes to DfnsSimpler setup, no service account needed
We recommend delegated registration for most applications—it gives you full control over user management and works with any identity provider. Both methods involve two phases:
  • Registration — One-time setup when a user first joins. Creates a Dfns user and passkey credential.
  • Login — For returning users. Authenticates the user and returns a Dfns auth token.

Delegated registration

In this method, your backend uses a service account to create and manage Dfns users. This gives you full control over user management and works with any authentication system.
See a complete working example in the nextjs-delegated SDK example.

Registration (new users)

Use the Delegated Registration flow. Step 1: User authenticates with your system Use your existing auth flow (NextAuth, Auth0, Firebase, custom, etc.). Step 2: Service account creates Dfns user
Backend
import { DfnsApiClient } from '@dfns/sdk'
import { AsymmetricKeySigner } from '@dfns/sdk-keysigner'

const signer = new AsymmetricKeySigner({
  credId: process.env.DFNS_SERVICE_ACCOUNT_CRED_ID,
  privateKey: process.env.DFNS_SERVICE_ACCOUNT_PRIVATE_KEY,
})

const dfns = new DfnsApiClient({
  authToken: process.env.DFNS_SERVICE_ACCOUNT_TOKEN,
  baseUrl: process.env.DFNS_API_URL,
  signer,
})

const challenge = await dfns.auth.createDelegatedUserRegistration({
  body: {
    email: user.email,
    kind: 'EndUser',
  },
})

return { challenge }
Step 3: User creates passkey
Frontend
import { WebAuthn } from '@dfns/sdk-browser'

const webauthn = new WebAuthn({ rpId: 'your-domain.com' })
const attestation = await webauthn.create(challenge)

await fetch('/api/complete-registration', {
  method: 'POST',
  body: JSON.stringify({
    temporaryAuthenticationToken: challenge.temporaryAuthenticationToken,
    attestation,
  }),
})
Step 4: Complete registration
Backend
const newUser = await dfns.auth.createDelegatedUserRegistration({
  body: {
    temporaryAuthenticationToken,
    firstFactorCredential: attestation,
  },
})

// Store mapping: your user ID -> Dfns user ID
await db.users.update({
  where: { id: yourUserId },
  data: { dfnsUserId: newUser.id },
})

Login (returning users)

Use the Delegated Login flow. Step 1: Service account initiates login
Backend
const challenge = await dfns.auth.createDelegatedUserLogin({
  body: {
    username: user.email,
  },
})

return { challenge }
Step 2: User signs with passkey
Frontend
const assertion = await webauthn.sign(challenge)

await fetch('/api/complete-login', {
  method: 'POST',
  body: JSON.stringify({
    challengeIdentifier: challenge.challengeIdentifier,
    assertion,
  }),
})
Step 3: Complete login
Backend
const { token } = await dfns.auth.completeDelegatedUserLogin({
  body: {
    challengeIdentifier,
    firstFactor: assertion,
  },
})

return { token }

Social registration

In this method, users authenticate directly with an identity provider (like Google) and pass the JWT token to Dfns. No service account is required.
See the complete auth-social SDK example for a working implementation.

Registration (new users)

Use the Social Registration flow.
Frontend
// After Google OAuth, you have the idToken
const challenge = await fetch('https://api.dfns.io/auth/registration/social', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orgId: DFNS_ORG_ID,
    socialLoginProviderKind: 'Oidc',
    idToken: googleIdToken,
  }),
}).then(res => res.json())

// User creates passkey
const webauthn = new WebAuthn({ rpId: 'your-domain.com' })
const attestation = await webauthn.create(challenge)

// Complete registration
const newUser = await fetch('https://api.dfns.io/auth/registration/enduser', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${challenge.temporaryAuthenticationToken}`,
  },
  body: JSON.stringify({
    firstFactorCredential: attestation,
  }),
}).then(res => res.json())

Login (returning users)

Use the Social Login API.
Frontend
const { token } = await fetch('https://api.dfns.io/auth/login/social', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orgId: DFNS_ORG_ID,
    socialLoginProviderKind: 'Oidc',
    idToken: googleIdToken,
  }),
}).then(res => res.json())

Security considerations

  • Never expose your service account credentials to the frontend
  • Validate tokens on your backend before creating Dfns users
  • Use HTTPS for all communications
  • Store the mapping between your user IDs and Dfns user IDs securely
  • Implement rate limiting on registration endpoints