OAuth 2.0 and OpenID Connect
How "Sign in with Google" works, and how apps get limited access to APIs without sharing passwords.
12 min read
You will learn
- Name the roles in OAuth 2.0
- Walk through the authorization code flow with PKCE
- Distinguish access tokens, refresh tokens and ID tokens
- Explain what OpenID Connect adds to OAuth
The password-sharing problem
A printing service wants to print photos from your cloud storage. In the bad old days, you'd give it your storage password — so it could also read your email, change your password, and keep access forever.
OAuth 2.0 solves this with delegated authorization: you approve a limited permission ("read photos") for this app, and it receives a token — never your password. You can revoke it any time.
The roles
| Role | In the example |
|---|---|
| Resource owner | You |
| Client | The printing app |
| Authorization server | The cloud provider's login/consent service — it issues tokens |
| Resource server | The photo API — it accepts tokens |
A scope names a permission: photos.read, calendar.write, openid profile email.
The authorization code flow (with PKCE)
The recommended flow for web, mobile and single-page apps:
1. App → Browser: redirect to the authorization server
GET https://login.provider.example/authorize
?response_type=code
&client_id=print-app
&redirect_uri=https://print.example/callback
&scope=openid photos.read
&state=xyz123 (protects against forged callbacks)
&code_challenge=E9Melhoa… (PKCE: hash of a random secret)
2. User logs in at the provider (the app never sees the password) and consents.
3. Provider → Browser: redirect back with a short-lived code
https://print.example/callback?code=SplxlOBe…&state=xyz123
4. App → Provider (back channel, server-to-server or direct HTTPS call):
POST /token code=SplxlOBe… & code_verifier=dBjftJeZ… (proves it's the same client)
← { "access_token": "…", "refresh_token": "…", "id_token": "…", "expires_in": 3600 }
5. App → Photo API: Authorization: Bearer <access_token>Why the detour through a code? The code travels through the browser, which is exposed; the tokens are delivered directly to the client in step 4, and the PKCE verifier proves only the app that started the flow can redeem the code.
Three kinds of tokens
| Token | Purpose | Audience |
|---|---|---|
| Access token | Call APIs. Short-lived (minutes to an hour). | The resource server (API) |
| Refresh token | Get new access tokens without logging in again. Long-lived, revocable, kept secret. | The authorization server |
| ID token (OIDC) | Tell the client who logged in. A JWT with claims like sub, name, email. | The client app |
Never send an ID token to an API as proof of access — that's what access tokens are for.
OpenID Connect: login on top of OAuth
OAuth answers "may this app access that API?" but not "who is the user?". OpenID Connect (OIDC) adds a standard identity layer: request the openid scope and you receive an ID token, plus a userinfo endpoint and discovery metadata (/.well-known/openid-configuration).
"Sign in with Google / Microsoft / GitHub" is OIDC. So is single sign-on across a company's apps with Microsoft Entra ID, Okta or Keycloak.
Other flows
- Client credentials — no user involved: one backend service calling another with its own identity (
client_id+ secret or certificate). - Device code — for TVs and CLIs: "go to example.com/device and enter code ABCD-EFGH".
- The old implicit and password grants are deprecated — don't use them in new apps.
In ASP.NET Core
// A web app that logs users in via an OIDC provider
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = "https://login.provider.example";
options.ClientId = "print-app";
options.ResponseType = "code"; // authorization code flow (+ PKCE by default)
});
// An API that accepts access tokens
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
options.Authority = "https://login.provider.example";
options.Audience = "photos-api";
});The framework handles redirects, code exchange, token validation and signature checks for you.
Key takeaways
- OAuth 2.0 delegates limited, revocable access (scopes) without sharing passwords.
- Use the authorization code flow with PKCE; tokens are exchanged on a back channel.
- Access tokens call APIs, refresh tokens renew them, ID tokens (OIDC) identify the user to the client.
- OpenID Connect = OAuth + standardized login — the basis of "Sign in with…" and SSO.
Quiz
4 questions · 3 correct to pass1.What problem did OAuth 2.0 originally solve?
2.What does OpenID Connect add on top of OAuth 2.0?
3.In the authorization code flow, why does the app exchange the code for tokens on a back channel?
4.What is PKCE for?
To complete this lesson
- ○ Pass the quiz (3/4 correct)