Skip to main content
ANVISoftware Solutions
Lesson 4 of 14Intermediate20 min

OAuth and OpenID Connect

By the end of this lesson

Explain the flows and delegate authentication safely.

The orders application needs to read an employee's calendar from a third-party scheduling service, so it can suggest delivery slots the employee is free to supervise.

One way to do that is to ask the employee for their scheduling-service password and log in as them. Consider what you have taken on. You now hold a working credential for a system you do not own. You can do everything that employee can do, not the one thing you need. The scheduling service cannot tell your requests apart from theirs. And the only way for the employee to withdraw your access is to change the password, which breaks everything else using it.

OAuth 2.0 exists to remove all of that. The employee approves a specific, limited access at the scheduling service itself, and your application receives a token representing that grant. The password never comes near you. That is the central idea, and everything else in this lesson is mechanism in service of it.

OpenID Connect is a thin layer on top, added because people kept trying to use OAuth for something it was not built for. OAuth answers 'may this application access that data'. OpenID Connect answers 'who is the person who just signed in'. They are different questions and they have different answers.

The parties and the artefacts. These names appear in every OAuth document you will read, so they are worth learning once:

Resource owner
The person who owns the data and can grant access to it. The employee, in our example.
Client
The application requesting access — your orders app. 'Client' always means the application, never the person, which is a frequent early misreading.
Authorization server
The service that authenticates the resource owner and issues tokens. It owns the login page, and it is the only place the password is typed.
Resource server
The API holding the data and accepting the token. The scheduling service's calendar API.
Access token
Proof of a granted access, sent to the resource server on each call. Short-lived and bounded by the scopes it was issued for. Your application does not need to understand its contents.
Scope
The named slice of access being requested, such as reading calendar events. Ask for the narrowest scope that does the job: a consent screen requesting broad access to show one feature makes people hesitate, and they are right to.
ID token
An OpenID Connect addition. A signed statement about who signed in, addressed to your application for it to read. It is not a credential for calling APIs.
Confidential and public clients
A confidential client runs somewhere a secret can be kept, such as your server. A public client — a single-page app, a mobile app, a desktop app — is shipped to the user's device, so anything embedded in it can be read out of it. The distinction decides which protections apply.

Keeping the two straight saves a great deal of confusion later:

 OAuth 2.0OpenID Connect
Question it answersMay this application access that data?Who is the person who just signed in?
What it isA delegated authorization frameworkAn authentication layer built on top of OAuth 2.0
Main artefactAn access token, for calling an APIAn ID token, for your application to read
Who consumes the tokenThe resource serverThe client application
Reach for it whenYou need limited access to an API on someone's behalfYou want users to sign in with an existing account, including single sign-on
Getting it wrong looks likeSending an ID token to an API as a credentialTreating the mere existence of an access token as proof of who the user is

The authorization code flow with PKCE, which is the current recommendation for browser, mobile and server-side applications alike. PKCE stands for proof key for code exchange, and it ties the final token request back to the application that started the flow.

  1. The client creates a one-time secret

    Your application generates a large random value called the code verifier, keeps it for this attempt only, and derives a SHA-256 hash of it called the code challenge. Only the hash leaves the application at this stage.

  2. The user is sent to the authorization server

    The browser is redirected to the authorization endpoint with your client id, the exact redirect URI you registered in advance, the scopes you need, a random state value, and the code challenge. From here your application is not involved, which is the point.

  3. The user authenticates and consents

    This happens entirely at the authorization server, on its own pages. Your application never sees the credential and does not learn how the person authenticated. If the organisation adds a second factor next year, nothing in your code changes.

  4. The browser comes back with an authorization code

    A redirect to your registered URI carrying a short-lived, single-use code and the state value you sent. Compare state with what you stored for this attempt: a mismatch or a missing value means this response does not belong to a flow you started, and the only correct action is to abandon it. The code on its own is not access.

  5. The client exchanges the code for tokens

    A direct back-channel request to the token endpoint containing the code and the original code verifier. The authorization server hashes the verifier and compares it with the challenge from step one. Only the application that began the flow can finish it, because only it has the verifier.

  6. The client calls the API with the access token

    Sent in the Authorization header as a bearer token. When it expires, the refresh token — if the flow granted one — obtains a new access token without involving the user again. Store refresh tokens with more care than access tokens; they are the longer-lived of the two.

The two requests, with placeholders in capitals for values you supply
HTTP
GET /authorize?response_type=code
  &client_id=orders-web-client
  &redirect_uri=https://orders.example-company.com/signin-callback
  &scope=openid%20profile%20calendar.read
  &state=RANDOM_VALUE_STORED_FOR_THIS_ATTEMPT
  &code_challenge=BASE64URL_SHA256_OF_THE_VERIFIER
  &code_challenge_method=S256 HTTP/1.1
Host: identity.example-company.com

POST /token HTTP/1.1
Host: identity.example-company.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=SINGLE_USE_CODE_FROM_THE_REDIRECT
&redirect_uri=https://orders.example-company.com/signin-callback
&client_id=orders-web-client
&code_verifier=THE_RANDOM_VALUE_FROM_STEP_ONE
  • The first request is the browser being sent to the authorization server. Its query string is wrapped over several lines here to stay readable; in reality it is one URL.
  • state is a random value you generate and store for this attempt. It has to match when the redirect returns. That single comparison is what stops your callback processing a response it never asked for.
  • code_challenge is the hash of the verifier, so the value travelling through the browser cannot be reused by anything that sees it.
  • The scope list opens with openid, which is what turns an OAuth request into an OpenID Connect one and asks for an ID token alongside the access token. profile and calendar.read are the actual permissions being requested.
  • The second request goes from your server to the token endpoint, not through the browser, and carries the verifier. The authorization server hashes it and compares it with what it stored in step one.
  • A confidential client also authenticates itself on this second request, with a client secret or a signed assertion. A browser or mobile application cannot keep a secret, and closing that gap is precisely what PKCE is for. Never embed a client secret in anything shipped to a device.
  • Every value in capitals is a placeholder. Client ids and redirect URIs are issued and registered in advance, and codes are generated per attempt.

Summary

  • OAuth 2.0 delegates limited access to an API without your application ever handling the user's password
  • OpenID Connect adds authentication on top, producing an ID token that tells your client who signed in
  • The authorization code flow with PKCE is the current default for browser, mobile and server-side clients
  • The implicit flow is superseded: a token in a URL is exposed, and nothing binds it to the application that asked
  • Request the narrowest scopes that do the job, check the state value on return, and validate the ID token yourself

Practice

Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.

Think about it

Make the case, including the cost

Your team needs to read employees' calendars from a third-party scheduling service. Someone suggests storing each employee's scheduling-service password in your database and logging in on their behalf, because it is fewer moving parts.

Give three concrete properties OAuth provides that the password approach does not. Then name what OAuth costs you, honestly.

Show solution

First: you never hold the credential. There is no password in your database, so a leak of your database does not put the scheduling service at risk, and nobody on your team can sign in as an employee.

Second: the grant is scoped. A token for reading calendar events cannot send mail, change settings or delete appointments. The password approach grants everything the employee can do, permanently.

Third: it can be withdrawn. The employee revokes your application's access at the scheduling service, in one place, without changing a password that other systems depend on. There is usually an audit record on the provider's side as well, which the password approach cannot produce because every action looks like the employee.

The cost is real: more moving parts. Redirects to handle, state and verifiers to store per attempt, token expiry and refresh to manage, a provider outage that now affects your feature, and registration to maintain when a redirect URI changes. It is more work than storing a password. It is also the only one of the two you can defend.

Challenge

What PKCE does and does not cover

Your front end is a single-page application, so it is a public client and cannot keep a secret.

Explain in your own words what PKCE protects, and name the risk it leaves untouched. Which other lesson in this course deals with that remaining risk?

Show solution

PKCE protects the exchange step. The authorization code travels back through the browser, where it is more exposed than a back-channel value, and PKCE makes an intercepted code useless on its own: redeeming it requires the verifier, which never left the application that started the flow. It gives a public client the binding that a client secret gives a confidential one.

What it does not touch is what happens after the tokens arrive. The access token is now in the browser, and script running in your page can read whatever your own script can read. If a cross-site scripting flaw lets someone else's script run in your page, PKCE is irrelevant to it.

That is the cross-site scripting lesson later in this course. It is also the practical reason browser applications sometimes keep tokens on a server-side session instead, with the browser holding only a cookie — it moves the credential out of reach of page script, at the cost of a back end for the front end to talk to.

Saved in this browser only.