.NET Academy

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

RoleIn the example
Resource ownerYou
ClientThe printing app
Authorization serverThe cloud provider's login/consent service — it issues tokens
Resource serverThe 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

TokenPurposeAudience
Access tokenCall APIs. Short-lived (minutes to an hour).The resource server (API)
Refresh tokenGet 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 pass
  1. 1.What problem did OAuth 2.0 originally solve?

  2. 2.What does OpenID Connect add on top of OAuth 2.0?

  3. 3.In the authorization code flow, why does the app exchange the code for tokens on a back channel?

  4. 4.What is PKCE for?

To complete this lesson

  • ○ Pass the quiz (3/4 correct)