File length: 19719
# Authentication - Passkeys - Quickstart
Source: https://supertokens.com/docs/authentication/passkeys/initial-setup
## Overview
This page shows you how to add the **Passkeys** authentication method to your project.
The tutorial creates a login flow, rendered by either the **Prebuilt UI** components or by your own **Custom UI**.
## Before you start
These instructions assume that you already have gone through the main [quickstart guide](/docs/quickstart/introduction).
If you have skipped that page please follow the tutorial and return here once you're done.
:::info SDK Support
WebAuthn (Passkeys) authentication is available in the **Node.js SDK** and **Python SDK**.
:::
## Steps
### 1. Initialize the frontend SDK
#### 1.1 Add the `WebAuthn` recipe in your main configuration file.
```tsx
// highlight-next-line
SuperTokens.init({
appInfo: {
apiDomain: "...",
websiteDomain: "...",
appName: "...",
},
recipeList: [
// highlight-start
WebAuthn.init(),
// highlight-end
Session.init()
]
});
```
#### 1.2 Include the pre-built UI components in your application.
In order for the **pre-built UI** to render inside your application, you have to specify which routes show the authentication components.
The **React SDK** uses [**React Router**](https://reactrouter.com/en/main) under the hood to achieve this.
Based on whether you already use this package or not in your project, there are two different ways of configuring the routes.
```tsx
// highlight-next-line
class App extends React.Component {
render() {
return (
{/*This renders the login UI on the route*/}
// highlight-next-line
{getSuperTokensRoutesForReactRouterDom(reactRouterDom, [WebauthnPreBuiltUI])}
{/*Your app routes*/}
);
}
}
```
:::important
If you are using `useRoutes`, `createBrowserRouter` or have routes defined in a different file, you need to adjust the code sample.
Please see [this issue](https://github.com/supertokens/supertokens-auth-react/issues/581#issuecomment-1246998493) for further details.
```tsx
function AppRoutes() {
const authRoutes = getSuperTokensRoutesForReactRouterDom(
reactRouterDom,
[/* Add your UI recipes here e.g. EmailPasswordPrebuiltUI, PasswordlessPrebuiltUI, ThirdPartyPrebuiltUI */]
);
const routes = useRoutes([
...authRoutes.map(route => route.props),
// Include the rest of your app routes
]);
return routes;
}
function App() {
return (
);
}
```
:::
```tsx
// highlight-next-line
class App extends React.Component {
render() {
// highlight-start
if (canHandleRoute([WebauthnPreBuiltUI])) {
// This renders the login UI on the route
return getRoutingComponent([WebauthnPreBuiltUI])
}
// highlight-end
return (
{/*Your app*/}
);
}
}
```
Add the `WebAuthn` recipe in your `AuthComponent`.
```tsx title="/app/auth/auth.component.ts"
@Component({
selector: "app-auth",
template: '',
})
export class AuthComponent implements OnDestroy, AfterViewInit {
constructor(
private renderer: Renderer2,
@Inject(DOCUMENT) private document: Document
) { }
ngAfterViewInit() {
this.loadScript('^{prebuiltUIVersion}');
}
ngOnDestroy() {
// Remove the script when the component is destroyed
const script = this.document.getElementById('supertokens-script');
if (script) {
script.remove();
}
}
private loadScript(src: string) {
const script = this.renderer.createElement('script');
script.type = 'text/javascript';
script.src = src;
script.id = 'supertokens-script';
script.onload = () => {
supertokensUIInit({
appInfo: {
appName: "",
apiDomain: "",
websiteDomain: "",
apiBasePath: "",
websiteBasePath: "",
},
recipeList: [
// highlight-start
supertokensUIWebAuthn.init(),
// highlight-end
supertokensUISession.init(),
],
});
}
this.renderer.appendChild(this.document.body, script);
}
}
```
Add the `WebAuthn` recipe in your `AuthView` file.
```tsx
```
Call the SDK init function at the start of your application.
The invocation includes the [main configuration details](/docs/references/frontend-sdks/reference#sdk-configuration), as well as the **recipes** that you use in your setup.
```tsx
SuperTokens.init({
appInfo: {
apiDomain: "...",
apiBasePath: "...",
appName: "...",
},
recipeList: [
Session.init(),
WebAuthn.init(),
],
});
```
First, you need to add the recipe script tag.
```html
```
You can initialize the SDK.
```tsx
supertokens.init({
appInfo: {
apiDomain: "",
apiBasePath: "",
appName: "...",
},
recipeList: [
supertokensSession.init(),
supertokensWebAuthn.init(),
],
});
```
```tsx
SuperTokens.init({
apiDomain: "",
apiBasePath: "",
});
```
```kotlin
void main() {
SuperTokens.init(
apiDomain: "",
apiBasePath: "",
);
}
```
### 2. Add the passkeys UI
#### 2.1 Add the sign up form
Create a form in which the user can input their email address.
When the user submits the form, call the `registerCredentialWithSignUp` method like in the next code snippet.
Under the hood, the method communicates with the backend SDK to fetch the registration options.
Once the backend responds, it uses the browser's APIs to begin the registration process.
For a more detailed overview of the sign up flow check the [Important Concepts page](/docs/authentication/passkeys/important-concepts#signup).
```ts
async function signUp(email: string) {
try {
let response = await registerCredentialWithSignUp({
email
});
if (
response.status === "SIGN_UP_NOT_ALLOWED" ||
response.status === "INVALID_AUTHENTICATOR_ERROR"
) {
// the reason string is a user friendly message
// about what went wrong. It can also contain a support code which users
// can tell you so you know why their sign in / up was not allowed.
window.alert(response.reason)
} else if (
response.status === "INVALID_EMAIL_ERROR" ||
response.status === "EMAIL_ALREADY_EXISTS_ERROR"
) {
window.alert("Invalid email");
} else if (
response.status === "INVALID_CREDENTIALS_ERROR" ||
response.status === "OPTIONS_NOT_FOUND_ERROR" ||
response.status === "INVALID_OPTIONS_ERROR" ||
response.status === "INVALID_AUTHENTICATOR_ERROR" ||
response.status === "EMAIL_ALREADY_EXISTS_ERROR" ||
response.status === "AUTHENTICATOR_ALREADY_REGISTERED" ||
response.status === "FAILED_TO_REGISTER_USER" ||
response.status === "WEBAUTHN_NOT_SUPPORTED"
) {
// These errors represent various issues with the authenticator, credential or the flow itself.
// These should be handled individually by you.
// The user should be informed that they should retry the sign up process or get in touch with you.
window.alert("Please try again");
} else {
// User signed up successfully.
window.alert("You have been signed up successfully");
}
} catch (err: any) {
if (err.isSuperTokensGeneralError === true) {
// this may be a custom error message sent from the API by you,
// or if the input email / phone number is not valid.
window.alert(err.message);
} else {
window.alert("Oops! Something went wrong.");
}
}
}
```
## Get the email address from the user
Add a form where the user can input their email address.
## Fetch the registration options from the backend SDK
When the user submits the form, call the `register options` API.
Save the response to use it in the next step.
```bash
curl --location --request POST '/webauthn/register/options' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"email": "johndoe@gmail.com",
"displayName": "John Doe"
}'
```
:::caution
The returned result matches the format required by the **browser's WebAuthn API**.
You will have to map the properties to the correct format based on the requirements of your platform.
:::
## Register a new credential authenticator API
Use the received options generate a new credential.
The implementation will vary based on the platform you are using.
- **React Native**: You can use the [`react-native-passkey`](https://github.com/f-23/react-native-passkey) library.
- **iOS**: Use the [`Authentication Services`](https://developer.apple.com/documentation/authenticationservices) framework.
- **Android**: Use the [`Android Credential Manager API`](https://developer.android.com/identity/sign-in/credential-manager).
- **Flutter**: Use [platform channels](https://docs.flutter.dev/platform-integration/platform-channels#architecture) to access the native APIs.
## Call the sign up API
Using the newly generate credential, call the sign up API to save the new authentication method.
```bash
curl --location --request POST '/webauthn/signup' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"webauthnGeneratedOptionsId": "opt_123...",
"credential": {
"id": "credential_id",
"rawId": "raw_credential_id",
"response": {
"clientDataJSON": "base64_client_data_json",
"attestationObject": "base64_attestation_object"
},
"type": "public-key"
},
"shouldTryLinkingWithSessionUser": true
}'
```
#### 2.2 Add the login form
Add a button that can trigger the sign in flow.
This is all that you need in terms of UI.
When the user clicks it, call the `authenticateCredentialWithSignIn` method to handle the whole process.
The function uses the backend registration options to trigger the challenge signing action through the browser API.
Then, it forwards the result to the backend for validation.
For a more detailed overview of the login flow check the [Important Concepts page](/docs/authentication/passkeys/important-concepts#login).
```ts
async function signIn(email: string) {
try {
let response = await authenticateCredentialWithSignIn();
if (
response.status === "SIGN_IN_NOT_ALLOWED"
) {
// the reason string is a user friendly message
// about what went wrong. It can also contain a support code which users
// can tell you so you know why their sign in / up was not allowed.
window.alert(response.reason)
} else if (response.status === "WEBAUTHN_NOT_SUPPORTED") {
// the user's browser does not support the WebAuthn standard
window.alert("Login method not supported");
} else if (
response.status === "INVALID_CREDENTIALS_ERROR" ||
response.status === "FAILED_TO_AUTHENTICATE_USER"
) {
// These errors represent various issues with the authenticator, credential or the flow itself.
// These should be handled individually by you.
// The user should be informed that they should retry the sign in process or get in touch with you.
window.alert("Please try again");
} else {
// User signed in successfully.
window.alert("You have been signed in successfully");
}
} catch (err: any) {
if (err.isSuperTokensGeneralError === true) {
// this may be a custom error message sent from the API by you,
// or if the input email / phone number is not valid.
window.alert(err.message);
} else {
window.alert("Oops! Something went wrong.");
}
}
}
```
## Add a button that can trigger the sign in flow
## Get the sign in options from the backend SDK
When the user taps the sign in button, call the backend API to fetch the sign in options.
```bash
curl --location --request POST '/webauthn/signin/options' \
--header 'Content-Type: application/json; charset=utf-8'
```
## Use the authenticator to sign the challenge
With the received options, invoke the authenticator to sign the challenge.
The implementation will vary based on the platform you are using.
## Call the sign in API
Send the signed challenge to the backend for validation.
```bash
curl --location --request POST '/webauthn/signin' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"webauthnGeneratedOptionsId": "opt_123...",
"credential": {
"id": "credential_id",
"rawId": "raw_credential_id",
"response": {
"clientDataJSON": "base64_client_data_json",
"attestationObject": "base64_attestation_object"
},
"type": "public-key"
},
"shouldTryLinkingWithSessionUser": true
}'
```
### 2. Initialize the backend SDK
### 3. Initialize the backend SDK
Initialize the backend SDK and include the **WebAuthn** `recipe`.
The init call includes [configuration details](/docs/references/backend-sdks/reference#sdk-configuration) for your app.
It specifies how the backend connects to the **SuperTokens Core**, as well as the **Recipes** used in your setup.
The recipe exposes the required endpoints that get accessed by the frontend code, and communicates with the **SuperTokens Core** to complete the authentication flow.
You can [configure different aspects](/docs/authentication/passkeys/customization) of the recipe's behavior but, for the completion of this guide, use the default values.
After you confirm that the flow works as expected you can explore more advanced customisations options.
```ts
supertokens.init({
// Replace this with the framework you are using
framework: "express",
supertokens: {
// We use try.supertokens for demo purposes.
// At the end of the tutorial we will show you how to create
// your own SuperTokens core instance and then update your config.
connectionURI: "https://try.supertokens.io",
// apiKey:
},
appInfo: {
appName: "",
apiDomain: "",
websiteDomain: "",
apiBasePath: "/auth",
websiteBasePath: "/auth",
},
recipeList: [
WebAuthN.init(),
Session.init()
]
});
```
:::caution
At the moment there is no support for using passkeys authentication in the Go SDK.
:::
```python
from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.recipe import session, webauthn
init(
app_info=InputAppInfo(
app_name="",
api_domain="",
website_domain="",
api_base_path="/auth",
website_base_path="/auth"
),
supertokens_config=SupertokensConfig(
# We use try.supertokens for demo purposes.
# At the end of the tutorial we will show you how to create
# your own SuperTokens core instance and then update your config.
connection_uri="https://try.supertokens.io",
# api_key=""
),
framework='flask', # Replace this with the framework you are using
recipe_list=[
webauthn.init(),
session.init()
]
)
```