Skip to main content

Add New Authenticator

Overview#

The majority of customers have multiple devices, and they expect to be able to access your application from all of them. In order to add this new device securely, a previously registered device is required to authorize the new device.

Adding a new authenticator requires a backend API call, therefore it is recommended to leverage the LoginID server SDK. However, the LoginID APIs can be called directly if required.

It is best practice to have the end user attempt to log in on their new device, rather than give them an explicit option to say they are on a new device. After authentication is attempted, there will be an error indicating there are no credentials available on the device. Once this error is returned, the user should be prompted to either add this new device as an authenticator, or only use this device once.

At a high level, this flow consists of three main components: generation of a code, authorization of that code, then using that code to add the new authenticator.

Prerequisites#

  • Create an application on LoginID Dashboard
  • Download Client SDK
  • Download Server SDK (optional)

With Server SDK#

Step 1: Get user_id#

The code generation functionality requires the user_id rather than the username. In the case where you have not stored the user_id, you can call the below SDK method to get the user_id.

Server SDK: .getUserID(username)

The response is the user’s profile information (including the user_id) for the provided username.

Step 2: Generate Code#

Once you have the user_id, you must generate a code. This code is what ties the new device and existing device together. To understand which code_type is best for your use case, please see our guide on choosing a code type. The purpose should be add_credential.

Server SDK: .generateCode(purpose, code_type, username, authorized)

This returns a code of the specified type as well as its expiry time.

Step 3: Notify User#

LoginID does not send the code to the user, as we understand it is important for you to maintain direct contact with your clients. Depending on your desired user experience, you can use the code returned in Step 2 in a variety of ways. For adding a new authenticator, this is frequently done through a QR code or push notification to the already registered device.

Step 4: Authorize Code#

The code generated in Step 2 has to be authorized before being able to be used to add the new authenticator. This should be done from an end user’s previously registered device. After the end user logs into their already registered device, they would be able to authorize the code.

Server SDK: .authorizeCode(code, purpose, code_type, user_id)

This returns confirmation the code was authorized, as well as the time the operation was completed.

Optional: Invalidate Code#

It may be desired to provide the user the ability to deny the addition of a new device. If so, the code generated in Step 2 can be invalidated.

Server SDK: .invalidateAllCodes(purpose, code_type, user_id)

Step 5: Add New Credential#

The authorized code can now be used to add the new device as an authenticator. The user has the option to specify a credential name for their new device at this point. If no credential name is specified, a default name will be populated based on the user agent.

Client SDK: .addFido2Credential(user_id, code, code_type, credential_name)

This returns a jwt containing the id_token. At this point, the user is now authenticated on the new device and that device has been added as an authenticator.


Calling APIs Directly#

Step 1: Retrieve user_id#

The code generation functionality requires the user_id rather than the username. In the case where you have not stored the user_id, you can call the below endpoint to get the user_id.

The required scope of the service token for this request is users.retrieve

Request:

POST /api/native/manage/users/retrieve

Header:

Authorization: Bearer {authorization_token}

Body:

{
  "username": "string"
}

The response is the user’s profile information (including the user_id) for the provided username.

Step 2: Generate Code#

Once you have the user_id, you must generate a code. This code is what ties the new device and existing device together. To understand which code_type is best for your use case, please see our guide on choosing a code type.

The scope of the authorization token is codes.generate

Request:

POST /api/native/codes/{type}/generate

Header:

Authorization: Bearer {authorization_token}

Body:

{
  "client_id": <dashboard-generated client_id>,
  "user_id": <user_id_to_be_authenticated>,
  "purpose": "add_credential",
  "authorize": <bool>
}

Where:

  • client_id: generated on the LoginID dashboard
  • user_id: ID of an already registered user. In this case it would be the user_id of the person trying to add a new device
  • purpose: the reason for generating a code. In this case it would always be add_credential
  • authorize: Whether the request to generate the code is pre-authorized. In this case it would be false as the new device has not yet been authorized

Response:

{
  "code": <string>,
  "expires_at": <ISO8061 timestamp>
  "is_authorized": false
} 

Step 3: Notify User#

LoginID does not send the code to the user, as we understand it is important for you to maintain direct contact with your clients. Depending on your desired user experience, you can use the code returned in Step 2 in a variety of ways. For adding a new authenticator, this is frequently done through a QR code or push notification to the already registered device.

Step 4: Authorize Code#

The code generated in Step 2 has to be authorized before being able to be used to add the new authenticator. This should be done from an end user’s previously registered device. After the end user logs into their already registered device, they would be able to authorize the code.

The scope of the authorization token is codes.authorize

Request:

POST /api/native/codes/{type}/authorize

Authorization:

Authorization: Bearer {authorization_token}

Body:

{
  "client_id": <dashboard-generated client_id>,
  "user_id": <user_id to be authenticated>,
  "purpose": "add_credential",
  "code": <user-entered code as string>
}

Response:

{
  "expires_at": <ISO8061 timestamp>,
  "is_authorized": true
}
note

The user_id is returned in the JWT in the /authenticate/complete call.

Optional: Invalidate Code#

It may be desired to provide the user the ability to deny the addition of a new device. If so, the code generated in Step 2 can be invalidated.

The scope of the authorization token is codes.invalidate

Request:

POST /api/native/codes/{code_type}/invalidate-all

Authorization:

Authorization: Bearer {authorization_token}

Body:

{
  "client_id": <dashboard-generated client_id>,
  "user_id": <user_id_to_be_authenticated>,
  "purpose": "add_credential"
}

Response:

{
  "deleted_at": <ISO8061 timestamp>
}

Step 5: Initiate Addition of New Authenticator#

The authorized code can now be used to add the new device as an authenticator. The user has the option to specify a credential name for their new device at this point. If no credential name is specified, a default name will be populated based on the user agent.

The scope of the authorization token is credentials.add

Request:

POST /credentials/fido2/init/code

Authorization:

Authorization: Bearer {authorization_token}

Body:

{
  "client_id": "string",
  "user_id": "string",
  "authentication_code": {
    "code": <code from Step 1 as string>,
    "type": <type from Step 1 as string>
  }
}
note

This call should be made by the front end, but requires an authorization token.

The response will be the standard registration initialization payload (i.e. same as /register/fido2/init).

Step 6: Credential Addition Completion#

Once the credential addition flow has been initialized, the user has the ability to enter a custom device name. After entering the device name, this would be submitted on the /credentials/fido2/complete call. The payload structure follows the same pattern as the normal registration flow.

After a successful credential addition, similar to the registration flow, a JWT will be returned indicating the successful registration of the new authenticator.