Chapter 7. OpenID Connect Authentication

Nearly every web application prompts users to create an account and log in. In order to create an account, users are asked to provide their name, their email address, a password, and password confirmation. Not only does this take a lot of effort for the user (50+ keystrokes), but it also creates security concerns, as users often create the same password on multiple sites and some sites do not properly secure these credentials.

OpenID exists to enable federated identity, where users are able to authenticate with the same identity across multiple web applications. Both users and web applications trust identity providers, such as Google, Yahoo!, and Facebook, to store user profile information and authenticate users on behalf of the application. This eliminates the need for each web application to build its own custom authentication system, and it makes it much easier and faster for users to sign up and sign into sites around the Web.

OpenID Connect is the next-generation version of OpenID. The development of OpenID Connect has taken into account two key concepts:

  • Passing permission to access authentication information (the user’s identity) to a site is very similar to passing along delegated access to a user’s data (such as their calendar). Developers shouldn’t have to use entirely different protocols for these two different use cases—especially because many developers need to handle both in their applications.

  • The specification should be modular—enabling spec compliance without requiring implementation of automated discovery, associations, and other complex bits included in the previous versions of OpenID.

The basic flow for OpenID Connect is:

  1. The application requests OAuth 2.0 authorization for one or more of the OpenID Connect scopes (openid, profile, email, address) by redirecting the user to an identity provider.

  2. After the user approves the OAuth authorization request, the user’s web browser is redirected back to the application using a traditional OAuth flow. The app makes a request to the Check ID Endpoint. This endpoint returns the user’s identity (user_id) as well as other bits, such as the aud and state, which must be verified by the client to ensure valid authentication.

  3. If the client requires additional profile information about the user, such as the user’s full name, picture, and email address, the client can make requests to the UserInfo Endpoint.

Because OpenID Connect is built on top of OAuth 2.0 and is designed as a modular specification, it’s much easier for you to implement federated authentication for your website in a compliant way. Since this is a Getting Started book, this chapter will primarily discuss the OpenID Connect Basic Client implementation.

ID Token

With OpenID Connect authentication, there is an additional type of OAuth token: an ID token. The ID token, or id_token, represents the identity of the user being authenticated. This is a separate token from the access token, which is used to retrieve the user’s profile information or other user data requested during the same authorization flow.

The ID token is a JSON Web Token (JWT), which is a digitally signed and/or encrypted representation of the user’s identity asserted by the identity provider. Instead of using cryptographic operations to validate the JSON Web Token, it can be treated as an opaque string and passed to the Check ID Endpoint for interpretation (see below). This flexibility keeps with the spirit of OAuth 2.0 and OpenID Connect being significantly easier to use than their predecessors.

Security Properties

Although the end user flow is quite similar, the security precautions necessary for authentication are much different than those for authorization because of the potential for replay attacks. Replay attacks occur when legitimate credentials are sent multiple times for malicious purposes.

There are two main types of replay attacks we wish to prevent:

  • An attacker capturing a user’s OAuth credentials as they log in to a site and using them later on the same site.

  • A rogue application developer using the OAuth token a user was issued to log in to their malicious app in order to impersonate the user on a different legitimate app.

The OAuth 2.0 specification requires the OAuth endpoint and APIs to be accessed over SSL/TLS to prevent man-in-the-middle attacks, such as the first case.

Preventing rogue application developers from replaying legitimate OAuth credentials their app received in order to impersonate one of their users on another app requires a solution specific to OpenID Connect. This solution is the Check ID Endpoint. The Check ID Endpoint is used to verify that the credentials issued by the OAuth provider were issued to the correct application.

It is recommended that all developers use the Check ID Endpoint or decode the JSON Web Token to verify the asserted identity, though this is not strictly necessary in some cases when the application uses the server-side Web Application flow and the UserInfo Endpoint provides all required information.

The server-side Web Application flow, when implemented as per the specification, only issues an authorization code through the user’s web browser. The web application should not ever accept an access token or identity token directly from the browser. The access token and identity token are retrieved by exchanging the authorization code in a server-to-server request. Since this exchange requires the server-to-server call to be authenticated with the client ID and client secret of the app which the authorization code was issued for, the OAuth token service will naturally prevent an app from accidentally using an authorization code issued to another app.

Alternatively, the client-side Web Application flow issues an access token and identity token directly to the app through the browser using a hash fragment. The access token and identity token are often sent to the backend web server using JavaScript in order to authenticate the user. In this case, the web server must either cryptographically verify the ID Token or call the Check ID endpoint to verify it was issued to the correct application. This is called “verifying the audience” of the token. See Check ID Endpoint for more information.

Obtaining User Authorization

The process of obtaining user authorization for OpenID Connect is nearly identical to the process of obtaining authorization for any OAuth 2.0 enabled API. You can use either the client-side implicit flow (as described in Chapter 3) or the server-side web app flow (as described in Chapter 2).

As with any usage of these flows, the client generates a URL pointing at the OAuth Authorization Endpoint and redirects the user to that URL. The following parameters are passed:

client_id

The value provided to you when you registered your application.

redirect_uri

The location the user should be returned to after they approve the authentication request.

scope

openid for a basic OpenID Connect request. If your client needs access to additional profile information for the user, additional scopes can be profiled in this space-delimited string: profile, email, address.

response_type

id_token to indicate that an id_token is required for the application. Additionally, a response type of token or code must be included, separating the two response types by a space. token indicates the client-side Web Application flow, while code indicates the server-side Web Application flow.

nonce

A unique value used by your application to protect against replay and cross-site request forgery (CSRF) attacks on your implementation. The value should be a random unique string for this particular request, unguessable and kept secret in the client (perhaps in a server-side session). This identical value will be included in the ID token response (see below).

The following is an example of a complete Authorization Endpoint URL, using the client-side implicit flow:

https://accounts.example.com/oauth2/auth?
  scope=openid+email&
  nonce=53f2495d7b435ac571&
  redirect_uri=https%3A%2F%2Foauth2demo.appspot.com%2Foauthcallback&
  response_type=id_token+token&
  client_id=753560681145-2ik2j3snsvbs80ijdi8.apps.googleusercontent.com

After the user approves the authentication request, they will be redirected back to the redirect_uri. Since this request uses the implicit flow, the redirect will include an access token that can be used with the UserInfo Endpoint to obtain profile information about the user. Additionally, and specific to OpenID Connect, the redirect will also include an id_token, which can be sent to the Check ID Endpoint to get the user’s identity.

Here’s an example redirect:

https://oauth2demo.appspot.com/oauthcallback#
  access_token=ya29.AHES6ZSzX
  token_type=Bearer&
  expires_in=3600&
  id_token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY...

The client then needs to parse the appropriate parameters from the hash fragment in the URL and call the Check ID Endpoint to validate the response.

Check ID Endpoint

The Check ID Endpoint exists to validate the id_token returned along with the OAuth 2.0 access_token by ensuring that it was intended for the correct client and is used by the client to begin an authenticated session. As described above, this check is required for the implicit flow for client-side applications (described in Chapter 3). If this check isn’t done correctly, the client becomes vulnerable to replay attacks.

Here’s an example Check ID endpoint request:

https://accounts.example.com/oauth2/tokeninfo?
  id_token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY...

And the response:

{
  "iss" : "https://accounts.example.com",
  "user_id" : "113487456102835830811",
  "aud" : "753560681145-2ik2j3snsvbs80ijdi8.apps.googleusercontent.com",
  "exp" : 1311281970
  "nonce" : 53f2495d7b435ac571
}

If the response is returned without a standard OAuth 2.0 error, the following checks need to be performed:

  • Verify the aud value in the response is identical to the client_id used in the Authorization request.

  • Verify that the nonce value in the response matches the value used in the Authorization request.

If this verification is completed successfully, the user_id is known to represent the unique identifier for the authenticated user, within the scope of the issuer (iss). If storing the identifier in a user database table and multiple identity providers are supported by your application, it is recommended that both values be stored upon account creation and queried upon each subsequent authentication request.

UserInfo Endpoint

While the Check ID Endpoint will return a unique identifier for the user authenticating to your application, many applications require additional information, such as the user’s name, email address, profile photo, or birthdate. This profile information can be returned by the UserInfo Endpoint.

The UserInfo Endpoint is a standard OAuth-authorized REST API, with JSON responses. As when accessing any other API using OAuth, the access_token can be passed either as an Authorization header or as a URL query parameter.

Here’s an example UserInfo request:

GET /v1/userinfo HTTP/1.1
Host: accounts.example.com
Authorization: Bearer ya29.AHES6ZSzX

With the response:

{
 "user_id": "3191142839810811",
 "name": "Example User",
 "given_name": "Example",
 "family_name": "User",
 "email": "[email protected]",
 "verified": true,
 "profile": "http://profiles.example.com/user",
 "picture": "https://photos.profiles.example.com/user/photo.jpg",
 "gender": "female",
 "birthday": "1982-02-11",
 "locale": "en-US"
}

OpenID Connect does not define any specific profile fields as required and does allow for additional profile fields to be included in the response.

Performance Improvements

The objective of the call to the Check ID Endpoint is to verify the legitimacy of the id_token. However, this requires an additional HTTP request to the OpenID Connect identity provider. This additional request can be avoided since the id_token is returned as a signed JSON Web Token (JWT) instead of as an opaque blob. The JWT includes the same information that is typically returned by the Check ID Endpoint, but the value is also cryptographically signed by the server in a way that can be validated by the client.

This gives the client the option to verify the signature using the JWT (for best performance) or simply call the Check ID Endpoint if the client wants to avoid cryptography.

Practical OpenID Connect

Since the OpenID Connect specification is still under active development, experimental implementations by identity providers still differ from the specification. Here are some example requests and responses using these experimental implementations.

For Google

Google’s OpenID Connect implementation (see Figure 7-1) uses the following Endpoints:

Check ID

https://www.googleapis.com/oauth2/v1/tokeninfo

UserInfo

https://www.googleapis.com/oauth2/v1/userinfo

Google does not have the generic openid scope, but it supports the following main scopes for its OpenID Connect implementation:

Email

https://www.googleapis.com/auth/userinfo.email

Profile

https://www.googleapis.com/auth/userinfo.profile

Here’s an example authorization URL for Google’s OpenID Connect implementation:

https://accounts.google.com/o/oauth2/auth?
  scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&
  state=ABC123456&
  redirect_uri=https%3A%2F%2Foauthssodemo.appspot.com%2Foauthcallback&
  response_type=token%20id_token&
  client_id=8819981768.apps.googleusercontent.com
Google asking if it’s OK to share info with example app “OAuth SSO Relying Party”
Figure 7-1. Google asking if it’s OK to share info with example app “OAuth SSO Relying Party”

In this example, we’re specifying a response_type of token id_token, indicating that we’re looking for both an ID token and a traditional OAuth 2.0 access token (via the implicit flow). After the user approves the request by clicking “Allow Access,” Google redirects back to the redirect_uri and includes an id_token and an access_token in the hash fragment of the URL. The id_token is a JSON Web Token (JWT) and contains the user’s ID. This ID token can be validated by comparing a cryptographic signature or the Check ID Endpoint can be called. For simplicity, we’ll show how to call the Check ID Endpoint. Here’s an example request:

https://www.googleapis.com/oauth2/v1/tokeninfo?
  id_token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY...

Here’s the response:

{
  "issued_to" : "8819981768.apps.googleusercontent.com",
  "user_id" : "113487456102835830811",
  "audience" : "8819981768.apps.googleusercontent.com",
  "expires_in" : 3465
}

After the Check ID response is properly validated by ensuring it’s been issued for the correct application (by comparing the value of the issued_to parameter to the app’s client ID), the app may wish to obtain additional profile information about the user. This information, such as the user’s name or email address, can be obtained as a JSON response from the UserInfo Endpoint. The OAuth access_token must be sent to authorize the request. Here’s an example request:

GET /oauth2/v1/userinfo HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer ya29.AHES6ZSzX

Here’s the response:

{
 "id": "110634877589748180443",
 "email": "[email protected]",
 "verified_email": true,
 "name": "Ryan Boyd",
 "given_name": "Ryan",
 "family_name": "Boyd",
 "link": "http://profiles.google.com/110634877589748180443",
 "picture": "https://lh6.googleusercontent.com/-XC1Cwt4OgfY/AAAAAAAAAAI/AAAAAAAACR8/SU9W99JQFvc/photo.jpg",
 "gender": "male",
 "birthday": "0000-10-05",
 "locale": "en-US"
}

You’ll notice that the response indicates my birth year as 0000. I’m not that old; Google uses this special value to indicate that the birth year is not shared.

For Facebook

Facebook’s implementation of identity using OAuth 2.0 isn’t documented as being OpenID Connect. However, it works similarly to the specification, with a few minor differences to account for in client code.

Facebook uses the following Endpoint:

UserInfo

https://graph.facebook.com/me

Facebook does not provide Check ID Endpoint functionality, and for this reason I recommend using only the Authorization Code flow for server-side applications (described in Chapter 2) and not the implicit flow for client-side applications. If you use the client-side Web Application flow, you’ll have no ability to verify the access token was intended for use by your application, and thus can leave your app vulnerable to replay attacks.

Here we can see an example authorization URL for Facebook’s OpenID Connect implementation:

https://www.facebook.com/dialog/oauth?
  client_id=202627763128396&
  redirect_uri=https%3A%2F%2Foauth2demo.appspot.com%2Foauthcallback&
  state=ABC123456

Since a scope is not specified, Facebook defaults to requesting authorization for public profile information. Additional information can be requested by specifying scope values such as email,read_stream. Notice that Facebook uses comma-delimited scope values instead of space-delimited values as defined by the latest OAuth 2.0 specification.

Since a response_type is not specified, Facebook defaults to the Authorization Code flow for server-side web applications. If you wish to use the implicit flow for client-side web applications, specify a response_type=token, though this is not recommended.

As is typical with the Authorization Code flow for server-side web applications described in Chapter 2, the user’s browser will be redirected to the application’s redirect_uri after the user approves access. The redirect URL will include an authorization code in the code query parameter. The application then needs to exchange the authorization code for an access token by making a request to the Token Endpoint.

While the authorization code exchange typically uses a HTTP POST, Facebook also supports using a HTTP GET:

https://graph.facebook.com/oauth/access_token?
     client_id=202627763128396&
     redirect_uri=https%3A%2F%2Foauth2demo.appspot.com%2Foauthcallback&
     client_secret=YOUR_APP_SECRET&code=123456

Since we’re using the Authorization Code flow for server-side web applications, there is no need to do a Check ID request. This is because the Authorization Code flow requires the application’s credentials to be sent securely to the server when exchanging an authorization code for an access token, resulting in an automatic check that the authorization code was issued to the current client. However, the application must keep the access token confidential on the server and prevent trusting any access token directly sent by the user, or the application could be vulnerable to the same type of replay attack that the Check ID endpoint was designed to prevent.

At this point, the application can obtain profile information for the user via the me endpoint of the Graph API. Here’s an example request:

https://graph.facebook.com/me?
  access_token=123456abc123456abc

Here’s the response:

{
   "id":"545296355",
   "name":"Ryan Boyd",
   "first_name":"Ryan",
   "last_name":"Boyd",
   "link":"http://www.facebook.com/rboyd",
   "username":"rboyd",
   "hometown":{
      "id":"114952118516947",
      "name":"San Francisco, California"
   },
   "location":{
      "id":"114952118516947",
      "name":"San Francisco, California"
   },
   "gender":"male",
   "email":"ryanu0040ryguy.com",
   "timezone":-8,
   "locale":"en_US",
   "verified":true,
   "updated_time":"2011-06-03T18:37:40+0000"
}

OpenID Connect Evolution

The protocol is likely to change after receiving feedback from both identity providers and relying parties. Information on the current Developer Preview can be found on the OpenID Foundation site, including the detailed specifications and mailing lists to follow development of the specifications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.27.234