This is part two of a tutorial blog series from Ben Finkel addressing the challenges, solutions, and implementation of sound authentication. By the end of this series, you will be confident in your ability to implement an authentication system — even with little-to-no background.
In my previous post I gave an abstract overview of the OAuth 2.0 “dance.” Let’s dive deeper into the process and understand every step.
But first, vocabulary! Sorry, but it’s important. I’ll be using these terms liberally throughout this series. Better learn them now.
- Consumer — Also known as a client or Relying Party (RP), the consumer is an application that wishes to access some resource protected by a different app. It relies upon that app for authentication and authorization (hence the “relying” party moniker). For the purposes of this tutorial, we’re building a consumer app.
- Provider — Also known as an Identity Provider (IDP), the provider is the application in control of the protected resources and the user’s authentication information. Providers include Google, Microsoft, Twitter, etc.
- User — Also known as the Resource Owner, the user is using the consumer app and granting it permission to use the resources it owns that are controlled by the provider. In this tutorial, we will often be the user ourselves for testing purposes but we’ll have to imagine anyone out in the wide world of the internet might be using our app and their own resources.
- Resource — The data that is protected (controlled) by the provider and owned by the user. This may be Google Calendar items, or a Twitter feed, or a Facebook friend list, just to name a few examples.
OAuth 2.0 In Depth
Here’s the step-by-step I already provided for OAuth 2.0.
- The consumer application informs the user that it would like access to their protected resource (their Twitter feed, for example).
- The user is redirected to a login page for the application that protects that resource for them (this app is known as the provider).
- After logging in, the provider presents the user with the name of the consumer app and the permissions it is requesting.
- The user can allow or deny this access by pressing the appropriate button.
- If the user allows the access to continue, the consumer and the provider exchange information in the background.
- The consumer then uses that information on a go-forward basis to perform whatever tasks it requires.
Let’s break it down into discrete details.
1. This step is not actually required by any rule. Informing the user about what you’re going to ask for is a nice convention as it helps them understand what’s going on and also acts as a validation point when step 3 occurs. If the provider lists different permissions, then the app warns the user about it. It’s a hint that maybe something is wrong.
2. The app opens a browser (or redirects, if it’s already running in a browser) to the specified URL endpoint for authorization. This is the first piece of information that’s going to change from provider to provider. For instance, the OAuth 2.0 endpoint for Google is https://accounts.google.com/o/oauth2/v2/auth while the endpoint for Facebook is https://www.facebook.com/dialog/oauth. Along with this request the consumer needs to provide a couple of pieces of information, typically in the querystring.
- a. client_id — This is a unique identifier for the consumer app. It has been created ahead of time using some interface the provider supplied (i.e. https://apps.twitter.com). This is how the provider knows what app is generating the request and can subsequently display the name of that app during step 3.
- b. redirect_uri — This is a URI that the provider will redirect to once it’s complete. Regardless of whether the user succeeds in logging in, regardless of whether the user allows or denies the access request, this is how control is turned back over to the consumer app.
- c. scope — Essentially, the scope is the list of permissions or access being requested from the provider. The options for scope will be completely unique for any given provider. We’ll cover scope in greater detail when we write our code since it is so provider-dependent.
Step 2 is where the user logs into the provider to first establish their identity. Often times you won’t notice this step occurring. Any idea why? Because we’re usually already logged into the site in question. My desktop, laptop, tablet, and phone are already signed into my Google account. When a consumer initiates the OAuth flow, I don’t have to re-login because Google recognizes me as already signed in. This process still happens, but it’s just in the background and may not be noticed. One good way to ensure you “see” this happen during testing is to run in an incognito browser window.
3. The presentation of the authorization page is where the rubber meets the road from the user’s perspective. This is handled completely by the provider app, although it’s worth noting some providers allow app creators to customize this page somewhat when they register their app to receive a client_id. On this page the provider echoes the scope(s) requested back to the user in a verbose form that’s easy for a person to understand.
4. The user has the choice to allow or deny. Either way, after a choice is made the user is redirected back to the redirect_uri that was supplied in step 2. This represents the only step that requires user involvement (assuming we don’t count step 1). Everything else happens between the consumer and the provider in the background.
5. The browser is redirected to the redirect_uri with a variety of parameters depending on what was chosen in step 4 and how the provider chooses to implement the OAuth 2.0 rules. If the user chose to allow the access, then the payload of the HTTP body will definitely include:
- a. code — A string representing an “authorization token.” This string is generated according to the whims and wishes of the provider; the OAuth spec defines no rules about this string other than the fact that it should expire within 10 minutes to mitigate the damage from a leaked or stolen code.
At this point the consumer has another behind-the-scenes exchange to perform: exchange the authorization token for an access token.
The consumer calls a URI known as the “token” uri. In the HTTP payload for this uri, the consumer supplies its client_id and redirect_uri along with the authorization token it just received. If the expiration of the authorization token has been reached, this request is denied. If this auth token was already exchanged for an access token, the request is denied. If everything is on the up-and-up however, the provider returns an HTTP response (200) and supplies the following information in the response body:
- a. access_token — This is another string of characters that represents the consumer’s access to the requested resources. Similar to the authentication token, it has no rules.
- b. expires_in — This is the number of seconds until the access_token expires. It is completely up to the provider to set the value, although 3600 (1 hour) is typical. Once that time has been reached, the access token is no longer valid.
- c. refresh_token — This is a string token just like above except it does not expire. Once the access_token has expired, the consumer app can use this token in a request for a new access token (with a reset expiration timer). This is an optional feature; the provider could simply force the user to reauthorize the consumer app every time the access token expires instead of supplying a refresh token.
6. Every request that the consumer makes of the provider app in the future will need to supply the access token acquired during Step 5. It is up to the provider to determine at the time of each request whether or not the access token is still valid. It may have expired or it may have been revoked by the resource owner, and in those cases the provider should respond with an appropriate error to the consumer app so that it can resolve the problem as gracefully as possible.
That’s a lot of details, don’t be alarmed if it’s overwhelming. In the next installment we’ll actually go through the OAuth dance using a neat tool from Google called the “OAuth 2.0 Playground.” This will let us walk through each step and see the details in actual use.
You can read the full tutorial series in these weekly installments:
Part 1: Authentication for the Modern Web
Part 2: Delegating Authentication
Part 3: Let’s Play on the OAuth 2.0 Playground
Part 4: Let’s Start Running Code
Part 5: Authorization Grant, Our First OAuth Dance Steps
Part 6: Authorization Request, An OAuth “State” of Mind
Part 7: Authorization Grant, Final OAuth Dance Steps
Part 8: Expanding Our Service with GitHub
Part 9: OAuth 2.0: Authorization, Not Authentication
The series will conclude in April 2016.
P.S. – Not a subscriber? Start your free week.