FusionAuth developer image
FusionAuth developer logo
  • Back to site
  • Expert Advice
  • Blog
  • Developers
  • Downloads
  • Account
  • Contact sales
Navigate to...
  • Welcome
  • Getting Started
  • 5-Minute Setup Guide
  • Release Notes
  • Core Concepts
    • Overview
    • Users
    • Roles
    • Groups
    • Entity Management
    • Registrations
    • Applications
    • Tenants
    • Identity Providers
    • Key Master
    • SCIM
    • Search
    • Authentication and Authorization
    • Integration Points
    • Localization and Internationalization
    • Editions and Features
    • Roadmap
  • Installation Guide
    • Overview
    • System Requirements
    • Server Layout
    • Cloud
    • Cluster
    • Docker
    • Fast Path
    • Kubernetes
      • Overview
      • Deployment Guide
      • Minikube Setup
      • Amazon EKS Setup
      • Google GKE Setup
      • Microsoft AKS Setup
    • Kickstart™
    • Homebrew
    • Packages
    • Database
    • FusionAuth App
    • FusionAuth Search
    • Common Configuration
  • Admin Guide
    • Overview
    • Account Portal
    • Config Management
    • Licensing
    • Monitoring
    • Proxy Setup
    • Securing
    • Technical Support
    • Troubleshooting
    • Upgrading
  • Migration Guide
    • Overview
    • General
    • Auth0
    • Keycloak
    • Amazon Cognito
    • Firebase
    • Tutorial
  • APIs
    • Overview
    • Authentication
    • Errors
    • Actioning Users
    • API Keys
    • Applications
    • Audit Logs
    • Connectors
      • Overview
      • Generic
      • LDAP
    • Consents
    • Emails
    • Entity Management
      • Overview
      • Entities
      • Entity Types
      • Grants
    • Event Logs
    • Families
    • Forms
    • Form Fields
    • Groups
    • Identity Providers
      • Overview
      • Links
      • Apple
      • External JWT
      • Epic Games
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
      • SAML v2
      • SAML v2 IdP Initiated
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • Xbox
    • Integrations
    • IP Access Control Lists
    • JWT
    • Keys
    • Lambdas
    • Login
    • Message Templates
    • Messengers
      • Overview
      • Generic
      • Kafka
      • Twilio
    • Multi-Factor/Two Factor
    • Passwordless
    • Reactor
    • Registrations
    • Reports
    • SCIM
      • Overview
      • SCIM EnterpriseUser
      • SCIM Group
      • SCIM Service Provider Config.
      • SCIM User
    • System
    • Tenants
    • Themes
    • Users
    • User Actions
    • User Action Reasons
    • User Comments
    • Webhooks
  • Client Libraries
    • Overview
    • Dart
    • Go
    • Java
    • JavaScript
    • .NET Core
    • Node
    • OpenAPI
    • PHP
    • Python
    • Ruby
    • Typescript
  • Themes
    • Overview
    • Examples
    • Helpers
    • Localization
    • Template Variables
  • Email & Templates
    • Overview
    • Configure Email
    • Email Templates
    • Email Variables
    • Message Templates
  • Events & Webhooks
    • Overview
    • Writing a Webhook
    • Securing Webhooks
    • Events
      • Overview
      • Audit Log Create
      • Event Log Create
      • JWT Public Key Update
      • JWT Refresh
      • JWT Refresh Token Revoke
      • Kickstart Success
      • User Action
      • User Bulk Create
      • User Create
      • User Create Complete
      • User Deactivate
      • User Delete
      • User Delete Complete
      • User Email Update
      • User Email Verified
      • User IdP Link
      • User IdP Unlink
      • User Login Failed
      • User Login Id Duplicate Create
      • User Login Id Duplicate Update
      • User Login New Device
      • User Login Success
      • User Login Suspicious
      • User Password Breach
      • User Password Reset Send
      • User Password Reset Start
      • User Password Reset Success
      • User Password Update
      • User Reactivate
      • User Registration Create
      • User Registration Create Complete
      • User Registration Delete
      • User Registration Delete Complete
      • User Registration Update
      • User Registration Update Complete
      • User Registration Verified
      • User Two Factor Method Add
      • User Two Factor Method Remove
      • User Update
      • User Update Complete
  • Example Apps
    • Overview
    • Dart
    • Go
    • Java
    • JavaScript
    • .NET Core
    • PHP
    • Python
    • Ruby
  • Lambdas
    • Overview
    • Apple Reconcile
    • Client Cred. JWT Populate
    • Epic Games Reconcile
    • External JWT Reconcile
    • Facebook Reconcile
    • Google Reconcile
    • HYPR Reconcile
    • JWT Populate
    • LDAP Connector Reconcile
    • LinkedIn Reconcile
    • Nintendo Reconcile
    • OpenID Connect Reconcile
    • SAML v2 Populate
    • SAML v2 Reconcile
    • SCIM Group Req. Converter
    • SCIM Group Resp. Converter
    • SCIM User Req. Converter
    • SCIM User Resp. Converter
    • Sony PSN Reconcile
    • Steam Reconcile
    • Twitch Reconcile
    • Twitter Reconcile
    • Xbox Reconcile
  • Identity Providers
    • Overview
    • Apple
    • Epic Games
    • External JWT
      • Overview
      • Example
    • Facebook
    • Google
    • HYPR
    • LinkedIn
    • Nintendo
    • OpenID Connect
      • Overview
      • Azure AD
      • Discord
      • Github
    • Sony PlayStation Network
    • Steam
    • Twitch
    • Twitter
    • SAML v2
      • Overview
      • ADFS
    • SAML v2 IdP Initiated
      • Overview
      • Okta
    • Xbox
  • Messengers
    • Overview
    • Generic Messenger
    • Kafka Messenger
    • Twilio Messenger
  • Connectors
    • Overview
    • Generic Connector
    • LDAP Connector
    • FusionAuth Connector
  • Self Service Account Mgmt
    • Overview
    • Updating User Data & Password
    • Add Two-Factor Authenticator
    • Add Two-Factor Email
    • Add Two-Factor SMS
    • Customizing
    • Troubleshooting
  • Advanced Threat Detection
    • Overview
  • Integrations
    • Overview
    • CleanSpeak
    • Kafka
    • Twilio
  • OpenID Connect & OAuth 2.0
    • Overview
    • Endpoints
    • Tokens
  • SAML v2 IdP
    • Overview
    • Google
    • Zendesk
  • Plugins
    • Plugins
    • Writing a Plugin
    • Custom Password Hashing
  • Guides
    • Overview
    • Advanced Registration Forms
    • Breached Password Detection
    • Multi-Factor Authentication
    • Multi-Tenant
    • Passwordless
    • Securing Your APIs
    • Silent Mode
    • Single Sign-on
  • Tutorials
    • Overview
    • User Control & Gating
      • Gate Unverified Users
      • Gate Unverified Registrations
      • User Account Lockout
    • Setup Wizard & First Login
    • Register/Login a User
    • Start and Stop FusionAuth
    • Authentication Tokens
    • Key Rotation
    • JSON Web Tokens
    • Prometheus Setup
    • Switch Search Engines
    • Two Factor (pre 1.26)
  • Reference
    • CORS
    • Configuration
    • Data Types
    • Known Limitations
    • Password Hashes

    Passwordless Authentication

    What Is Passwordless

    Passwordless authentication allows a user to prove their identity without a password.

    With FusionAuth, passwordless authentication is by default implemented with a one use, timebound code, delivered by email. However, that is not the only option. If you need more customization, you can use the passwordless API to generate a code. Then you may deliver it by another method, such as an SMS or push notification.

    This guide will cover the FusionAuth passwordless authentication implementation including standard configuration, APIs you may use to build a custom experience, and system settings.

    Here’s a video showing the default passwordless process in FusionAuth:

    • What Is Passwordless

    • When Does It Make Sense

    • Setting Up For Passwordless

    • Using the FusionAuth OAuth Interface

    • Using the API directly

    • Two Factor Authentication

    • Customizing Passwordless

    • Security

    • Troubleshooting

    When Does It Make Sense

    Passwordless authentication eases a user’s sign-in experience. Rather than having to remember which password they used, a user gives their email address and is sent a link. When they click through, they are authenticated.

    In addition to being easier for users, a passwordless login experience prevents them from reusing the same password across different sites or applications. No longer will you worry about another website’s data breach causing illicit access to your system. In addition, password brute forcing is no longer a threat since the codes are one-time use.

    Setting Up For Passwordless

    If you are planning to use passwordless, you have two options. The first option you can use is the FusionAuth OAuth interface. FusionAuth’s OAuth interface is customizable via themes to make each of the web pages look like your application. The other option is using the passwordless API. Let’s look at each in turn.

    In either case, you should:

    • Configure your SMTP server settings under Tenants → Email. If you are testing this flow out locally, you may want to use mailcatcher

    • Create an application

    • Turn on "Passwordless Login" under the "Security" tab of the application configuration:

      Turn on passwordless login
    • Add a user and register them with your application. Make sure you register a valid email address

    Since, by default, passwordless authentication requires email delivery, ensure FusionAuth is correctly configured to send email. One way to test email delivery is by creating a user and sending them a password setup email.

    Using the FusionAuth OAuth Interface

    The FusionAuth OAuth Interface is also known as "hosted login pages" because FusionAuth hosts all the login pages.

    Choose the FusionAuth OAuth interface approach if you want to use the Authorization Code grant. Enabling passwordless adds an option for a user to receive a one-time code. Because this is the Authorization Code grant, any library or framework that supports this OAuth grant will work.

    To use this option:

    • Go to your application configuration page in the administration UI

    • Configure the OAuth redirect URL as you would with any application where users authenticate password (more on OAuth config here). Make sure the Authorization Code grant is an enabled grant

    Now you’re done with configuration.

    To test out how your users would experience this:

    1. Go to your application login page and click the "Login with a magic link" button

      The passwordless login magic link.
    2. Enter the user’s email

      The passwordless email request form.
    3. Go to the user’s inbox

    4. Click on the link

    As soon as the link is clicked, the user has begun an Authorization Code grant. You can consume the authorization code using a library or your own code. Whatever you would normally do if someone signed in with a password, you can now do here. This means that you’ll be provided with the same refresh tokens, user data, or JWTs that would be delivered if the user had signed in with a password.

    To customize the look of the login pages, use themes. While editing the theme, you could remove the username/password form. This would force everyone to use passwordless authentication.

    Since changing a theme modifies it across all applications in a tenant, this might also affect the FusionAuth admin application (if the new application is in the default tenant) and other applications in the same tenant. If you want to hide the username/password form on an application by application basis, you can use separate tenants or add logic to your theme to hide parts of the HTML based on the client_id.

    Limitations

    There are a few limitations when using the FusionAuth OAuth interface:

    • This approach will only send the passwordless code via email

    • This approach also requires you to use the Authorization Code or Implicit grant

    Using the API directly

    While using the FusionAuth OAuth interface works for many, you may need more control. You can use the passwordless API to authenticate a user with a one-time code. The passwordless API reference docs cover each of the API calls, but this guide will walk you through an implementation.

    There are a couple of reasons you might choose this method of integration.

    • You can customize every part of the user login experience

    • You can send the code using a different method such as a text message or Slack direct message

    When using this option, you must set up an API key with the appropriate permissions. The minimum level of privilege required is the POST permission to the /api/passwordless/start endpoint.

    Calling the API

    There are four parts to the passwordless flow.

    1. You start the passwordless login via an API call to FusionAuth

    2. Deliver the code to the user

    3. The user enters the code

    4. You complete the passwordless login via an API call

    Starting the login

    You start a passwordless login by calling the /api/passwordless/start endpoint.

    Start Passwordless Login API call
    API_KEY=...
    REQUEST_PAYLOAD='{...}'
    curl  -H "Content-type: application/json" -H "Authorization: $API_KEY" https://local.fusionauth.io/api/passwordless/start -d $REQUEST_PAYLOAD

    Here’s an example request payload:

    Start Passwordless Login Request JSON
    {
      "applicationId": "10000000-0000-0002-0000-000000000001",
      "loginId": "jared@piedpiper.com",
      "state": {
        "client_id": "10000000-0000-0002-0000-000000000001",
        "redirect_uri": "https://piedpiper.com/callback",
        "response_type": "code",
        "scope": "openid",
        "state": "CSRF123"
      }
    }

    The state property in the JSON is optional. If present, it is echoed back to your application at the end of the Passwordless login workflow. This allows anonymous users to interact with your application, then log in and have data from their anonymous session available.

    For example, if you have a shopping site, you may want to allow a user to add items to their cart before they sign in. Once they have logged in, the state parameter can be used to associate their cart id with the authenticated user. In this case, the state key might be set to a JSON object like this:

    { "cart_id" : 1234 }

    The call to /api/passwordless/start begins the authentication process, and returns a response with a code:

    Start Passwordless Login Response JSON
    {
      "code" : "CynAUMCHLxCCAWyHXOVWPQd8ZY0a6U0e3YpYkT0MNxs"
    }

    Possession of this one-time code authenticates the end user. Deliver this code to the end user using whatever method you’d like. If you want to use FusionAuth to deliver the code via email, see Sending the Code Using FusionAuth.

    Sending the Code Using FusionAuth

    This is an optional passwordless API call. If you want to send the passwordless code via the email server configured in FusionAuth, you may use the /api/passwordless/send API endpoint.

    Using this API call allows you the benefits of the FusionAuth locale-aware email templates and email delivery capabilities without requiring you to use the FusionAuth login forms.

    The User Enters the Code

    Once the user possesses the code, they must provide it to your application. You must build an interface for them to do so.

    Completing the Login

    When the user provides the code to you, call the /api/passwordless/login endpoint. You can pass other information such as IP address, but the code is required.

    Complete Passwordless Login Request JSON
    {
      "code" : "CynAUMCHLxCCAWyHXOVWPQd8ZY0a6U0e3YpYkT0MNxs"
    }

    If the code is valid, your application will receive user data, a JWT, and other data based on the application configuration. If you sent a state property in the JSON when starting the authentication process, it will also be included in the response, under the state key.

    Complete Passwordless Response JSON
    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
      "state": {
        "client_id": "10000000-0000-0002-0000-000000000001",
        "redirect_uri": "https://piedpiper.com/callback",
        "response_type": "code",
        "scope": "openid",
        "state": "CSRF123"
      },
      "user": {
        "active": true,
        "birthDate": "1976-05-30",
        "connectorId": "e3306678-a53a-4964-9040-1c96f36dda72",
        "data": {
          "displayName": "Johnny Boy",
          "favoriteColors": [
            "Red",
            "Blue"
          ]
        },
        "email": "example@fusionauth.io",
        "expiry": 1571786483322,
        "firstName": "John",
        "fullName": "John Doe",
        "id": "00000000-0000-0001-0000-000000000000",
        "imageUrl": "http://65.media.tumblr.com/tumblr_l7dbl0MHbU1qz50x3o1_500.png",
        "lastLoginInstant": 1471786483322,
        "lastName": "Doe",
        "middleName": "William",
        "mobilePhone": "303-555-1234",
        "passwordChangeRequired": false,
        "passwordLastUpdateInstant": 1471786483322,
        "preferredLanguages": [
          "en",
          "fr"
        ],
        "registrations": [
          {
            "applicationId": "10000000-0000-0002-0000-000000000001",
            "data": {
              "displayName": "Johnny",
              "favoriteSports": [
                "Football",
                "Basketball"
              ]
            },
            "id": "00000000-0000-0002-0000-000000000000",
            "insertInstant": 1446064706250,
            "lastLoginInstant": 1456064601291,
            "preferredLanguages": [
              "en",
              "fr"
            ],
            "roles": [
              "user",
              "community_helper"
            ],
            "username": "johnny123",
            "usernameStatus": "ACTIVE"
          }
        ],
        "timezone": "America/Denver",
        "tenantId": "f24aca2b-ce4a-4dad-951a-c9d690e71415",
        "twoFactorEnabled": false,
        "usernameStatus": "ACTIVE",
        "username": "johnny123",
        "verified": true
      }
    }

    The user is now authenticated. Your application has user data, pre-existing state if provided, and a JWT which can be used to represent the user to other resources. If you want to send the JWT to a client as a cookie, you can now do so.

    Additional Passwordless Login Parameters

    JWTs are typically passed to other systems like an API server to enable access to protected resources (more about JWTs). If you are using passwordless authentication and are not using the JWT, you can turn off its generation. Creating and signing the JWT requires server resources; turning JWT generation off will improve performance.

    To do so, set the noJWT parameter to true when you call the complete API endpoint.

    Common Failure Paths

    Every time you start a passwordless login for a given user, all other codes for that user are marked invalid. Codes are also invalid after a configurable time limit.

    If a user provides a code that is invalid, if their account is locked, or if there is any other issue in the request, a status code in the 400 range will be returned. Please consult the passwordless API reference docs for more details about return status codes.

    Two Factor Authentication

    You can use FusionAuth passwordless authentication in combination with two-factor authentication (also called multi-factor authentication or MFA).

    When two-factor authentication is enabled for a user, after the one-time code has been provided they are prompted to provide an additional two-factor verification code.

    Learn more about setting up two-factor authentication.

    Two Factor Authentication With the API

    Two factor authentication also works when using the api. In that case, when you complete the passwordless authentication, instead of getting the user data, you’ll get a twoFactorId:

    {"twoFactorId":"VnNILnXs_EDG-cjwokwITRApmAxCMkojeT3CUqqLhLc"}

    Your application must then prompt the user for their two-factor code, from SMS or an application like Google Authenticator. Note that this is an entirely different code than the one-time code returned when you started the passwordless login. Pass the twoFactorId and the two-factor code to the /api/two-factor/login endpoint in order to complete the two-factor authentication.

    If a user has previously completed a two-factor authentication and has decided to trust the device, you may have a twoFactorTrustId value. This can be passed to the /api/passwordless/login endpoint. If valid, this will skip the two-factor challenge.

    Customizing Passwordless

    You can configure the FusionAuth passwordless implementation to meet your application’s needs in a number of ways.

    Templates

    If you are using the FusionAuth provided email templates, whether you are using the standard FusionAuth user interface or the /api/passwordless/send call, you will need to customize them. Since the template references "FusionAuth", duplicate the template:

    Duplicate the passwordless login email template.

    Then modify it with your branding and messaging.

    Modifying the passwordless email template.

    Configure the tenant email template settings to use your passwordless login template.

    Updating the tenant to use the new passwordless email template.

    When customizing, you can use any Apache FreeMarker built-ins within the template and in the subject. Make sure you modify both the HTML and text templates. Here are the default templates:

    The default passwordless HTML template
    [#setting url_escaping_charset="UTF-8"]
    You have requested to log into FusionAuth using this email address. If you do not recognize this request please ignore this email.
    <p>
      [#-- The optional 'state' map provided on the Start Passwordless API call is exposed in the template as 'state' --]
      [#assign url = "https://local.fusionauth.io/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
      [#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]
    
      <a href="${url}">${url}</a>
    </p>
    - FusionAuth Admin
    The default passwordless text template
    [#setting url_escaping_charset="UTF-8"]
    You have requested to log into FusionAuth using this email address. If you do not recognize this request please ignore this email.
    
    [#-- The optional 'state' map provided on the Start Passwordless API call is exposed in the template as 'state' --]
    [#assign url = "https://local.fusionauth.io/oauth2/passwordless/${code}?tenantId=${user.tenantId}" /]
    [#list state!{} as key, value][#if key != "tenantId" && value??][#assign url = url + "&" + key?url + "=" + value?url/][/#if][/#list]
    
    ${url}
    
    - FusionAuth Admin

    You can localize the template as well:

    The localization screen for your email templates.

    Here is more information about email templates.

    Customizing the Subject

    Here’s an example of how to customize the subject with FreeMarker. The subject below shows how to customize the subject with the time the link expires.

    Customizing the subject
    [#setting time_zone = (user.timezone)!"US/Denver"]
    [#setting time_format = "h:mm a"]
    Expires at: ${((.now?date?long + timeToLive * 1000)?number_to_time)?string}

    One Time Code Customization

    You can modify the lifetime of the code delivered to users. By default it is 180 seconds; change this in the tenant settings:

    The tenant settings to customize code lifetime and generation strategy.

    You can also change the type of the code generated. For example, you may want your code to be only alphanumeric characters.

    You may change your code length or generation strategy for security or user experience reasons. You may have requirements that specify a certain code length. If you deliver the code by text message, having a user enter a six digit alphanumeric code sent to them by SMS is a lot easier than a 64 byte string.

    You have the following options for the code generation strategy:

    • alphabetic characters

    • alphanumeric characters

    • bytes

    • digits

    Consult the tenant API documentation for the length limits, which vary based on the strategy.

    Security

    With passwordless authentication, if the user’s email account is hijacked, their account on your system is compromised. However, many organizations have security policies and protections around email accounts. It is often easier to protect and regularly change one email account password than to change all of a user’s passwords. Email accounts are also more likely to have two-factor authentication enabled.

    One way to increase the security of your passwordless authentications is to decrease the lifetime of the code. This will help if the email is compromised or accidentally forwarded.

    There are no limits on how many passwordless requests can be made for a user, but only the most recent code is valid. Using any of the others, even if they have not yet expired, will display an Invalid login credentials message to the user.

    If someone tries to log in with an email that is not present in the FusionAuth user database, they’ll see the same notification as they would if the email existed. No email will be sent.

    If you use the passwordless API, follow the principle of least privilege, and limit the calls to which the API key has access. If you are using the API key only for passwordless login, then don’t give this key any other permissions.

    What About Users' Passwords

    When FusionAuth is your user datastore, adding a user means you must either provide a password or send them a link to set up their initial password.

    If you are only allowing passwordless authentication for your application, don’t allow the user to specify a password and instead generate a random series of characters for the password. We recommend generating at least 32 characters in the ascii character set that are completely random to ensure the user’s account is secure.

    Troubleshooting

    Email

    If you are experiencing troubles with email deliverability, review the email troubleshooting documentation.

    Invalid Links

    In some cases, email clients will visit links in an email before the user does. In particular, this is known to happen with Outlook "safe links". If the client does this when the email contains a passwordless one time code, that code may be invalid when the user clicks on it, as it has already been "used" by the client.

    One option is to consult with your email client adminstrator. It may be possible to add the application’s URL to an allow list.

    In version 1.27, FusionAuth changed the link processing behavior to remedy this for some situations. Given the wide variety of email client behavior, it may still be present in other scenarios.

    If your users' passwordless codes are being expired by an email client, please add details to this GitHub issue.

    Feedback

    How helpful was this page?

    See a problem?

    File an issue in our docs repo

    © 2021 FusionAuth
    Subscribe for developer updates