Device-based auth overview

Stytch's WebAuthn and Time-based one-time passcode (TOTP) products allow you to authenticate users frictionlessly with built-in device biometrics and secure hardware keys, or improve security and layer authentication through authenticator apps.

Types of device-based auth

Device-based auth type icon

WebAuthn

Create frictionless authentication flows for your users across any of their devices with WebAuthn, allowing them to use authentication methods such as Face ID, Touch ID, YubiKeys, and more.

Device-based auth type icon

TOTP authenticator apps

Level up the security in your authentication solution with time-sensitive passcodes.


Example apps using device-based auth

Try out our WebAuthn auth product layered on as a second factor with email magic links in our demo app.

Starting with a template

example app

WebAuthn

Overview

The Web Authentication API (WebAuthn) is a specification that allows web applications on supported browsers to authenticate a user via authenticator types such as built-in device biometrics (e.g. facial recognition on mobile and fingerprint readers on desktop) or secure hardware keys (e.g. YubiKeys). While WebAuthn has many benefits, developers need to understand the API to implement it securely. Stytch's WebAuthn product simplifies the process by abstracting the implementation details of WebAuthn for developers to make it as quick as possible to implement securely.


Setting up WebAuthn

There are two steps during a WebAuthn authentication flow, registration and authentication. The first step handles registering a WebAuthn device to a user. The second step handles the authentication attempt.

For both the registration and authentication steps, you’ll make two Stytch requests. The first request returns the necessary components to communicate with the WebAuthn device. The second request is used to pass the response from the WebAuthn call back to Stytch for verification.

We’ll be using the webauthn-json library to assist this integration. This library converts the JSON request into the correct data types by unmarshalling and decoding the body and then outputs marshalled JSON that you can pass back to Stytch.

As you’re working through this guide, feel free to ask any questions if you get stuck in our Slack community or reach out to us at support@stytch.com

  1. Step 1: Create Stytch User

    If the user attempting to register isn't associated with a Stytch user ID, you'll have to add them with Stytch's /v1/users/create endpoint if you haven't already. Pass the user's email address or phone number and store the returned user_id. The user_id will be used to register and authenticate with WebAuthn.

    curl --request POST \
    	--url https://test.stytch.com/v1/users \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    		"email": "sandbox@stytch.com"
    	}'
  2. Step 2: Start Registration Process

    To authenticate with WebAuthn, you first need to register an authenticator. This will have to happen once per registration. Start by generating the request needed for webauthn-json's create call. To do this you'll make a request to /v1/webauthn/register/start. You need two fields for the request, a Stytch user_id and the domain where the webauthn-json's create call will be invoked, i.e. your login page's domain. There are two optional fields, user_agent and authenticator_type. You can pass a user_agent to help identify the WebAuthn registration. This could be helpful for telling registrations apart. You can use authenticator_type to require a certain type of WebAuthn device, either platform (like a fingerprint reader) or cross-platform (like a YubiKey). If you omit the field, we'll assume either type is acceptable.

    curl --request POST \
    	--url https://test.stytch.com/v1/webauthn/register/start \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    	    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    	    "domain": "example.com"
    	}'
  3. Step 3: Calling webauthn-json's Create Method

    Using the public_key_credential_creation_options field from the /v1/webauthn/register/start response, call webauthn-json's create method. You'll want to handle any errors that crop up in this step, like if there are no WebAuthn devices available to register. If the WebAuthn call is successful, JSON.stringify the response and get ready for another Stytch call.

    import * as webauthnJson from '@github/webauthn-json';
    
    // You implemented this in Step 2
    const registerStartData = await callWebauthnRegisterStart()
    const credential = await webauthnJson.create({
      publicKey: registerStartData.public_key_credential_creation_options
    });
    // And you'll implement this in Step 4
    await callWebauthnRegister({
      public_key_credential: JSON.stringify(credential)
    })
  4. Step 4: Register the WebAuthn Registration with Stytch

    Next we'll use the JSON string you created in Step 3 as the public_key_credential in our request to /v1/webauthn/register. Stytch will validate the credential and return the webauthn_registration_id if the registration is successful. The user now has a registered WebAuthn registration that can be used for authentication. We recommend saving the webauthn_registration_id and domain. This allows you to check that the user in question has a valid WebAuthn registration on file with Stytch before you call /v1/webauthn/authenticate/start for subsequent logins.

    curl --request POST \
    	--url https://test.stytch.com/v1/webauthn/register \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    	    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    	    "public_key_credential": "{\"type\":\"public-key\",\"id\":\"Ab6y28pCs5bVRIzSmrlufidfR57gRlEZ-KSTVGJYdkwAfR_SeaVXvdW6ND_XljM25cXYI-dSwrhjuNsj1L3uC0BHqN3mBQIzSswJneTv08RbDNZOLhjiwOEnQ03uPbL5eA7EcyinClOU_qwPMf5lowW1NSTWtaFvOlY\",\"rawId\":\"Ab6y28pCs5bVRIzSmrlufidfR57gRlEZ-KSTVGJYdkwAfR_SeaVXvdW6ND_XljM25cXYI-dSwrhjuNsj1L3uC0BHqN3mBQIzSswJneTv08RbDNZOLhjiwOEnQ03uPbL5eA7EcyinClOU_qwPMf5lowW1NSTWtaFvOlY\",\"response\":{\"clientDataJSON\":\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiaFladExOVDlTSWdacVBuS2ZiblFYM25DSjdOYXZUVF9TNm9DOVhSRVl2MEYiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJjcm9zc09yaWdpbiI6ZmFsc2V9\",\"attestationObject\":\"o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZyZjc2lnWEYwRAIgLEvyXrb_aMCVOjpYBLpm3cPaaquDN0ouXaL27SF9Lp0CIB2f56tWUDvs6oBl3pMxIIrJqJhZKkK7btJtWVDLsFFbaGF1dGhEYXRhWP5Jlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0VheZqwrc4AAjW8xgpkiwsl8fBVAwB6Ab6y28pCs5bVRIzSmrlufidfR57gRlEZ-KSTVGJYdkwAfR_SeaVXvdW6ND_XljM25cXYI-dSwrhjuNsj1L3uC0BHqN3mBQIzSswJneTv08RbDNZOLhjiwOEnQ03uPbL5eA7EcyinClOU_qwPMf5lowW1NSTWtaFvOlalAQIDJiABIVggFCI-4HODPxlfeBwfFyzQG_btRm_pB6mb9E1E-rANMwoiWCBCr6C2SQOGElh9N9OMzVBcMnOolAcvz3S0STbnNTHOmg\"},\"clientExtensionResults\":{}}"
    	}'
  5. Step 5: Start Authentication Process

    Similar to registering, authentication starts by generating the request for the webauthn-json's get method. To do this you'll make a request to /v1/webauthn/authenticate/start. You need two fields for the request, the user_id of the user and the domain the WebAuthn call will be invoked on.

    curl --request POST \
    	--url https://test.stytch.com/v1/webauthn/authenticate/start \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    	    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6"
    	    "domain": "example.com"
    	}'
  6. Step 6: Calling webauthn-json's Get Method

    Using the public_key_credential_request_options field from the /v1/webauthn/authenticate/start response, call webauthn-json's get method. Again, you'll want to handle any errors that might arise here. If webauthn-json's get call is successful, JSON.stringify the response and get ready for another Stytch call.

    import * as webauthnJson from '@github/webauthn-json';
    
    // You implemented this in Step 5
    const authenticationStartData = await callWebauthnAuthenticateStart()
    const credential = await webauthnJson.get({
      publicKey: authenticationStartData.public_key_credential_request_options
    });
    // And you'll implement this in Step 7
    await callWebauthnAuthenticate({
      public_key_credential: JSON.stringify(credential)
    })
  7. Step 7: Authenticate the WebAuthn Registration with Stytch

    Using the JSON string you created from webauthn-json's get method as the public_key_credential, make a request to /v1/webauthn/authenticate. Stytch will perform validation on the passed credential and return a 400 if there are any issues. If the authentication is successful, we'll return a 200.

    curl --request POST \
    	--url https://test.stytch.com/v1/webauthn/authenticate \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    	    "public_key_credential": "{\"type\":\"public-key\",\"id\":\"Ab6y28pCs5bVRIzSmrlufidfR57gRlEZ-KSTVGJYdkwAfR_SeaVXvdW6ND_XljM25cXYI-dSwrhjuNsj1L3uC0BHqN3mBQIzSswJneTv08RbDNZOLhjiwOEnQ03uPbL5eA7EcyinClOU_qwPMf5lowW1NSTWtaFvOlY\",\"rawId\":\"Ab6y28pCs5bVRIzSmrlufidfR57gRlEZ-KSTVGJYdkwAfR_SeaVXvdW6ND_XljM25cXYI-dSwrhjuNsj1L3uC0BHqN3mBQIzSswJneTv08RbDNZOLhjiwOEnQ03uPbL5eA7EcyinClOU_qwPMf5lowW1NSTWtaFvOlY\",\"response\":{\"authenticatorData\":\"SZYN5YgOjGh7NBcPZHZgW1_krrmihjLHmVzzuoNcl2MFYZKokg\",\"clientDataJSON\":\"eyJ2eXBlOjopo2ViYBx0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWEtEWDVJa25EWEU3by1KQlRkYTNfS1NiTXdmb3dMWDQxMldlNEFDY04tYWgiLCJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJjcm9zc09yaWdpbiI6ZmFsc2V9\",\"signature\":\"MEYCIQDU1FGXEBrq3hsQ2ye1pBcYLMu7zmzLVVdcbs6R21hGyAIhAJmpdBo2Hd7P4Ks9VFKBUYbKSIioMdhl2XIIjWHNKD77\",\"userHandle\":\"dXNlus1kZXZlbG9wLBC2M2E1MGI0LWEwMGEtNGU3NC89NTJmLTFlOGRhODE2nDBnMw\"},\"clientExtensionResults\":{}}"
    	}'
  8. Step 8: You're Done!

    You just finished all the critical components to authenticate your users via WebAuthn! Have any feedback after having integrated WebAuthn? Get in touch with us and tell us what you think in our Slack community or via support@stytch.com.


Getting started with a SDK

This guide is coming soon.


Mobile Biometrics

Overview

Biometrics have become a popular way for apps to layer low-friction step-up auth into user workflows — for good reason. Biometrics ensure a user demonstrates both possession of an original device and a unique biometric trait.

Our iOS SDK supports Mobile Biometrics natively on all Apple devices. Check out our iOS SDK Docs to integrate native biometrics into your app.

Mobile Biometrics are coming soon to our React Native and upcoming Android SDKs, keep an eye on our Changelog to hear when they ship!


Apple Passkeys

Overview

Apple Passkeys is secure and frictionless authentication technology that is based on FIDO Alliance and W3C standards. They allow a seamless transition for Apple device users to a passwordless authentication flow that drops into your app in just a few calls with Stytch.

We're excited to support Apple Passkeys across all Apple devices soon. Keep an eye on our Changelog to hear when it ships!


Time-based one-time passcodes

Overview

Using time-based one-time passcodes within apps like Google Authenticator to provide an extra level of security for critical services by requiring a user to prove possession of their device. It is a popular method for adding multi-factor authentication to an application.


Setting up time-based one-time passcodes (TOTP)

Time-based one-time passcodes (TOTP) are a passwordless two-factor authentication option that can be used when you need high security assurance. TOTP authentication solutions are ideal for particularly sensitive use cases that are also highly attractive in terms of the potential payoff they offer – think money movement in fintech or cryptocurrency, or access to a company’s HR or payroll information. When integrated as a second authentication factor, TOTP serves as an additional safeguard by requiring users to prove possession of their device. It works by generating a one-time passcode that’s based on the current time and a shared secret between an authenticator app like Google Authenticator or Authy, and the server (in this case, Stytch). Users, who must have an authenticator app downloaded on their device, are asked to input the unique passcode within a certain period of time, usually 30 seconds, as evidence of their identity. Once the user supplies the TOTP code, your app can use Stytch’s /totps/authenticate endpoint to verify that passcodes are valid and ultimately grant users access.

  1. Step 1: Create Stytch User

    If the user attempting to register isn’t already associated with a Stytch user_id, you’ll need to create a Stytch User with the /users/create endpoint. Pass the user’s email address or phone number into the User create endpoint and store the returned user_id. The user_id will be used to register and authenticate with TOTP.

    curl --request POST \
    	--url https://test.stytch.com/v1/users \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    		"email": "sandbox@stytch.com"
    	}'
  2. Step 2: Create a TOTP for the user

    Next, you’ll want to create a TOTP for the user by passing the user_id into the /totps/create endpoint. This endpoint also takes in an expiration_minutes value; this value is the expiration for the TOTP instance. The newly create TOTP instance must be authenticated at least once within this timeframe otherwise it will expire and be render unusable. Make sure to expeditiosly prompt the user to complete the TOTP flow quickly after TOTP creation. Defaults to 60 (1 hour) with a minimum of 5 and a maximum of 1440 (1 day).

    This endpoint returns four unique fields in addition to the stand fields like status_code and request_id. The secret is shared between the authenticator app and your server. The totp_id is the Stytch identifier for a given TOTP instance. The qr_code is the base64-encoded representation of the QR code image. You can embed this directly into your website and it will display as a QR code that users can scan. And finally recovery_codes, if the user loses access to their authenticator app, they can input these recovery codes into the /v1/totps/recover endpoint at any point. If the recovery code is valid, then the recover endpoint will return a 200, and you, the developer, can choose to delete the TOTP instance and generate a new one or simply display the secret back to the user. Note that 10 recovery codes are generated, and each one can only be used once.

    curl --request POST \
    	--url https://test.stytch.com/v1/totps \
    	-u 'PROJECT_ID:SECRET' \
    	-H 'Content-Type: application/json' \
    	-d '{
    	    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    	    "expiration_minutes": 60,
    	}'
  3. Step 3: Authenticating a TOTP code

    Make sure that the user authenticates their first TOTP immediately after creating the TOTP so that the TOTP instance does not expire. When the user sends you the TOTP code, you will hit the /totps/authenticate endpoint with the user's user_id and the supplied totp_code to authenticate it.

    In this step, to accomodate for network and user action delays, TOTP codes from the previous and next 30-second periods are allowed by this endpoint. If you’d like to begin a new Stytch session or re-use an existing one, you may pass in session_duration_minutes(to begin a new session) or session_token(to re-use an existing one). Please check out our session management guide for more information.

    curl --request POST \
        --url https://test.stytch.com/v1/totps/authenticate \
        -u 'PROJECT_ID:SECRET' \
        -H 'Content-Type: application/json' \
        -d '{
            "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
            "totp_code": "576831",
        }'
  4. Step 4: You're Done!

    You just finished all the critical components to add a second factor of authentication via TOTP! Have any feedback after having integrated TOTP? Get in touch with us and tell us what you think in our Slack community or via support@stytch.com.


Getting started with a SDK

This guide is coming soon.