OpenID Connect

This document describes our OAuth 2.0 implementation for authentication, which conforms to the OpenID Connect specification.

Setting up OAuth 2.0

Before your application can use Yuvo's OAuth 2.0 authentication system for user login, you must set up a client in the Manage Clients page, to obtain OAuth 2.0 credentials.

Obtain OAuth 2.0 credentials

You need OAuth 2.0 credentials, including a client ID and client secret, to authenticate users and gain access to User's identity.

To find your Client ID and Client Secret, do the following:

  1. Create your project's OAuth 2.0 credentials by clicking Add New Client (In Manage Clients page), and provide the name and redirect URI.
  2. Look for the newly created client and click "View" button to access the Client ID and Client Secret.

Authenticating the user

Authenticating the user involves obtaining an ID token and validating it. ID tokens are a standardized feature of OpenID Connect designed for use in sharing identity assertions on the Internet.

The most commonly used approaches for authenticating a user and obtaining an ID token are called the "server" flow and the "implicit" flow. The server flow allows the back-end server of an application to verify the identity of the person using a browser or mobile device. The implicit flow is used when a client-side application (typically a JavaScript app running in the browser) needs to access APIs directly instead of via its back-end server.

This document describes how to perform the server flow for authenticating the user. The implicit flow is significantly more complicated because of security risks in handling and using tokens on the client side. Currently Yuvo does not support implicit flow.

Server flow

Make sure you set up your client in the Manage Clients page to enable it to use these protocols and authenticate your users. When a user tries to log in with Yuvo, you need to:

  1. Create an anti-forgery state token
  2. Send an authentication request to Yuvo
  3. Confirm the anti-forgery state token
  4. Exchange code for access token and ID token
  5. Obtain user information from the ID token
  6. Authenticate the user

1. Create an anti-forgery state token

You must protect the security of your users by preventing request forgery attacks. The first step is creating a unique session token that holds state between your app and the user's client. You later match this unique session token with the authentication response returned by the Yuvo OAuth Login service to verify that the user is making the request and not a malicious attacker. These tokens are often referred to as cross-site request forgery (CSRF) tokens.

One good choice for a state token is a string of 30 or so characters constructed using a high-quality random-number generator. Another is a hash generated by signing some of your session state variables with a key that is kept secret on your back-end.

2. Send an authentication request to Yuvo

The next step is forming an HTTPS GET request with the appropriate URI parameters. Note the use of HTTPS rather than HTTP in all the steps of this process; HTTP connections are refused. You should retrieve the base URI from the Discovery document using the key authorization_endpoint. The following discussion assumes the base URI is https://secure.yuvohub.com/openid/authorize.

For a basic request, specify the following parameters:

  • client_id, which you obtain from the Manage Clients page.
  • response_type, which in a basic request should be code.
  • scope, which in a basic request should be openid email. (Read more at scope.)
  • redirect_uri should be the HTTP endpoint on your server that will receive the response from Yuvo. You specify this URI in the Manage Clients.
  • state should include the value of the anti-forgery unique session token, as well as any other information needed to recover the context when the user returns to your application, e.g., the starting URL. (Read more at state.)
  • nonce a random value generated by your app that enables replay protection when present.

Here is an example of a complete OpenID Connect authentication URI, with line breaks and spaces for readability:

https://secure.yuvohub.com/openid/authorize?  client_id=1234567&  response_type=code&  scope=openid%20email&  redirect_uri=https://oauth2-login-demo.example.com/code&  state=security_token%3D138r5719ru3e1%26url%3Dhttps://oauth2-login-demo.example.com/myHome&  nonce=0394852-3190485-2490358&

Users are required to give consent if your app requests any new information about them, or if your app requests account access that they have not previously approved.

3. Confirm anti-forgery state token

The response is sent to the redirect_uri that you specified in the request. All responses are returned in the query string, as shown below:

https://oauth2-login-demo.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps://oa2cb.example.com/myHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7

On the server, you must confirm that the state received from Yuvo matches the session token you created in Step 1. This round-trip verification helps to ensure that the user, not a malicious script, is making the request.

4. Exchange code for access token and ID token

The response includes a code parameter, a one-time authorization code that your server can exchange for an access token and ID token. Your server makes this exchange by sending an HTTPS POST request. The POST request is sent to the token endpoint, which you should retrieve from the Discovery document using the key token_endpoint. The following discussion assumes the endpoint is https://secure.yuvohub.com/token. The request must include the following parameters in the POST body:

Field Description
code The authorization code that is returned from the initial request.
client_id The client ID that you obtain from the Manage Clients page, as described in Obtain OAuth 2.0 credentials.
client_secret The client secret that you obtain from the Manage Clients page, as described in Obtain OAuth 2.0 credentials.
redirect_uri The URI that you specify in the Manage Clients page, as described in Set a redirect URI.
grant_type This field must contain a value of authorization_code, as defined in the OAuth 2.0 specification.

The actual request might look like the following example:

POST /openid/token HTTP/1.1 Host: https://secure.yuvohub.com/ Content-Type: application/x-www-form-urlencoded code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=1234567& client_secret={client_secret}& redirect_uri=https://oauth2-login-demo.example.com/code& grant_type=authorization_code

A successful response to this request contains the following fields in a JSON array:

Field Description
access_token A token that can be sent to a Yuvo API.
id_token A JWT that contains identity information about the user that is digitally signed by Yuvo.
expires_in The remaining lifetime of the access token.
token_type Identifies the type of token returned. At this time, this field always has the value Bearer.
refresh_token (optional) This field is only present if access_type=offline is included in the authentication request. For details, see Refresh tokens.

5. Obtain user information from the ID token

An ID Token is a JWT (JSON Web Token), that is, a cryptographically signed Base64-encoded JSON object. Normally, it is critical that you validate an ID token before you use it, but since you are communicating directly with Yuvo over an intermediary-free HTTPS channel and using your client secret to authenticate yourself to Yuvo, you can be confident that the token you receive really comes from Yuvo and is valid. If your server passes the ID token to other components of your app, it is extremely important that the other components validate the token before using it.

Since most API libraries combine the validation with the work of decoding the base64 and parsing the JSON, you will probably end up validating the token anyway as you access the fields in the ID token.

An ID token's payload

An ID token is a JSON object containing a set of name/value pairs. Here’s an example, formatted for readability:

{"iss":"https://secure.yuvohub.com/openid", "sub":"153", "aud":"1234567", "exp":1353604926, "iat":1353601026, "auth_time": 1522718109, "at_hash": "viGUlJDmqGv7-BW8KK7giw", "email":"jsmith@example.com" }

Yuvo ID Tokens may contain the following fields (known as claims):

Claim Provided Description
iss always The Issuer Identifier for the Issuer of the response. Always https://secure.yuvohub.com/openid/for Yuvo ID tokens.
at_hash Access token hash. Provides validation that the access token is tied to the identity token. If the ID token is issued with an access token in the server flow, this is always included. This can be used as an alternate mechanism to protect against cross-site request forgery attacks, but if you follow Step 1 and Step 3 it is not necessary to verify the access token.
sub always An identifier for the user, unique among all Yuvo accounts and never reused. A Yuvo account can have multiple emails at different points in time, but the sub value is never changed. Use sub within your application as the unique-identifier key for the user.
email The user's email address. This may not be unique and is not suitable for use as a primary key. Provided only if your scope included the string "email".
profile The URL of the user's profile page. Might be provided when:
  • The request scope included the string "profile"
  • The ID token is returned from a token refresh
When profile claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present.
picture The URL of the user's profile picture. Might be provided when:
  • The request scope included the string "profile"
  • The ID token is returned from a token refresh
When picture claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present.
name The user's full name, in a displayable form. Might be provided when:
  • The request scope included the string "profile"
  • The ID token is returned from a token refresh
When name claims are present, you can use them to update your app's user records. Note that this claim is never guaranteed to be present.
aud always Identifies the audience that this ID token is intended for. It must be one of the OAuth 2.0 client IDs of your application.
iat always The time the ID token was issued, represented in Unix time (integer seconds).
exp always The time the ID token expires, represented in Unix time (integer seconds).

6. Authenticate the user

After obtaining user information from the ID token, you should query your app's user database. If the user already exists in your database, you should start an application session for that user.

If the user does not exist in your user database, you should redirect the user to your new-user sign-up flow. You may be able to auto-register the user based on the information you receive from Yuvo, or at the very least you may be able to pre-populate many of the fields that you require on your registration form. In addition to the information in the ID token, you can get additional user profile information at our user profile endpoints.

Validating an ID token

You need to validate all ID tokens on your server unless you know that they came directly from Yuvo. For example, your server must verify as authentic any ID tokens it receives from your client apps.

The following are common situations where you might send ID tokens to your server:

  • Sending ID tokens with requests that need to be authenticated. The ID tokens tell you the particular user making the request and for which client that ID token was granted.
  • Sending ID tokens that contain OpenID 2.0 identifiers (openid_id) that need to be mapped to the Yuvo ID (sub).

ID tokens are sensitive and can be misused if intercepted. You must ensure that these tokens are handled securely by transmitting them only over HTTPS and only via POST data or within request headers. If you store them on your server, you must also store them securely.

One thing that makes ID tokens useful is that fact that you can pass them around different components of your app. These components can use an ID token as a lightweight authentication mechanism authenticating the app and the user. But before you can use the information in the ID token or rely on it as an assertion that the user has authenticated, you must validate it.

Validation of an ID token requires several steps:

  1. Verify that the ID token is properly signed by the issuer. Yuvo-issued tokens are signed using one of the certificates found at the URI specified in the jwks_uri field of the discovery document.
  2. Verify that the value of iss in the ID token is equal to https://secure.yuvohub.com/openid.
  3. Verify that the value of aud in the ID token is equal to your app’s client ID.
  4. Verify that the expiry time (exp) of the ID token has not passed.

Steps 2 to 4 involve only string and date comparisons which are quite straight forward, so we won't detail them here.

The first step is more complex, and involves cryptographic signature checking.

This involves an HTTP round trip, introducing latency and the potential for network breakage. Since Yuvo changes its public keys only infrequently, you can cache them and, in the vast majority of cases, perform local validation much more efficiently. This requires retrieving and parsing certificates, and making the appropriate crypto calls to check the signature. Fortunately, there are well-debugged libraries available in a wide variety of languages to accomplish this.

Obtaining user profile information

To obtain additional profile information about the user, you can use the access token (which your application receives during the authentication flow) and the OpenID Connect standard:

  1. To be OpenID-compliant, you must include the openid profile scope in your authentication request.

    If you want the user’s email address to be included, you can optionally request the openid email scope. To specify both profile and email, you can include the following parameter in your authentication request URI:

    scope=openid%20email%20profile
  2. Add your access token to the authorization header and make an HTTPS GET request to the userinfo endpoint. Users may choose to supply or withhold certain fields, so you might not get information for every field to which your scopes request access.

The Discovery document

The OpenID Connect protocol requires the use of multiple endpoints for authenticating users, and for requesting resources including tokens, user information, and public keys.

To simplify implementations and increase flexibility, OpenID Connect allows the use of a "Discovery document," a JSON document found at a well-known location containing key-value pairs which provide details about the OpenID Connect provider's configuration, including the URIs of the authorization, token, userinfo, and public-keys endpoints. The Discovery document for Yuvo's OpenID Connect service may be retrieved from:

https://secure.yuvohub.com/openid/.well-known/openid-configuration

To use Yuvo's OpenID Connect services, you should hard-code the Discovery-document URI (https://secure.yuvohub.com/openid/.well-known/openid-configuration) into your application. Your application fetches the document, then retrieves endpoint URIs from it as needed. For example, to authenticate a user, your code would retrieve the value of the authorization_endpoint key and use its value (https://secure.yuvohub.com/openid/authorize in the example below) as the base URI for authentication requests that are sent to Yuvo.

Here is an example of such a document; the field names are those specified in OpenID Connect Discovery 1.0 (refer to that document for their meanings). The values are purely illustrative and might change, although they are copied from from a recent version of the actual Yuvo Discovery document:

{
    "issuer": "https://secure.yuvohub.com/openid",
    "authorization_endpoint": "https://secure.yuvohub.com/openid/authorize",
    "token_endpoint": "https://secure.yuvohub.com/openid/token",
    "userinfo_endpoint": "https://secure.yuvohub.com/openid/userinfo",
    "end_session_endpoint": "https://secure.yuvohub.com/openid/end-session",
    "response_types_supported": [
        "code",
        "id_token",
        "id_token token",
        "code token",
        "code id_token",
        "code id_token token"
    ],
    "jwks_uri": "https://secure.yuvohub.com/openid/jwks",
    "id_token_signing_alg_values_supported": [
        "HS256",
        "RS256"
    ],
    "subject_types_supported": [
        "public"
    ],
    "token_endpoint_auth_methods_supported": [
        "client_secret_post",
        "client_secret_basic"
    ]
}

            

You may be able to avoid an HTTP round-trip by caching the values from the Discovery document. Standard HTTP caching headers are used and should be respected.