Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

Customization

Overview

Like the other SuperTokens authentication recipes, you can customize the WebAuthn flow through different configuration options and overrides. The following page describes the options that you can change and the different scenarios enabled through customization.


Backend recipe configuration

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import WebAuthn from "supertokens-node/recipe/webauthn";

supertokens.init({
  framework: "express",
  supertokens: {
    // https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
    connectionURI: "https://try.supertokens.com",
    // apiKey: <API_KEY(if configured)>,
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [
    WebAuthn.init({
      getOrigin: async () => {
        return "https://example.com";
      },
      getRelyingPartyId: async () => {
        return "example.com";
      },
      getRelyingPartyName: async () => {
        return "example";
      },
    }),
    Session.init(), // initializes session features
  ],
});
import (
	"net/http"

	"github.com/supertokens/supertokens-golang/recipe/session"
	"github.com/supertokens/supertokens-golang/recipe/webauthn"
	"github.com/supertokens/supertokens-golang/recipe/webauthn/webauthnmodels"
	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	apiBasePath := "/auth"
	websiteBasePath := "/auth"
	err := supertokens.Init(supertokens.TypeInput{
		Supertokens: &supertokens.ConnectionInfo{ConnectionURI: "https://try.supertokens.com"},
		AppInfo: supertokens.AppInfo{
			AppName:         "<YOUR_APP_NAME>",
			APIDomain:       "<YOUR_API_DOMAIN>",
			WebsiteDomain:   "<YOUR_WEBSITE_DOMAIN>",
			APIBasePath:     &apiBasePath,
			WebsiteBasePath: &websiteBasePath,
		},
		RecipeList: []supertokens.Recipe{
			webauthn.Init(&webauthnmodels.TypeInput{
				GetOrigin: func(tenantID string, req *http.Request, userContext supertokens.UserContext) (string, error) {
					return "https://example.com", nil
				},
				GetRelyingPartyId: func(tenantID string, req *http.Request, userContext supertokens.UserContext) (string, error) {
					return "example.com", nil
				},
				GetRelyingPartyName: func(tenantID string, userContext supertokens.UserContext) (string, error) {
					return "example", nil
				},
			}),
			session.Init(nil),
		},
	})
	if err != nil {
		panic(err)
	}
}
from typing import Optional

from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.framework import BaseRequest
from supertokens_python.recipe import session, webauthn
from supertokens_python.recipe.webauthn import WebauthnConfig
from supertokens_python.types.base import UserContext


async def get_origin(*, tenant_id: str, request: Optional[BaseRequest], user_context: UserContext):
    return "https://example.com"

async def get_relying_party_id(*, tenant_id: str, request: Optional[BaseRequest], user_context: UserContext):
    return "example.com"

async def get_relying_party_name(*, tenant_id: str, user_context: UserContext):
    return "example"

init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth"
    ),
    supertokens_config=SupertokensConfig(
        # https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
        connection_uri="https://try.supertokens.com",
        # api_key="<API_KEY(if configured)>"
    ),
    framework='flask',  # Replace this with the framework you are using
    recipe_list=[
        webauthn.init(
            config=WebauthnConfig(
                get_origin=get_origin,
                get_relying_party_id=get_relying_party_id,
                get_relying_party_name=get_relying_party_name,
            )
        ),
        session.init()  # initializes session features
    ]
)

The backend recipe accepts the following properties during initialization:

Option Description Default
getRelyingPartyId Sets the domain name associated with the WebAuthn credentials. This helps ensure that only your domain uses the credentials. Hostname of appInfo.apiDomain
getRelyingPartyName Sets a human-readable name for your application. The name appears to users during the WebAuthn registration process. The appName value that you have set in appConfig
getOrigin Configures the frontend origin that WebAuthn credentials bind to. appInfo.getOrigin(...), normally the configured website origin
emailDelivery Configures how the system builds and sends account-recovery emails. Read the email delivery page for more information. Default email service
validateEmailAddress Adds custom validation logic for email addresses. Basic email format validation

All the properties are optional.

The backend recipe accepts the following properties during initialization:

Option Description Default
get_relying_party_id Sets the domain name associated with the WebAuthn credentials. This helps ensure that only your domain uses the credentials. Hostname of app_info.api_domain
get_relying_party_name Sets a human-readable name for your application. The name appears to users during the WebAuthn registration process. The app_name value that you have set in app_config
get_origin Configures the frontend origin that WebAuthn credentials bind to. app_info.get_origin(...), normally the configured website origin
email_delivery Configures how the system builds and sends account-recovery emails. Read the email delivery page for more information. Default email service
validate_email_address Adds custom validation logic for email addresses. Basic email format validation

All the properties are optional.

The backend recipe accepts the following optional properties in webauthnmodels.TypeInput:

Option Description Default
GetRelyingPartyId Sets the domain name associated with the WebAuthn credentials. Hostname of AppInfo.APIDomain
GetRelyingPartyName Sets a human-readable name for your application. AppInfo.AppName
GetOrigin Configures the frontend origin that WebAuthn credentials bind to. AppInfo.GetOrigin(...), normally the configured website origin
EmailDelivery Configures how the system builds and sends account-recovery emails. Default email service
ValidateEmailAddress Adds custom validation logic for email addresses. Basic email format validation

The RP ID must equal the frontend origin’s host or be a registrable domain suffix of it. It must not include a scheme, port, or path. The origin must include the exact scheme and host, plus the port when it is non-default. For example, RP ID example.com is valid for origin https://login.example.com, but api.example.net is not. If your API and website use unrelated hosts, the default RP ID derived from the API domain is invalid for the website origin; configure both values explicitly.


Credential generation

The client generates the credentials based on the options provided by the backend SDK. The frontend SDK uses navigator.credentials.create() to start the registration ceremony.

To change the options used to generate credentials, you need to override the registerOptions function.

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import WebAuthn from "supertokens-node/recipe/webauthn";

supertokens.init({
  framework: "express",
  supertokens: {
    // https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
    connectionURI: "https://try.supertokens.com",
    // apiKey: <API_KEY(if configured)>,
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [
    WebAuthn.init({
      override: {
        functions: (originalImplementation) => {
          return {
            ...originalImplementation,
            registerOptions: (input) => {
              return originalImplementation.registerOptions({
                ...input,
                attestation: "direct",
                residentKey: "required",
                timeout: 10 * 1000,
                userVerification: "required",
                userPresence: true,
                displayName: "John Doe",
                supportedAlgorithmIds: [-257],
                relyingPartyId: "example.com",
                relyingPartyName: "example",
                origin: "https://example.com",
              });
            },
          };
        },
      },
    }),
    Session.init(), // initializes session features
  ],
});
config := &webauthnmodels.TypeInput{
	Override: &webauthnmodels.OverrideStruct{
		Functions: func(original webauthnmodels.RecipeInterface) webauthnmodels.RecipeInterface {
			originalRegisterOptions := *original.RegisterOptions
			registerOptions := func(
				email, recoverAccountToken, displayName *string,
				relyingPartyID, relyingPartyName, origin string,
				timeout *int, attestation *webauthnmodels.Attestation,
				residentKey *webauthnmodels.ResidentKey,
				userVerification *webauthnmodels.UserVerification,
				userPresence *bool,
				supportedAlgorithmIDs []webauthnmodels.COSEAlgorithmIdentifier,
				tenantID string, userContext supertokens.UserContext,
			) (webauthnmodels.RegisterOptionsResponse, error) {
				customTimeout := 10 * 1000
				customAttestation := webauthnmodels.AttestationDirect
				customResidentKey := webauthnmodels.ResidentKeyRequired
				customUserVerification := webauthnmodels.UserVerificationRequired
				customUserPresence := true
				return originalRegisterOptions(
					email, recoverAccountToken, displayName,
					"example.com", "example", "https://example.com",
					&customTimeout, &customAttestation, &customResidentKey,
					&customUserVerification, &customUserPresence,
					[]webauthnmodels.COSEAlgorithmIdentifier{-257}, tenantID, userContext,
				)
			}
			original.RegisterOptions = &registerOptions
			return original
		},
	},
}
from typing import List, Optional, cast

from typing_extensions import Unpack

from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.recipe import session, webauthn
from supertokens_python.recipe.webauthn import (
    RecipeInterface,
    WebauthnConfig,
    WebauthnOverrideConfig,
)
from supertokens_python.recipe.webauthn.interfaces.recipe import (
    Attestation,
    RegisterOptionsKwargsInput,
    ResidentKey,
    UserVerification,
)
from supertokens_python.types.base import UserContext


def override_webauthn_functions(original_implementation: RecipeInterface):
    original_register_options = original_implementation.register_options

    async def register_options(
        *,
        relying_party_id: str,
        relying_party_name: str,
        origin: str,
        resident_key: Optional[ResidentKey] = None,
        user_verification: Optional[UserVerification] = None,
        user_presence: Optional[bool] = None,
        attestation: Optional[Attestation] = None,
        supported_algorithm_ids: Optional[List[int]] = None,
        timeout: Optional[int] = None,
        tenant_id: str,
        user_context: UserContext,
        **kwargs: Unpack[RegisterOptionsKwargsInput],
    ):
        return await original_register_options(
            relying_party_id="example.com",
            relying_party_name="example",
            origin="https://example.com",
            resident_key="required",
            user_verification="required",
            user_presence=True,
            attestation="direct",
            supported_algorithm_ids=[-257],
            timeout=10 * 1000,
            tenant_id=tenant_id,
            user_context=user_context,
            email=cast(str, kwargs.get("email")),
            recover_account_token=cast(str, kwargs.get("recover_account_token")),
            display_name="John Doe",
        )

    original_implementation.register_options = register_options
    return original_implementation


init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth",
    ),
    supertokens_config=SupertokensConfig(
        # https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
        connection_uri="https://try.supertokens.com",
        # api_key="<API_KEY(if configured)>"
    ),
    framework="flask",  # Replace this with the framework you are using
    recipe_list=[
        webauthn.init(
            config=WebauthnConfig(
                override=WebauthnOverrideConfig(functions=override_webauthn_functions)
            )
        ),
        session.init(),  # initializes session features
    ],
)

Input properties

Name Type Description Default Value
relyingPartyId string The domain name of your application that the system uses for validating the credential. Uses getRelyingPartyId from the recipe configuration, which defaults to the hostname of appInfo.apiDomain
relyingPartyName string The human-readable name of your application. Uses getRelyingPartyName from the recipe configuration, which defaults to appName
origin string The frontend origin where the credential is created. Uses getOrigin from the recipe configuration, which normally defaults to the configured website origin
timeout number The time in milliseconds that the user has to complete the credential generation process. 60000
attestation "none" | "indirect" | "direct" | "enterprise" The attestation conveyance preference requested from the authenticator. none
supportedAlgorithmIds number[] The cryptographic algorithms that can generate credentials. Different authenticators support different algorithms. [-8, -7, -257]
residentKey "discouraged" | "preferred" | "required" Whether the authenticator creates a discoverable credential. A discoverable credential may be synced or device-bound. required
userVerification "discouraged" | "preferred" | "required" Whether user verification (like PIN or biometrics) is necessary. preferred
userPresence boolean Whether the ceremony requires evidence of user interaction. This is separate from user verification. true
displayName string The display name of the user. The user’s email property

Input properties

Name Type Description Default Value
relying_party_id str The domain name of your application that the system uses for validating the credential. Uses get_relying_party_id from the recipe configuration, which defaults to the hostname of app_info.api_domain
relying_party_name str The human-readable name of your application. Uses get_relying_party_name from the recipe configuration which defaults to the app_name
origin str The frontend origin where the credential is created. Uses get_origin from the recipe configuration, which normally defaults to the configured website origin
timeout int The time in milliseconds that the user has to complete the credential generation process. 60000
attestation "none" | "indirect" | "direct" | "enterprise" The attestation conveyance preference requested from the authenticator. none
supported_algorithm_ids List[int] The cryptographic algorithms that can generate credentials. Different authenticators support different algorithms. [-8, -7, -257]
resident_key "discouraged" | "preferred" | "required" Whether the authenticator creates a discoverable credential. A discoverable credential may be synced or device-bound. required
user_verification "discouraged" | "preferred" | "required" Whether user verification (like PIN or biometrics) is necessary. preferred
user_presence bool Whether the ceremony requires evidence of user interaction. This is separate from user verification. True
display_name str The display name of the user. The user’s email property

Input properties

Name Type Description Default Value
relyingPartyId string The domain name used to validate the credential. Uses GetRelyingPartyId, which defaults to the hostname of AppInfo.APIDomain
relyingPartyName string The human-readable name of your application. Uses GetRelyingPartyName, which defaults to AppInfo.AppName
origin string The frontend origin where the credential is created. Uses GetOrigin, which normally defaults to the configured website origin
timeout *int The time in milliseconds available to complete credential creation. 60000
attestation *webauthnmodels.Attestation The attestation conveyance preference requested from the authenticator. AttestationNone
supportedAlgorithmIds []webauthnmodels.COSEAlgorithmIdentifier The allowed credential algorithms. [-8, -7, -257]
residentKey *webauthnmodels.ResidentKey Whether the authenticator creates a discoverable credential. ResidentKeyRequired
userVerification *webauthnmodels.UserVerification Whether user verification, such as a PIN or biometrics, is necessary. UserVerificationPreferred
userPresence *bool Whether the ceremony requires evidence of user interaction. true
displayName *string The display name of the user. The user’s email

Keep the default attestation: "none" unless your relying party has a specific attestation policy. "direct" can expose identifying authenticator information and still requires you to validate the attestation statement and its certificate chain against trust anchors you maintain. Requesting direct attestation does not by itself make an authenticator trusted.


Credential validation

When a user attempts to sign in, the authenticator uses their credential to sign an assertion on the client. The frontend SDK uses navigator.credentials.get() to start the authentication ceremony.

The server generates the options for signing the challenge through the backend SDK, and then sends them to the client. To change those, you need to override the signInOptions function.

import supertokens from "supertokens-node";
import Session from "supertokens-node/recipe/session";
import WebAuthn from "supertokens-node/recipe/webauthn";

supertokens.init({
  framework: "express",
  supertokens: {
    // https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
    connectionURI: "https://try.supertokens.com",
    // apiKey: <API_KEY(if configured)>,
  },
  appInfo: {
    // learn more about this on https://supertokens.com/docs/references/backend-sdks/reference#sdk-configuration
    appName: "<YOUR_APP_NAME>",
    apiDomain: "<YOUR_API_DOMAIN>",
    websiteDomain: "<YOUR_WEBSITE_DOMAIN>",
    apiBasePath: "/auth",
    websiteBasePath: "/auth",
  },
  recipeList: [
    WebAuthn.init({
      override: {
        functions: (originalImplementation) => {
          return {
            ...originalImplementation,
            signInOptions: (input) => {
              return originalImplementation.signInOptions({
                ...input,
                timeout: 10 * 1000,
                userVerification: "required",
                userPresence: true,
                relyingPartyId: "example.com",
                origin: "https://example.com",
              });
            },
          };
        },
      },
    }),
    Session.init(), // initializes session features
  ],
});
config := &webauthnmodels.TypeInput{
	Override: &webauthnmodels.OverrideStruct{
		Functions: func(original webauthnmodels.RecipeInterface) webauthnmodels.RecipeInterface {
			originalSignInOptions := *original.SignInOptions
			signInOptions := func(
				relyingPartyID, relyingPartyName, origin string,
				timeout *int,
				userVerification *webauthnmodels.UserVerification,
				userPresence *bool,
				tenantID string, userContext supertokens.UserContext,
			) (webauthnmodels.SignInOptionsResponse, error) {
				customTimeout := 10 * 1000
				customUserVerification := webauthnmodels.UserVerificationRequired
				customUserPresence := true
				return originalSignInOptions(
					"example.com", "example", "https://example.com",
					&customTimeout, &customUserVerification, &customUserPresence,
					tenantID, userContext,
				)
			}
			original.SignInOptions = &signInOptions
			return original
		},
	},
}
from typing import Any

from supertokens_python import InputAppInfo, SupertokensConfig, init
from supertokens_python.recipe import session, webauthn
from supertokens_python.recipe.webauthn import (
    RecipeInterface,
    WebauthnConfig,
    WebauthnOverrideConfig,
)
from supertokens_python.types.base import UserContext


def override_webauthn_functions(original_implementation: RecipeInterface):
    original_sign_in_options = original_implementation.sign_in_options

    async def sign_in_options(
        *,
        tenant_id: str,
        user_context: UserContext,
        **kwargs: Any
    ):
        return await original_sign_in_options(
            tenant_id=tenant_id,
            user_context=user_context,
            timeout=10 * 1000,
            user_verification="required",
            user_presence=True,
            relying_party_id='example.com',
            relying_party_name='Example',
            origin='https://example.com',
        )

    original_implementation.sign_in_options = sign_in_options
    return original_implementation

init(
    app_info=InputAppInfo(
        app_name="<YOUR_APP_NAME>",
        api_domain="<YOUR_API_DOMAIN>",
        website_domain="<YOUR_WEBSITE_DOMAIN>",
        api_base_path="/auth",
        website_base_path="/auth"
    ),
    supertokens_config=SupertokensConfig(
        # https://try.supertokens.com is for demo purposes. Replace this with the address of your core instance (sign up on supertokens.com), or self host a core.
        connection_uri="https://try.supertokens.com",
        # api_key="<API_KEY(if configured)>"
    ),
    framework='flask',  # Replace this with the framework you are using
    recipe_list=[
        webauthn.init(
            config=WebauthnConfig(
                override=WebauthnOverrideConfig(
                    functions=override_webauthn_functions
                )
            )
        ),
        session.init()  # initializes session features
    ]
)

Input properties

Name Type Description Default
relyingPartyId string The domain name of your application that the system uses for validating the credential. Uses getRelyingPartyId from the recipe configuration, which defaults to the hostname of appInfo.apiDomain
relyingPartyName string The human-readable name of your application. Uses getRelyingPartyName from the recipe configuration, which defaults to appName
origin string The expected frontend origin for the authentication response. Uses getOrigin from the recipe configuration, which normally defaults to the configured website origin
timeout number The time in milliseconds that the user has to complete the credential validation process. 60000
userVerification "discouraged" | "preferred" | "required" The parameter controls whether user verification (like PIN or biometrics) is necessary. preferred
userPresence boolean Whether the ceremony requires evidence of user interaction. This is separate from user verification. true

Input properties

Name Type Description Default
relying_party_id str The domain name of your application that the system uses for validating the credential. Uses get_relying_party_id from the recipe configuration, which defaults to the hostname of app_info.api_domain
relying_party_name str The human-readable name of your application. Uses get_relying_party_name from the recipe configuration which defaults to the app_name
origin str The expected frontend origin for the authentication response. Uses get_origin from the recipe configuration, which normally defaults to the configured website origin
timeout int The time in milliseconds that the user has to complete the credential validation process. 60000
user_verification "discouraged" | "preferred" | "required" The parameter controls whether user verification (like PIN or biometrics) is necessary. preferred
user_presence bool Whether the ceremony requires evidence of user interaction. This is separate from user verification. True

Input properties

Name Type Description Default
relyingPartyId string The domain name used to validate the credential. Uses GetRelyingPartyId, which defaults to the hostname of AppInfo.APIDomain
relyingPartyName string The human-readable name of your application. Uses GetRelyingPartyName, which defaults to AppInfo.AppName
origin string The expected frontend origin for the authentication response. Uses GetOrigin, which normally defaults to the configured website origin
timeout *int The time in milliseconds available to complete authentication. 60000
userVerification *webauthnmodels.UserVerification Whether user verification, such as a PIN or biometrics, is necessary. UserVerificationPreferred
userPresence *bool Whether the ceremony requires evidence of user interaction. true

API reference

API schema and response details