Non-custody 2/2: customer login and delegated wallets
Give your users ownership and power to use their wallets directly.
For this example we will use Dfns typescript SDK to register end users (your customers) to the Dfns API and let them own their wallets and use it directly.
In this scenario, the wallets live in your organization but belong to your end users. Only they can do transactions and use the private key to sign messages. All actions require to be signed by their passkey. The SDK greatly simplifies that process.
Clone the example
This example contains all the functions you need to get started with login and wallets delegation.
git clone https://github.com/dfns/dfns-sdk-ts.git --no-checkout
cd dfns-sdk-ts
git sparse-checkout set examples/sdk/nextjs-delegated
git checkout m
cd examples/sdk/nextjs-delegated/
Edit next.config.ts to remove the line:
transpilePackages: ['@dfns/sdk-browser'],
Update the hardcoded dependencies and install the project:
npm i && npm remove @dfns/sdk @dfns/sdk-browser @dfns/sdk-keysigner && npm i @dfns/sdk @dfns/sdk-browser @dfns/sdk-keysigner
Prepare the environment
Copy .env.example
to a new file .env.local
and set the following values,
DFNS_API_URL
:https://api.dfns.io
or.ninja
depending on the environment you are usingDFNS_ORG_ID
: your Organization ID (found in the Dashboard: click you email then "Account")DFNS_CRED_ID
: theSigning Key Cred ID
created when you registered the service account. On the dashboard head to Settings > Service Accounts to copy it.DFNS_PRIVATE_KEY
: the private key from the step 'generate a keypair', the newlines should not be a problemDFNS_AUTH_TOKEN
: theauthToken
from above, the value should start witheyJ0...
NEXT_PUBLIC_PASSKEYS_RELYING_PARTY_ID
: the passkey relying party id, aka, the domain where your app lives (Read more here). We advise using the root domain (eg.acme.com
, notapp.acme.com
) for more passkey flexibility (so that passkey is re-usable on subdomains). During development on localhost, you can set it tolocalhost
.NEXT_PUBLIC_PASSKEYS_RELYING_PARTY_NAME
: A string representing the name of the relying party, aka, your company name (e.g. "Acme"). The user will be presented with that name when creating or using a passkey.
Run the development server
npm run dev
And finally open http://localhost:3000
Service account action signing
As any user on Dfns, your service account needs to sign its actions. The file app/api/clients.ts
uses the Dfns SDK to register the service account private key into a signer, as well as a API client that will take care of gathering the right information and requesting signing when necessary.
export const apiClient = (authToken?: string) => {
const signer = new AsymmetricKeySigner({
credId: process.env.DFNS_CRED_ID!,
privateKey: process.env.DFNS_PRIVATE_KEY!.replace(/\\n/g, '\n'),
})
return new DfnsApiClient({
orgId: process.env.DFNS_ORG_ID!,
authToken: authToken ?? process.env.DFNS_AUTH_TOKEN!,
baseUrl: process.env.DFNS_API_URL!,
signer,
})
}
Delegated registration
The service account can use Delegated Registration to register an end user (a.k.a. one of your customers) to Dfns. Registering this user to your platform and validating his login is out of scope here, we just consider that you have properly authenticated your user before creating his Dfns account. The flow is similar to users registration:
Requesting a challenge from Dfns. Note that the username comes from the frontend in this example, but it doesn't have to, you could be providing it directly from your backend.
const client = apiClient()
const challenge = await client.auth.createDelegatedRegistrationChallenge({
body: { kind: 'EndUser', email: username },
})
Asking the customer to create a new credentials and sign the challenge with it. This is done via the web front end:
import { WebAuthnSigner } from '@dfns/sdk-browser'
[...]
const attestation = await webauthn.create(challenge)
Registering the end user credentials
Note that you can directly create a delegated wallet directly during registration.
const client = apiClient(temporaryAuthenticationToken)
const registration = await client.auth.registerEndUser({
body: {
...signedChallenge,
wallets: [{ network: 'EthereumSepolia' }],
},
})
Delegated login
In a similar flow, once you have authenticated your user on your platform, you can log him into Dfns in order to let him use his wallet.
const client = apiClient()
const login = await client.auth.delegatedLogin({ body: { username } })
You will get a token back from this call, that you can later use in all for all delegated actions.
Delegated calls to the API
The SDK provides an easy way to call the API with your delegated end user credentials:
export const delegatedClient = (authToken: string) => {
return new DfnsDelegatedApiClient({
orgId: process.env.DFNS_ORG_ID!,
authToken,
baseUrl: process.env.DFNS_API_URL!,
})
}
The API requires the end user to sign any modifying action with his passkey. For instance when requesting Dfns to issue a signature using his wallet:
Request a challenge from Dfns. Note that the username comes from the frontend in this example, but it doesn't have to, you could be providing it directly from your backend.
const client = delegatedClient(authToken) // end user token here
[...]
const challenge = await client.wallets.generateSignatureInit({ walletId, body })
Asking the customer to create a new credentials and sign the challenge with it. This is done via the web front end:
import { WebAuthnSigner } from '@dfns/sdk-browser'
[...]
const attestation = await webauthn.sign(challenge)
Finally calling the signature API to trigger the action.
const client = delegatedClient(authToken)
[...]
const signature = await client.wallets.generateSignatureComplete(
{
walletId,
body: requestBody,
},
signedChallenge
)
Going further
Head to the SDK docs to better understand:
The backend client, used with your service account token, with the action key signer for your service account to sign its actions automatically
The backend "delegated" client, used with your delegated end user token
The frontend browser sdk, to simplify the signing process with WebAuthn
More information about the other SDKs: Dfns SDKs
Congratulations! you now have all the tools to integrate Dfns into your own application.
This is the end the Getting Started wizard.
Onboarding to DfnsLast updated