Skip navigation
Documentation

Duo OIDC Auth API - Duo Universal Prompt Public Preview

Last Updated: November 23rd, 2020

The Duo OIDC Auth API is an OIDC standards-based API for adding strong two-factor authentication to your web application. This API supports the Duo Universal Prompt, which uses a new OIDC-compliant authentication protocol to perform two-factor authentication with our existing Duo Prompt user interface.

The OIDC Auth API supporting the Duo Universal Prompt is in Public Preview. Contact duo-frameless-integrations-beta@cisco.com with feedback.

Overview

Duo uses the OpenID Connect protocol to deliver two-factor authentication to users and make available the result of the two-factor authentication. The flow begins with a health check to Duo's service, followed by a request for authorization. After validating this request, Duo redirects the user to a Duo-hosted site for multi-factor authentication. Upon a successful second-factor authentication Duo redirect the user to the redirect URI supplied in the initial Authorization request. To retrieve the result of the second factor authentication from Duo the client will make a request to the Token endpoint.

Duo OIDC MFA Authentication Flow

The redirect URI is a critical part of adding support for the Duo Universal Prompt. After a successful two-factor authentication, the end-user must be sent to a page which will retrieve the result of the authentication. The redirect URI parameter in the JWT sent to Duo in the first authorization request tells Duo where to redirect the end-user.

The client can configure their desired behavior in the event of a failed health check to determine whether a user should be granted access to their protected resources or prevented from doing so.

JSON Web Tokens (JWTs) are used in each request and should be signed using the secret key you are provided when protecting an application.

Adding Duo requires some understanding of your application's language and authentication process.

To add Duo to your application using our client SDKs for Python or Java, see the Duo Web v4 instructions.

First Steps

Before starting:

  1. Sign up for a Duo account.
  2. Log in to the Duo Admin Panel and navigate to Applications.
  3. Click Protect an Application and locate the entry for Web SDK in the applications list. Click Protect to the far-right to configure the application and get your integration key, secret key, and API hostname. You'll need this information to complete your setup. See Protecting Applications for more information about protecting applications in Duo and additional application options.

    Once you authenticate to this Duo application using the /oauth API the "Integration key" and "Secret key" labels update to "Client ID" and "Client secret" respectively.

  4. Use NTP to ensure that your server's time is correct.

Documented properties will not be removed within a stable version of the API. Once a given API endpoint is documented to return a given property, a property with that name will always appear (although certain properties may only appear under certain conditions).

New, undocumented properties may also appear at any time. For instance, Duo may make available a beta feature involving extra information returned by an API endpoint. Until the property is documented here its format may change or it may even be entirely removed from our API.

Universal Prompt

Duo's next-generation authentication experience, the Universal Prompt, is coming to web-based applications that display the current Duo Prompt in browsers.

Migration to Universal Prompt for your Duo Web application will be a two-step process:

  • Build your application with support for the Universal Prompt by using the OIDC Auth API.
  • Enable the Universal Prompt experience for users of the Duo Web application (when the Universal Prompt becomes available).

If you're creating a Duo Web application for the first time, building it with the OIDC Auth API or Duo Web v4 SDK ensures it will support the Universal Prompt in the future.

The "Universal Prompt" section on the details page of your Duo Web application indicates availability of the update.

Universal Prompt Info

Once a user authenticates to that application via the Duo v4 /oauthapi endpoints, the "Universal Prompt" section of the Duo Web application page reflects this status. When the Universal Prompt becomes available, you'll return here to activate it for users of this application.

Duo Web Universal Prompt Info

Click the See Update Progress link to view the Universal Prompt Update Progress report. This report shows the update availability and migration progress for all your Duo applications that will have Universal Prompt support.

Watch the Duo Blog for future updates about the Duo Universal Prompt.

JSON Web Tokens

JSON Web Tokens (JWTs) are the secure mechanism with which Duo will verify that the request is coming from a trusted client application. More information about the structure of JWTs can be found at JWT.IO and RFC 7519.

JWTs are encoded as a single string, but when decoded have three sections: the header, the payload, and the signature.

Example of an encoded JWT

Encoded JWT

Example of the encoded JWT above decoded and separated into each section

Decoded JWT

In the header, Duo requires that the typ field is equal to JWT and that the alg field is HS512. We also require that the HS512 HMAC algorithm be used to sign the request.

The signature for each JWT should use the client secret value (shown as the "secret key" before you authenticate with the integration) that you can find on your integration’s page in the Admin Panel.

For each OIDC endpoint there exists a required field for supplying a JWT. Each JWT will have the same requirements for the header and signature, but the payload data shape has differing format described as the field name followed by .payload in the endpoint details below. For example, in the Health Check endpoint /oauth/v1/health_check we require that a client_assertion parameter is provided with a value of type JWT. We denote the shape of that JWT payload with the parameter name of client_assertion.payload.

Please note that all Unix timestamps are in seconds.

Endpoints

Health Check

To ensure that all Duo services are operating we require a call to a health check endpoint with arguments that enable us to verify your integration is properly configured.

POST/oauth/v1/health_check

Arguments

Argument Type Required? Description
client_id String Required This value was previously referred to as the integration key. Locate the value on the application's page in the Duo Admin Panel.
client_assertion JWT Required See below for the expected client_assertion payload.
client_assertion.payload Object Required
Argument Type Required? Description
iss String Required The Client ID (or Integration key) from the application's page in the Duo Admin Panel.
sub String Required This should match the above iss value.
aud String Required This field must match the expected base URL where the request was sent, using the “API hostname" from the application's page in the Duo Admin Panel: https://{api_hostname}/oauth/v1/health_check.
exp String Required The time at which the request you are sending should expire. Duo recommends an exp value of five minutes. The timestamp format should be in seconds from Unix epoch.
jti String Required This should be a sufficiently random value unique to each JWT.
iat Integer Optional The time at which the JWT was created.

Example Response

Success:

{
  "stat": "OK",
  "response": {
     “timestamp”: {int32} The time at which the health check occurred.
  }
}

Failure:

{
  “stat”: “FAIL”,                        
    “code”: {String} An API error code which corresponds to our existing API error documentation,
    “timestamp”: {int32} The time at which the health check occurred,
    “message”: {String} The failure error message,
    “message_detail”: {String} Additional information about the error.
  }
}

Authorization Request

This endpoint processes your application’s request for Duo to perform multi-factor authentication and sends the end-user to the Duo-hosted multi-factor authentication prompt. We require various fields to be sent in a JWT with this request in order to determine the validity of the request.

After Duo processes this request, this endpoint will return a response that redirects the end user to a Duo hosted domain to perform multi-factor authentication. This is the same domain that was previously used as the source of the iframe that delivered the authentication prompt.

In the event that multi-factor authentication is unsuccessful, we will not redirect the end-user to the specified redirect URI. A failed authentication will appear in the Authentication Log (found in the Admin Panel) associated with the end-user.

After a successful authentication, Duo redirects the user to the redirect URI specified in the redirect_uri field as described below.

GET/oauth/v1/authorize
POST/oauth/v1/authorize

Arguments

Argument Type Required? Description
response_type String Required We expect the value code to be present for this field.
client_id String Required This value was previously referred to as the integration key. Locate the value on the application's page in the Duo Admin Panel.
request JWT Required See below for the expected request payload.
redirect_uri String Optional The URI where the end-user will be redirected after a successful auth. While supplying this value in the query parameters is optional, if provided it should match the value provided in the JWT payload.
scope String Optional We expect the value openid to be present for this field.
nonce String Optional If provided, this must be a random value that is cryptographically secure such that it is unguessable by a third party. We require a length of at least 16 characters and at most 1024 characters.
state String Optional A random, cryptographically secure value that the client must maintain throughout the authentication. We require a length of at least 16 characters and at most 1024 characters.
request.payload Object Required
Argument Type Required? Description
response_type String Required We expect the value code to be present for this field.
scope String Required Must match the value defined above.
exp String Required The time at which the request you are sending should expire. Duo recommends an exp value of five minutes. The timestamp format should be in seconds from Unix epoch.
client_id String Required The Client ID (or Integration key) from the application's page in the Duo Admin Panel.
redirect_uri String Required The URI where the end-user will be redirected after a successful auth.
state String Required A random, cryptographically secure value that the client must maintain throughout the authentication. We require a length of at least 16 characters and at most 1024 characters.
duo_uname String Required Unique name for the user in Duo. If a user has aliases in Duo, those are valid to send in this field. Subject to username normalization as configured for the application in the Admin Panel.
iss String Optional If provided, this field must match the client_id sent in this payload.
aud String Optional This field must match the expected base URL where the request was sent, using the “API hostname" from the application's page in the Duo Admin Panel: https://{api_hostname}/oauth/v1/token.
nonce String Optional If provided, this must be a random value that is cryptographically secure such that it is unguessable by a third party. We require a length of at least 16 characters and at most 1024 characters.
use_duo_code_attribute Boolean Optional If provided and the value is true, then the authorization code will be returned under the attribute name of duo_code.

We require that the state parameter be sent in either the JWT payload OR in the request query parameters. If the state parameter is present both in the JWT payload and in the request query parameters, we will honor the value sent in the request query parameters. Additionally, while the nonce parameter is optional, if it is sent in both the JWT payload AND the request query parameters, we will honor the value sent in the request query parameters.

Upon request Duo can enable a feature to pre-register redirect_uris such that the redirect_uri provided in the Authorization Request JWT must match one of the pre-registered redirect URIs. If this feature is enabled for a customer and a redirect URI does not match one of the pre-registered values, then the request will fail. Pre-registering redirect URIs is part of the OIDC specification and ensures that users will only be redirected to those URIs specifically pre-registered. Duo’s use of the explicit authorization code flow for the OIDC spec sufficiently protects against these types of attacks, so pre-registering Redirect URIs is optional.

Access Token

The success response to the redirect URI sent previously will have the following structure:

{
  code {String}: A randomly generated authorization code that will be used to retrieve
                 the result of the authentication,
  state {String}: This should be the value for state passed in to the original request.
                  It is up to the client to verify that it is the same value as a security
                  measure. This value specifically protects against CSRF attacks (see RFC 6749).
}

This code can then be exchanged for an ID Token that includes contextual information about the two-factor authentication via a POST request to the Token Endpoint.

If the custom claim of use_duo_code_attribute was sent with a value of true in the authorization request JWT, then the authorization code parameter name will be duo_code instead of code.

POST/oauth/v1/token

Arguments

Argument Type Required? Description
grant_type String Required This must be equal to the string authorization_code.
code String Required This must be equal to the string code received in the previous response.
redirect_uri String Required This must be equal to the redirect URI sent in the request to the Authorization Request endpoint /oauth/v1/authorize.
client_assertion_type String Required This must be equal to the string value urn:ietf:params:oauth:client-assertion-type:jwt-bearer,
client_assertion JWT Required See below for the required payload fields.
client_id JWT Optional The Client ID (or Integration key) from the application's page in the Duo Admin Panel.
request.payload Object Required
Argument Type Required? Description
iss String Required The Client ID (or Integration key) from the application's page in the Duo Admin Panel.
sub String Required This should match the above iss value.
aud String Required This field must match the expected base URL where the request was sent, using the “API hostname" from the application's page in the Duo Admin Panel: https://{api_hostname}/oauth/v1/token.
exp String Required The time at which the request you are sending should expire. Duo recommends an exp value of five minutes. The timestamp format should be in seconds from Unix epoch.
jti String Required This should be a sufficiently random value unique to each JWT.
iat Integer Optional The time at which the JWT was created.

The response from the Token Endpoint includes an ID token, an access token, and contextual information about the authentication. The access token can not be exchanged for further access because Duo is not a first-factor identity provider.

Response Format

The response follows the structure below:

{
    id_token {JWT}: See below for id_token payload structure,
      access_token {String}: A random and unique identifier for the authentication session,
      expires_in {integer}: The time at which the access token is considered to be expired,
      token_type {String}: This will be equal to the string “Bearer”.
}
id_token.payload Object
iss String A URI which details the endpoint that the response came from, with a hostname that should match your Duo application's API hostname.
sub String Equal to the duo_uname of the end-user who authenticated.
preferred_username String Equal to the username sent as duo_uname to the Authorization Request endpoint.
aud String Equal to the Client ID (or Integration key) from the application's page in the Duo Admin Panel.
exp Integer The time at which this JWT expires.
iat Integer The time at which the JWT was issued.
auth_time Integer The time at which this end-user authenticated with Duo.
nonce String The random string generated during the initial authorization request.
auth_result Object
result String This will be equal to the string allow.
status String This will be equal to the string allow.
status_msg String A string describing why the authentication was successful.
auth_context Object Please see the Duo Authentication Logs reason values for the full authentication log information.

Duo highly recommends that you perform the ID Token validation steps outlined in the OIDC Specification. Additionally, if your implementation maintains stateful information about the user authenticating you should validate that the user described in the ID token matches your expected user. For example, if you have stored stateful information with the state parameter that user jdoe is authenticating, you should verify that the ID Token specifically has the username jdoe in the preferred_username field.

Example Response

{
    "aud": "DIXXXXXXXXXXXXXXXXXX",
    "auth_context": {
        "access_device": {
            "browser": "Chrome",
            "browser_version": "85.0.4183.121",
            "flash_version": "uninstalled",
            "hostname": null,
            "ip": "10.65.201.7",
            "is_encryption_enabled": "unknown",
            "is_firewall_enabled": "unknown",
            "is_password_set": "unknown",
            "java_version": "uninstalled",
            "location": {
                "city": "Ann Arbor",
                "country": "United States",
                "state": "Michigan"
            },
            "os": "Mac OS X",
            "os_version": "10.15.7",
            "security_agents": "unknown"
        },
        "alias": "",
        "application": {
            "key": "DIXXXXXXXXXXXXXXXXXX",
            "name": "Web SDK"
        },
        "auth_device": {
            "ip": "10.65.201.8",
            "location": {
                "city": "Ann Arbor",
                "country": "United States",
                "state": "Michigan"
            },
            "name": "734-555-1311"
        },
        "email": "",
        "event_type": "authentication",
        "factor": "duo_push",
        "isotimestamp": "2020-10-19T18:04:48.970845+00:00",
        "ood_software": null,
        "reason": "user_approved",
        "result": "success",
        "timestamp": 1603130688,
        "trusted_endpoint_status": "unknown",
        "txid": "95de7901-6f9f-dad7-ba00-781ca52ae114",
        "user": {
            "groups": [],
            "key": "DUXXXXXXXXXXXXXXXXXX",
            "name": "narroway"
        }
    },
    "auth_result": {
        "result": "allow",
        "status": "allow",
        "status_msg": "Login Successful"
    },
    "auth_time": 1603130688,
    "exp": 1603134288,
    "iat": 1603130689,
    "iss": "https://api-xxxxxxxx.duosecurity.com/oauth/v1/token",
    "preferred_username": "narroway",
    "sub": "narroway"
}

Unsupported OIDC Endpoints

Duo’s implementation of 2FA does not require and therefore does not support the following endpoints/code flows:

  1. UserInfo - Required for Primary.
  2. Discovery Endpoint - Required for Primary.
  3. Refresh - By design 2FA token should be valid only once.
  4. JWKS - Duo does not currently support asymmetric signing and thus does not have an endpoint to describe public keys and their rotations.

Troubleshooting

Need some help? Take a look at our OIDC Auth API Knowledge Base articles or Community discussions. For further assistance, contact Support.