Bind Passkey To User
Prerequisites​
- Set up a developer account
- Create an application
- Create at least one identity
Overview
In order to authenticate with Beyond Identity, you need a Universal Passkeys. This guide will walk you through binding a passkey to an identity.
Passkeys are generated through a binding job. On creation of a binding job, a binding link will be generated. That binding link can be used in the Embedded SDK to bind a passkey to a specific device or browser. This passkey will be stored in the user's device's hardware root of trust (i.e. secure enclave).
Bind Passkey with Admin Console​
A passkey can be bound to an identity directly from the Beyond Identity Admin Console. Under the realm that hosts your application, look for the "PROJECT MANAGEMENT" tab and select "Identities". A list of identities should be displayed. Tap on an identity that you would like to bind to a passkey. Next click on the "Add a passkey" button. Select an application and click "Proceed & send email".
The user will receive a registration email with a link to generate a passkey. Clicking the link will redirect the end user to the Beyond Identity Cloud. The Beyond Identity Cloud will look up the Authenticator Config that is associated with that passkey creation link and redirect the end user to your application using the Authenticator Config's Invoke URL.
Bind Passkey by API​
Before making any API calls you'll want to generate an API access token. Check out API Tokens for help creating an access token.
A binding job can be generated through the Beyond Identity API. There are two delivery_method
options to consider:
- RETURN: indicates that a binding link will be returned to the caller upon creation of the binding job. The developer can then deliver that link to the end user however they want (in-line, sms, email, etc). This is the suggested method if you want the end user to create a passkey without having to leave your application.
- EMAIL: indicates that a passkey creation email will be sent to the end user. The end user will receive the email and click the passkey creation link. Clicking the link will redirect the end user to the Beyond Identity Cloud. Beyond Identity Cloud will look up the Authenticator Config that is associated with that passkey creation link and redirect the end user to the Authenticator Config's Invoke URL with an appended
/bind
path. TheInvoke URL
should be an HTTP request handler in your application. Once the user has been redirected to your application, you as the developer can handle the binding link in the SDK.
- RETURN
Setup your backend
Get Access Token for API calls​
Before making any API calls you'll want to generate an API access token. Check out API Tokens for help with creating an access token.
Create an Identity​
If the user is creating a new account, you'll want to create an identity with the user's information such as email address and username. Collect this information on your front end and create the identity on your backend. You'll need the identityId
to bind to a passkey. Check out User Provisioning for help creating an identity.
Get Binding Link for Identity​
In order to get a binding link for a passkey, you need to create a binding job for an existing identity. The following code snippet uses the RETURN
delivery method. This is the fastest way to get a binding link as this method indicates that a binding link will be returned to the caller upon creation of the binding job. This binding link is the link you will send to your application to complete the passkey binding process.
- Curl
- CSharp
- Dart
- Go
- Java
- Node
- Python
- Ruby
- Rust
/credential-binding-jobs
1 2 3 4 5
curl "https://api-$(REGION).beyondidentity.com/v1/tenants/$(TENANT_ID)/realms/$(REALM_ID)/identities/$(IDENTITY_ID)/credential-binding-jobs" \ -X POST \ -H "Authorization: Bearer $(API_TOKEN)" \ -H "Content-Type: application/json" \ -d "{\"job\":{\"delivery_method\":\"RETURN\",\"authenticator_config_id\":\"$(AUTHENTICATOR_CONFIG_ID)\"}}"
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
Setup your front end
Handle Binding Link in the SDK​
Once you have a binding link generated, feed that link into your application to complete the binding process. You'll need to query your backend for the link and then feed it into the SDK. Upon success, a private key will have been created in the device's hardware trust module and the corresponding public key will have been sent to the Beyond Identity Cloud. At this point the user has a passkey enrolled on this device.
Don't forget to initalize your SDK ahead of time. For more information see SDK Setup.
- Javascript
- Kotlin
- Swift
- React Native
- Flutter
const bindResponse = await embedded.bindPasskey(bindingLink);
console.log(bindResponse);
EmbeddedSdk.bindPasskey(url = bindingLink)
.onEach { result ->
result.onSuccess { success ->
Timber.d("Bind Passkey success = $success")
}
result.onFailure { failure ->
Timber.e("Bind Passkey failure = $failure")
}
}
Embedded.shared.bindPasskey(url: bindingLink) { result in
switch result {
case let .success(bindResponse):
print(bindResponse)
case let .failure(error):
print(error.localizedDescription)
}
}
const bindResponse = await Embedded.bindPasskey(bindingLink);
console.log(bindResponse);
var bindResponse = await EmbeddedSdk.bindPasskey(bindingLink);
debugPrint(bindResponse);
Setup your backend
Get Access Token for API calls​
Before making any API calls you'll want to generate an API access token. Check out API Tokens for help with creating an access token.
Create an Identity​
If the user is creating a new account, you'll want to create an identity with the user's information such as email address and username. Collect this information on your front end and create the identity on your backend. You'll need the identityId
to bind to a passkey. Check out User Provisioning for help creating an identity.
Get Binding Link for Identity​
In order to get a binding link for a passkey, we need to create a binding job for an existing identity. The following code snippet uses the EMAIL
delivery method. This method will send your user an email with a link that, when clicked, redirects the user to your application and provides your application with a binding link. If you wish to configure email branding, visit the Admin Console. This binding link is the link your application will use to complete the passkey binding process.
- Curl
- CSharp
- Dart
- Go
- Java
- Node
- Python
- Ruby
- Rust
/credential-binding-jobs
1 2 3 4 5
curl "https://api-$(REGION).beyondidentity.com/v1/tenants/$(TENANT_ID)/realms/$(REALM_ID)/identities/$(IDENTITY_ID)/credential-binding-jobs" \ -X POST \ -H "Authorization: Bearer $(API_TOKEN)" \ -H "Content-Type: application/json" \ -d "{\"job\":{\"delivery_method\":\"EMAIL\",\"authenticator_config_id\":\"$(AUTHENTICATOR_CONFIG_ID)\",\"post_binding_redirect_uri\":\"$(APP_REDIRECT_URI)\"}}"
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
/credential-binding-jobs
Setup your front end
Deep linking​
When we send out a passkey email, the link will redirect to your application specified by the Authenticator Config's Invoke URL. This URI should either be an app scheme or a Universal URL / App link.
While app schemes are generally easier to set up, Universal URLs and App Links are recommended as they provide protection against App Scheme hijacking.
- Kotlin
- Swift
- React Native
- Flutter
Android supports two ways of deep linking into an application. Pick one of the following and follow the guide:
Apple supports two ways of deep linking into an application. Pick one of the following and follow the guide:
If you are using Expo, follow the deep linking guide.
If you are using an app created with the react native CLI, follow the Kotlin and Swift guides to setting up deep linking on React Native.
Also check out React Native Linking:
import { useEffect, useState } from 'react';
import { Linking } from 'react-native';
export default function useDeepLinkURL() {
const [linkedURL, setLinkedURL] = (useState < string) | (null > null);
// If the app is not already open
useEffect(() => {
const getUrlAsync = async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl !== null) {
setLinkedURL(decodeURI(initialUrl));
}
};
getUrlAsync();
}, []);
// If the app is already open
useEffect(() => {
const callback = ({ url }: { url: string }) => setLinkedURL(decodeURI(url));
const linkingEventListener = Linking.addEventListener('url', callback);
return () => {
linkingEventListener.remove();
};
}, []);
return { linkedURL };
}
// in App.js
const { linkedURL } = useDeepLinkURL();
useEffect(() => {
async function register() {
if (linkedURL !== null) {
console.log('intercepted:', linkedURL);
}
}
register();
}, [linkedURL]);
Follw the Deep linking flutter guide.
Handle link in the SDK​
The binding link that is redirected to your application will take on the following form. A /bind
path will be appended to your Invoke URL as well as several other query parameters. Use a "/bind" route to intercept this url in your application:
$invoke_url/bind?api_base_url=<api_base_url>&tenant_id=<tenant_id>&realm_id=<realm_id>&identity_id=<identity_id>&job_id=<job_id>&token=<token>
Once you receive the incoming URL, pass it into the SDK to complete the binding process.
Upon success, a private key will have been created in the device's hardware trust module and the corresponding public key will have been sent to the Beyond Identity Cloud. At this point the user has a passkey enrolled on this device.
Don't forget to initalize your SDK ahead of time. For more information see SDK Setup.
- Javascript
- Kotlin
- Swift
- React Native
- Flutter
This guide uses NextAuth for all OAuth/OIDC flows. All code snippets are provided in the context of the NextAuth Example App. Note that your application must be set up as a confidential application.
Create a bind.tsx
page under /next-auth-example/pages
. As long as your Invoke URL
is configured properly in your Authenticator Config, this is the page that will be redirected to during an authorization flow. Copy the following code snippet into that page.
import { useEffect, useState } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
const BindPasskeyFromRedirect = () => {
const [bindPasskeyResult, setBindPasskeyResult] = useState('');
useEffect(() => {
// -- 1
const bindPasskey = async () => {
const BeyondIdentityEmbeddedSdk = await import(
'@beyondidentity/bi-sdk-js'
);
let embedded = await BeyondIdentityEmbeddedSdk.Embedded.initialize();
// -- 2
if (embedded.isBindPasskeyUrl(window.location.href)) {
// Only bind if the URL is a "bind" URL
let bindPasskeyUrl = window.location.href;
// -- 3
embedded
.bindPasskey(bindPasskeyUrl)
.then(setBindPasskeyResult)
.catch((error) => {
setBindPasskeyResult(error.toString());
});
}
};
bindPasskey().catch(console.error);
}, []);
return (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
}}
>
<div className="container">
<div className="row">
{bindPasskeyResult.length > 0 && (
<div className="row row-cols-1 row-cols-md-1 mt-3">
<div className="col">
{JSON.stringify(bindPasskeyResult, null, 2)}
</div>
</div>
)}
</div>
</div>
</div>
);
};
export default BindPasskeyFromRedirect;
What's happening here?
- The
useEffect
is only called once on page load. In this function, we initialize the Beyond Identity SDK and useembedded.isBindPasskeyUrl
to check if the current page that was redirected to is in fact a validbind
URL. - If the URL is valid, we pull the URL using
window.location.href
and pass that directly intoembedded.bindPasskey
. embedded.bindPasskey
completes the binding process and stores a passkey in the user's browser.
Validate the incoming URL with isBindPasskeyUrl
in your Activity either using onCreate
or onNewIntent
. When the URL is intercepted, pass it to bindPasskey
.
intent?.data?.let { uri ->
when {
EmbeddedSdk.isBindPasskeyUrl(uri.toString()) -> {
EmbeddedSdk.bindPasskey(
url = uri.toString(),
) {
...
}
}
...
}
}
Catch a url from your AppDelegate or SceneDelegate. You can validate the incoming URL with isBindPasskeyUrl
. When the URL is intercepted, pass it to bindPasskey
.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url, Embedded.shared.isBindPasskeyUrl(url) {
Embedded.shared.bindPasskey(url: bindingLink) { result in
switch result {
case let .success(bindResponse):
print(bindResponse)
case let .failure(error):
print(error.localizedDescription)
}
}
}
}
You can validate the incoming URL with isBindPasskeyUrl
. When the URL is intercepted, pass it to bindPasskey
.
const isBindUrl = await Embedded.isBindPasskeyUrl(url);
if (isBindUrl) {
const bindResponse = await Embedded.bindPasskey(bindingLink);
console.log(bindResponse);
}
Use Flutter's getInitialUri
from uni_links to intercept the Url. You can validate the incoming URL with isBindPasskeyUrl
. When the URL is intercepted, pass it to bindPasskey
.
final uri = await getInitialUri();
if (uri != null) {
bool isBindUrl = await EmbeddedSdk.isBindPasskeyUrl(uri);
if (isBindUrl) {
BindPasskeyResponse bindResponse = await EmbeddedSdk.bindPasskey(uri);
debugPrint(bindResponse);
}
}