Start building: login & create a wallet via API
Building your own app? Learn how to consume the Dfns APIs using your Employee login.
1
Setup the environment
npx create-react-app dfns-direct-loginnpm startimport "./App.css";
import { useRef, useState } from "react";
const baseURL = "https://api.dfns.io"; // .io for prod, .ninja for staging
const credentialName = "DemoLogin";
const DEFAULT_ORG_ID = "or-*****-*****-****************"; // your org id here for convenience
const DEFAULT_EMAIL = "*******@mycompany.com"; // your email here for convenience
function App() {
const codeRef = useRef();
const [registrationState, setRegistrationState] = useState({});
const handleRegister = () => {
const code = codeRef.current.value;
/* HERE GOES THE CODE FOR REGISTERING A CREDENTIAL */
// uncomment when completed
// .then(setRegistrationState)
// .catch(setRegistrationState)
};
const orgIdRef = useRef(DEFAULT_ORG_ID);
const emailRef = useRef(DEFAULT_EMAIL);
const [loginState, setLoginState] = useState({});
const handleLogin = () => {
const orgId = orgIdRef.current.value;
const email = emailRef.current.value;
/* HERE GOES THE CODE FOR LOGIN */
// uncomment when completed
// .then(setLoginState)
// .catch(setLoginState)
};
return (
<div className="App">
<h1>User login demo app</h1>
<h2>1. Credential registration</h2>
<div>
<p>If not done already, you need to register on this website. Get a Credential Code from the <a rel="noreferrer" href="https://app.dfns.io/v3/settings/authentication/credentials" target="_blank">dashboard</a> (Settings > Authentication > Credentials)</p>
<p><label>Enter your Credential Code: <input type="text" ref={codeRef}></input></label></p>
<p><button onClick={handleRegister}>Register</button></p>
<p>Current state: <small><code>{JSON.stringify(registrationState, null, 2)}</code></small></p>
</div>
<h2>2. User login</h2>
<div>
<p><label>Enter your Organization Id Code: <input type="text" ref={orgIdRef} defaultValue={DEFAULT_ORG_ID}></input></label</p>
<p><label>Enter your Email address: <input type="email" ref={emailRef} defaultValue={DEFAULT_EMAIL}></input></label></p>
<p><button onClick={handleLogin}>Login</button></p>
<p>Current state: <small><code>{JSON.stringify(loginState, null, 2)}</code></small></p>
</div>
</div>
);
}
export default App;
/* Helpers to convert challenges from Dfns format to WebAuthn format */
function base64url_decode(value) {
const m = value.length % 4;
return Uint8Array.from(
atob(
value
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(value.length + (m === 0 ? 0 : 4 - m), "=")
),
(c) => c.charCodeAt(0)
).buffer;
}
function base64url_encode(buffer) {
return btoa(
Array.from(new Uint8Array(buffer), (b) => String.fromCharCode(b)).join("")
)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
function string_to_ArrayBuffer(value) {
var enc = new TextEncoder();
return enc.encode(value).buffer;
}
2
Registering a new credential
const handleRegister = () => {
const code = codeRef.current.value;
/* HERE GOES THE CODE FOR REGISTERING A CREDENTIAL */
// uncomment when completed
// .then(setRegistrationState)
// .catch(setRegistrationState)
};fetch(`${baseURL}/auth/credentials/code/init`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
code: registrationCode,
credentialKind: "Fido2",
}),
}).then((response) => response.json()).then((dfnsChallenge) => {
return navigator.credentials.create({
publicKey: {
challenge: string_to_ArrayBuffer(dfnsChallenge.challenge),
pubKeyCredParams: dfnsChallenge.pubKeyCredParams,
rp: { name: credentialName },
user: {
displayName: dfnsChallenge.user.displayName,
id: string_to_ArrayBuffer(dfnsChallenge.user.id),
name: dfnsChallenge.user.name,
},
attestation: dfnsChallenge.attestation,
excludeCredentials: dfnsChallenge.excludeCredentials.map(({ id, type }) => ({
id: base64url_decode(id),
type,
})),
authenticatorSelection: dfnsChallenge.authenticatorSelection,
}
}).then((browserCredential) => {
return {
challengeIdentifier: dfnsChallenge.challengeIdentifier,
credentialName: credentialName,
credentialKind: dfnsChallenge.kind,
credentialInfo: {
credId: browserCredential.id,
attestationData: base64url_encode(browserCredential.response.attestationObject),
clientData: base64url_encode(browserCredential.response.clientDataJSON),
},
};
});
}).then((signedChallenge) => fetch(`${baseURL}/auth/credentials/code/verify`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(signedChallenge)
}).then((response) => response.json())3
Login using your 🔑 Passkey
const handleLogin = () => {
const orgId = orgIdRef.current.value;
const email = emailRef.current.value;
/* HERE GOES THE CODE FOR LOGIN */
// uncomment when completed
// .then(setLoginState)
// .catch(setLoginState)
};fetch(`${baseURL}/auth/login/init`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
orgId: orgId,
username: email,
})
}).then((response) => response.json()).then(signChallenge)function signChallenge(dfnsChallenge) {
return navigator.credentials.get({
publicKey: {
challenge: string_to_ArrayBuffer(dfnsChallenge.challenge),
allowCredentials: dfnsChallenge.allowCredentials.webauthn.map(({ id, type }) => ({
id: base64url_decode(id),
type,
})),
userVerification: dfnsChallenge.userVerification
}
})
.then((browserCredential) => {
console.log(browserCredential);
return {
challengeIdentifier: dfnsChallenge.challengeIdentifier,
firstFactor: {
kind: "Fido2",
credentialAssertion: {
credId: browserCredential.id,
clientData: base64url_encode(browserCredential.response.clientDataJSON),
authenticatorData: base64url_encode(browserCredential.response.authenticatorData),
signature: base64url_encode(browserCredential.response.signature),
userHandle: base64url_encode(browserCredential.response.userHandle),
},
},
};
});
}.then((signedChallenge) => fetch(`${baseURL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(signedChallenge),
}).then((response) => response.json())4
First API request: read the wallet list
fetch(`${baseURL}/wallets`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
}
}).then((response) => response.json());curl -H "Authorization: Bearer <token>" -H "Content-Type: application/json" https://api.dfns.io/wallets5
User Actions: signing requests
const userActionPayload = { network: "Ethereum" };
const userActionHttpMethod = "POST";
const userActionHttpPath = "/wallets";fetch(`${baseURL}/auth/action/init`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
userActionPayload: JSON.stringify(userActionPayload),
userActionHttpMethod,
userActionHttpPath,
}),
}).then((response) => response.json()).then(signChallenge).then((signedChallenge) => fetch(`${baseURL}/auth/action`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(signedChallenge),
}))
.then((response) => response.json())
.then((response) => response.userAction).then((userAction) => fetch(`${baseURL}${userActionHttpPath}`, {
method: userActionHttpMethod,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
"X-DFNS-USERACTION": userAction,
},
body: JSON.stringify(userActionPayload)
}))
.then((response) => response.json());Last updated