Skip to content

Integrating Webapp with OAuth2

Fanny Strudel edited this page Nov 2, 2016 · 3 revisions

Single Sign On for Web Apps with VMware Identity Manager

This guide will walk you through using VMware Identity Manager to set up secure single sign on for your web app using the OpenID Connect protocol. This guide is light weight to get you started quickly. If you want a deeper understanding of OpenID Connect, please review the official spec at http://openid.net/specs/openid-connect-core-1_0.html.

An example application can be found here.

Use this authentication flow when you need to authenticate end-users in your application using VMware Identity Manager.

Authentication Flow Overview

In OpenID Connect authentication, Identity Manager performs authentication of your users and passes your app an ID Token that contains information about the user and the authentication. The flow follows these steps:

  1. You register your app as an OpenID Connect client in Identity Manager. You must only complete this step once for your app.

  2. The user tries to access your app.

  3. Your app checks whether the user already has a session cookie in their browser.

  4. If the user doesn't already have a session, your app sends the user to Identity Manager.

  5. Identity Manager authenticates the user according to the policies you set up.

  6. Identity Manager returns the user and an Authorization Code to your app.

  7. Your app sends that Authorization Code to Identity Manager over TLS.

  8. Identity Manager returns an ID Token, Authorization Token, and Refresh Token to your app.

  9. When the user's ID Token expires, your app uses the Refresh Token to request a new ID Token over TLS.

You can see the authentication flow in this sequence diagram:

Webapp Oauth2.0 Authentication Flow

Setting up OpenID Connect

Getting started

To get started with Identity Manager and OpenID Connect, you must have an Identity Manager tenant and a user with admin rights. If you don’t already have a tenant, you can get a free trial tenant at http://www.air-watch.com/lp/vmware-identity-manager-free-trial/ to test your app integration.

App Registration

The first step to using Identity Manager for single sign on is registering your app. Registering your app’s details lets Identity Manager know your app should be considered a trusted client for the OpenID Connect Service.

You have to register your app’s ID, app secret, and redirect URI with Identity Manager. You also have to choose what information your app requests from Identity Manager and the end user.

  1. To register your app, log in to the Identity Manager Admin Console as your tenant admin.

  2. Click the Catalog triangle dropdown.

  3. Click Settings. Settings

  4. Click Remote App Access.

    Remote App Access

  5. Click Create Client.

    Create Client

You should now see the Create Client dialog box. This dialog box allows you to enter your app’s Client ID, Redirect URI, and Shared Secret. It also lets you choose what Scopes (information types) your app requests from Identity Manager and the user during End User Authentication. See the screenshot below for an example of the dialog box:

  1. Access Type: Set it to "User Access Token". This indicates that your app will ask Identity Manager to authenticate users.

  2. Client ID: Enter the client identifier your app will use to authenticate itself to Identity Manager. Your Client ID must not match any Client ID already being used by your tenant. You can use the following characters: alphanumeric (A-Z, a-z, 0-9) period (.), underscore (_), and hyphen (-) and at sign (@).

    Example: Example_AppID

  3. Redirect URI: Enter the redirection endpoint Identity Manager will use for responses to your app’s requests. Your URI must use HTTPS (and TLS) for security. Your URI can include query parameters, but it should not include any URI fragments.

Security tip: Make sure that your app’s redirection endpoint cannot be used as an open redirector. The easiest way to do this is by not allowing redirects away from the endpoint you register with Identity Manager. If you must redirect users or responses away from your endpoint, make sure you only redirect them to validated URIs trusted by your app. Failing to validate redirect URIs lets attackers steal tokens or codes by redirecting them away from your app.

Example: https://example-app.com/redirect?auth=oauth

  1. Scope: Choose what kinds of information identity manager sends your app by default.

Security tip: Only select the scopes your app needs. End users may feel nervous about granting your app permission to see too much of their information; limiting your scopes also reduces the amount of user information transferred over the network.

The following scopes are available:

  • User: Always select this scope; it lets Identity Manager know your application needs access to user information.

  • OpenID: Always select this scope; it lets Identity Manager know you want an ID Token containing user information to be returned along with the Access Token.

  • Email: Select this scope if you need information about user email. The email attribute gives the user’s email, and the email_verified attribute tells your app whether the user’s email has been verified as belonging to the user.

  • Profile: Select this scope if you need the user’s name information. The family_name attribute gives you the user’s last name and the given_name attribute gives you his or her first name.

The following scopes are not used at this time:

  • Application: Not used at this time.
  • NAPPS: Not used at this time.
  1. Shared Secret: Click Advanced to generate the secret your app will use to authenticate with Identity Manager. We recommend clicking Generate Secret to have Identity Manager create a secure secret for your app; if you create your own secret, ensure it is difficult for attackers to guess.

    Example: Pckev3lu4IwfL4ljUOFe961TpDEtT8mVPnOSJNFbZc0Cggu

Click Add to register your app once you’ve entered your Client ID, Redirect URI, and Shared Secret. You’re now ready to begin using your app with Identity Manager’s Authorization Code flow.

OAuth2.0 Client

Authenticating Users

Once your app is registered with Identity Manager, it can ask Identity Manager to authenticate a user. Your app and Identity Manager follow the flow described in Authentication Flow Overview:

Webapp Oauth2.0 Authentication Flow

End User Authentication

When your app needs to authenticate a user, it should send the user to Identity Manager. Identity Manager will handle the authentication process completely independently of your app, eliminating the need for your app to contain any authentication logic.

To authenticate your user, redirect his or her browser to the /authorize endpoint:

https://[tenant location]/SAAS/auth/oauth2/authorize

where [tenant location] is the hostname of your tenant. Note that you must use HTTPS to in all your calls to Identity Manager.

The URL requires the following query parameters:

  • response_type=code. Required.

  • client_id=[your Client ID]. Required; your [Client ID] is the Client ID you entered in the App Registration step.

  • redirect_uri=[your redirect URI]. Required. [your redirect URI] must match the URI you entered in App Registration.

  • state=[your app state]. Recommended. This is a state token your app generates to ensure an attacker can’t send a fake response to your app (known as cross-site request forgery). The token should be around 30 characters long and generated using a random number generator.

  • scope=[your scopes]. Optional. The scopes of information that should be returned to your app. This parameter must contain the OpenID scope. Your client can request fewer scopes than you initially selected in App Registration, but not more. For example, you could initially select the scopes openid, user, profile, and email, but only request the scopes openid, user, and profile in your request. Identity Manager would only ask the user for permission to share the scopes you requested, and it would only return the scopes you requested to your app in the ID Token.

Example URL (with line breaks for readability):

https://exampletenant.vmwareidentity.com/SAAS/auth/oauth2/authorize?
response_type=code&
client_id=Example_AppID&
redirect_uri=https://example-app.com/redirect?auth%3Doauth&
state=6h3qG9XkjGlfvGIee1Up2e38Cn5H0&
scope=openid+user+email

Authentication Event

After your app sends your user’s browser to Identity Manager, Identity Manager authenticates the user. Identity Manager then sends the browser back to your app using the Redirect URI you entered in App Registration.

Authentication Response

Identity Manager’s response comes to your app’s Redirect URI. Your app receives a short-lived Authorization Code it can exchange for an Access Token and ID Token. If it passed Identity Manager a state token, it receives an identical state token in the response.

Identity Manager’s response follows this pattern:

[redirect URI]?code=[authorization code]&state=[your app’s state]

For example, if your used the example URL above, Identity Manager would return the following (with line breaks for readability):

https://example-app.com/redirect?auth=oauth&
code=3CVlTYl0JROh0ZSZEI5s0ii&
state=6h3qG9XkjGlfvGIee1Up2e38Cn5H0

The code parameter contains the Authorization Code your app must exchange for an Access Token in the Access Token Request step.

State Matching

Your app must match the state token it receives from Identity Manager with the one it sent in the Authentication Request. If the tokens do not match, an attacker may be impersonating Identity Manager.

Access Token Request

The Authorization Code your app received in the Authentication Response is very short lived; your app needs to exchange it for a longer term Access Token (and optionally a Refresh Token and ID Token). To exchange the code for an Access Token, your app should send a POST request to the following URL:

POST https://[tenant location]/SAAS/auth/oauthtoken

The URL requires the following parameters in the body of the request, using the application/x-www-form-urlencoded format:

  • grant_type=authorization_code. Required.

  • code=[your authorization code]. Required. This parameter contains the Authorization Code Identity Manager sent to your app in the Authentication Response.

  • redirect_uri=[your redirect URI]. Required. [your redirect URI] must match the URI you entered in App Registration.

The call also requires the following headers:

  • Authorization: Basic [base64Encode(clientId + ":" + secret)]. Required. Your app should use its Client ID and Client Secret to authenticate with Identity Manager using Basic Authentication. To create your Basic Credentials, create a string with your app’s Client ID and Client Secret, separated by a colon ( : ). Using the Client ID and Secret from the App Registration section, the string would be:

Example_AppID:Pckev3lu4IwfL4ljUOFe961TpDEtT8mVPnOSJNFbZc0Cggu

After the string is Base64 encoded, the Authorization Header is as follows:

Authorization: Basic RXhhbXBsZV9BcHBJRDpQY2tldjNsdTRJd2ZMNGxqVU9GZTk2MVRwREV0VDhtVlBuT1NKTkZiWmMwQ2dndQ==

  • Content-Type: application/x-www-form-urlencoded

Example request (with line breaks for readability):

POST https://exampletenant.vmwareidentity.com/SAAS/auth/oauthtoken

Body:

grant_type=authorization_code
&code=3CVlTYl0JROh0ZSZEI5s0ii
&redirect_uri=https://example-app.com/redirect?auth%3Doauth

Headers:

Authorization: Basic RXhhbXBsZV9BcHBJRDpQY2tldjNsdTRJd2ZMNGxqVU9GZTk2MVRwREV0VDhtVlBuT1NKTkZiWmMwQ2dndQ==
Content-Type: application/x-www-form-urlencoded

Token Response

Identity Manager validates your app’s Authorization Code and returns an ID Token and an Access Token. The ID Token describes the user (and the Authentication Event in which the user authenticated with Identity Manager). The Access Token gives your app permission to access APIs on behalf of a user; if you are only using Identity Manager to authenticate the user, your app can ignore the Access Token.

Identity Manager also sends your app a Refresh Token if you left the Refresh Token box checked in App Registration. The Refresh Token can be used to renew your app’s permission to access APIs on behalf of the user when your Access Token expires.

Identity Manager response is a JSON object with the following attributes:

  • access_token. This attribute contains an Access Token in JWT format. Your app can use this token to access Identity Manager APIs on behalf of the user.

  • token_type. This attribute tells your application what type of Access Token it was given. At this time, Identity Manager tokens are always bearer tokens.

  • expires_in. The Access Token will expire in the number of seconds given in this attribute. If the Access Token expires, your app will have to use the Refresh Token to request a new Access Token or begin the Authenticating Users flow again.

  • refresh_token. This attribute contains the Refresh Token your app can exchange for a new Access Token.

  • scope. This attribute lists the scopes of information that the ID Token contains.

  • id_token. This attribute contains an ID Token in JWT format. The ID Token describes the user and the Authentication Event in which the user authenticated with Identity Manager.

Example JSON response:

{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI4NTdjNWM3MS1kNTZjLTRiMDMtOTIxYy0yMThiMDMxNGViMWIiLCJwcm4iOiJkZXZhZG1pbkBGU1RSVURFTC1TVkEiLCJkb21haW4iOiJocy50cmNpbnQuY29tIiwidXNlcl9pZCI6IjgiLCJhdXRoX3RpbWUiOjE0NzU1NDE0ODYsImlzcyI6Imh0dHBzOi8vZnN0cnVkZWwtc3ZhLmhzLnRyY2ludC5jb20vU0FBUy9hdXRoIiwiYXVkIjoiaHR0cHM6Ly9mc3RydWRlbC1zdmEuaHMudHJjaW50LmNvbS9TQUFTL2F1dGgvb2F1dGh0b2tlbiIsImN0eCI6Ilt7XCJtdGRcIjpcInVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0XCIsXCJpYXRcIjoxNDc1NTMxNTkwLFwiaWRcIjo1fV0iLCJzY3AiOiJvcGVuaWQgdXNlciBlbWFpbCIsImlkcCI6IjAiLCJlbWwiOiJkZXZhZG1pbkB3c2FpcmxhYnMuY29tIiwiY2lkIjoiRXhhbXBsZV9BcHBJRCIsImRpZCI6IiIsIndpZCI6IiIsImV4cCI6MTQ3NTU2MzA4NiwiaWF0IjoxNDc1NTQxNDg2LCJzdWIiOiJmYjk1YzE5Mi1lYWMxLTQ5ZmEtYWQzZi0wMTUyZjhlYTJjMzUiLCJwcm5fdHlwZSI6IlVTRVIifQ.s0ZUO3dArAIjbN--blvXSlWNeHqxTmbKXwDYrvE6O5ro7WqrQJjWKVkY_pR9pAZ244Iwtr7bylt-GhK7SzSnKc8OdplgMEXiLk5N1FwE587pQdEZuSvCSRF3rkisFx-a5_YsD9tyhNC-cj6b6vjtuWE_Bnbbd0CuBSNzzsQdI_U",
"token_type": "Bearer",
"expires_in": 21599,
"refresh_token": "MBVaMXu8uotfW5eTWspOEWU",
"scope": "openid user email",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJlbWFpbCI6ImRldmFkbWluQHdzYWlybGFicy5jb20iLCJ1cGRhdGVkX2F0IjowLCJleHAiOjE0NzU1NDE2MDYsImlhdCI6MTQ3NTU0MTQ4Niwic3ViIjoiZGV2YWRtaW5ARlNUUlVERUwtU1ZBIiwiaXNzIjoiaHR0cHM6Ly9mc3RydWRlbC1zdmEuaHMudHJjaW50LmNvbS9TQUFTL2F1dGgiLCJhdWQiOlsiRXhhbXBsZV9BcHBJRCJdLCJhdXRoX3RpbWUiOjE0NzU1NDE0ODYsImF6cCI6IkV4YW1wbGVfQXBwSUQiLCJhdF9oYXNoIjoid1FtSnBPSTU1V2ctOFF5bkhwejF6QSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlfQ.oqia_8mHGK1QsmhuKoNCFU-KSPRoDMVYCfS0VuIQUfupY0AVnAdFOcuC1NUBZWEOyPF1ntuntVTsyae9CnF70vFwSceUtWnoV4k9QJ7JcJ0lfzeY3DZ1XwGI5b4zFHoDWevaShk6hg3x93JhruD5pvkO45Xq6EBDaqQ7M7YgkCY"
}

The access_token is a JWT token, like the id_token.

Using the ID Token

The ID Token is formatted using the JWT standard. You can decode the token using base64 decoding to reveal claims about the user and Authentication Event.

The payload of the ID Token contains claims about the user. Identity Manager ID Tokens can contain the following attributes:

  • email: Provided if the email scope was checked in App Registration. This attribute contains the user’s email address.

  • updatedAt: This attribute tells your application the last time the user’s account was updated in Identity Manager. Given in Unix time.

  • exp: The time at which the Access Token will expire. Given in Unix time.

  • iat: The time at which the ID Token was issued. Given in Unix time.

  • sub: The user’s unique identifier in Identity Manager. Contains a user identifier and an Identity Manager tenant identifier, divided by an at sign ( @ ). For example, the unique identifier user@EXAMPLETENANT identifies a user known as “user” and an Identity Manager instance known as “EXAMPLETENANT”.

  • iss: The Identity Manager endpoint that issued the ID Token. Your app should verify that this matches the URL used in the Access Token Request.

  • aud: The audience the ID Token was issued for; your app must verify that the value in aud matches its Client ID. In this example it will be Example_AppID.

  • auth_time: The time at which the ID token was issued. Given in Unix time.

  • azp: The party the token was given to. This value should match your app’s Client ID.

  • at_hash: The base64url encoding of the left-most half of the hash of the octets of the ASCII representation of the Access Token value. Identity Manager uses the RS256 hash algorithm. Your app must verify that the at_hash value matches the hashed value of the access token. To verify the match, hash the access_token value with SHA-256, then take the left-most 128 bits and base64url encode them. The at_hash value is a case sensitive string.

  • given_name: Provided if the profile scope was checked in App Registration. The user’s first name.

  • family_name: Provided if the profile scope was checked in App Registration. The user’s last name.

  • email_verified: Provided if the email scope was checked in App Registration. This value is true if someone has taken action to confirm the email address and if it is believed that the email belongs to the subject of the ID token.

Example ID Token payload:

{
"email": "<devadmin@wsairlabs.com>",
"updated_at": 0,
"exp": 1475541606,
"iat": 1475541486,
"sub": "devadmin@FSTRUDEL-SVA",
"iss": "<https://fstrudel-sva.hs.trcint.com/SAAS/auth>",
"aud": [
  "Example_AppID"
],
"auth_time": 1475541486,
"azp": "Example_AppID",
"at_hash": "wQmJpOI55Wg-8QynHpz1zA",
"email_verified": true

}

Refreshing your Tokens

The Access and ID Tokens have limited lifetimes. Your app will get an error if it tries to access resources using an expired token. If your Tokens expire, your app should request a new Access and ID Tokens using the Refresh Token.

The application needs to send the following request to the token endpoint:

POST https://[tenant location]/SAAS/auth/oauthtoken

The URL requires the following parameters in the body of the request, using the application/x-www-form-urlencoded format:

  • grant_type=refresh_token. Required.

  • refresh_token=[refresh token]. Required. The Refresh Token returned by Identity Manager during the first Access Token Request.

  • scope. Optional. The list of scopes separated by space and URL encoded.

The request also requires the following headers:

  • Authorization: Basic [base64Encode(clientId + ":" + secret)]. Required. Your app should use its Client ID and Client Secret to authenticate with Identity Manager using Basic Authentication. To create your Basic Credentials, create a string with your app’s Client ID and Client Secret, separated by a colon ( : ). Using the Client ID and Secret from the App Registration section, the string would be:

Example_AppID:Pckev3lu4IwfL4ljUOFe961TpDEtT8mVPnOSJNFbZc0Cggu

After the string is Base64 encoded, the Authorization Header is as follows:

Authorization: Basic RXhhbXBsZV9BcHBJRDpQY2tldjNsdTRJd2ZMNGxqVU9GZTk2MVRwREV0VDhtVlBuT1NKTkZiWmMwQ2dndQ==
  • Content-Type: application/x-www-form-urlencoded

Example request (with line breaks for readability):

POST https//exampletenant.vmwareidentity.com/SAAS/auth/oauthtoken

Body:

grant_type=refresh_token&
refresh_token=MBVaMXu8uotfW5eTWspOEWU

Headers:

 Authorization: Basic RXhhbXBsZV9BcHBJRDpQY2tldjNsdTRJd2ZMNGxqVU9GZTk2MVRwREV0VDhtVlBuT1NKTkZiWmMwQ2dndQ==
 Content-Type: application/x-www-form-urlencoded

Identity Manager will respond with a new Access Token.

Example response:

{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiJkYzllMzQ0MC04MzdiLTRmYTQtYTNmNC03NWJmZGIzYTY0OWIiLCJwcm4iOiJkZXZhZG1pbkBGU1RSVURFTC1TVkEiLCJkb21haW4iOiJocy50cmNpbnQuY29tIiwidXNlcl9pZCI6IjgiLCJhdXRoX3RpbWUiOjE0NzU1OTY5MzMsImlzcyI6Imh0dHBzOi8vZnN0cnVkZWwtc3ZhLmhzLnRyY2ludC5jb20vU0FBUy9hdXRoIiwiYXVkIjoiaHR0cHM6Ly9mc3RydWRlbC1zdmEuaHMudHJjaW50LmNvbS9TQUFTL2F1dGgvb2F1dGh0b2tlbiIsImN0eCI6Ilt7XCJtdGRcIjpcInVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0XCIsXCJpYXRcIjoxNDc1NTMxNTkwLFwiaWRcIjo1fV0iLCJzY3AiOiJvcGVuaWQgdXNlciBlbWFpbCIsImlkcCI6IjAiLCJlbWwiOiJkZXZhZG1pbkB3c2FpcmxhYnMuY29tIiwiY2lkIjoiRXhhbXBsZV9BcHBJRCIsImRpZCI6IiIsIndpZCI6IiIsImV4cCI6MTQ3NTYxODUzMywiaWF0IjoxNDc1NTk2OTMzLCJzdWIiOiJmYjk1YzE5Mi1lYWMxLTQ5ZmEtYWQzZi0wMTUyZjhlYTJjMzUiLCJwcm5fdHlwZSI6IlVTRVIifQ.UZRTuIZaafxSVUwd2wWfYVPqC0UTIzfi3GHiT03IVj1BcVOnrITWogrcbPI5c7ztbOJA0-mIUbsIBTQV0ozxjnDX9R8GJ85BBR5XlJTM2QUOvBN5mKJ9WaN3NzRBDW1yMwzn2H4bGZUwdnGr-Y8iAJiNVE3zVMz7NH82ZVAMDwA",
"token_type": "Bearer",
"expires_in": 21599,
"refresh_token": "MBVaMXu8uotfW5eTWspOEWU",
"scope": "openid user email"
}