Introducing the next era of Duende IdentityServer.

Read our CEO’s announcement

OAuth 2.0 vs OpenID Connect: What Is the Difference?

Khalid Abuhakmeh
Two blue circles

OAuth 2.0 handles authorization: what can this application do? OpenID Connect (OIDC) handles authentication: who is this user? Most real applications need both, and the two protocols work together by design. OIDC is a layer on top of OAuth 2.0, not a replacement for it.

This post explains what each protocol does, how they differ, and how to configure them together in ASP.NET Core with Duende IdentityServer.

Updated: August 2026

What Is OAuth 2.0?

OAuth 2.0 is an authorization framework that lets an application request limited access to a user’s resources without handling the user’s password. The application gets an access token from an authorization server, then presents that token to an API. The API checks the token’s scopes and decides what the application is allowed to do.

OAuth 2.0 defines several grant types (authorization code, client credentials, refresh token) and the concept of scopes as permissions. It does not define a standard way to identify the user. An access token tells the API “this application has permission to read this data.” It does not say “this request came from Alice.”

For a deeper look at how OAuth 2.0 works, read What Is OAuth?. The full protocol is defined in RFC 6749.

What Is OpenID Connect?

OpenID Connect is an authentication protocol built on top of OAuth 2.0. It adds a standardized way to verify who the user is and retrieve basic profile information.

The key additions OIDC brings:

  • ID tokens: A JWT (pronounced “jot”) that contains claims about the user, such as sub (subject identifier), name, and email. The client application validates this token to confirm the user’s identity.
  • UserInfo endpoint: An API that returns additional claims about the authenticated user.
  • Discovery document: A JSON document at /.well-known/openid-configuration that publishes all the server’s endpoints, supported scopes, and signing keys. Clients use this instead of hardcoding URLs.
  • Standard scopes: openid (required, returns the sub claim), profile (name, family name, etc.), email, address, and phone.

Where OAuth 2.0 says “this app can access this resource,” OIDC says “this user is Alice, she authenticated 30 seconds ago with a password and a second factor, and here is her email.”

The full specification is OpenID Connect Core 1.0.

What Is the Difference Between OAuth 2.0 and OpenID Connect?

Aspect OAuth 2.0 OpenID Connect

Purpose

Authorization (what can this app do?)

Authentication (who is this user?)

Token types

Access token, refresh token

ID token (plus access and refresh tokens from OAuth)

User identity

No standard mechanism

ID token with sub, name, email, and other claims

Specification

Scopes

Application-defined (e.g., api1, read, write)

Standardized (openid, profile, email, address, phone) plus custom

Discovery

Not defined

/.well-known/openid-configuration

Use alone?

Yes, for machine-to-machine API access

No, OIDC requires OAuth 2.0 underneath

Think of it this way: OAuth 2.0 is the hotel key card. It grants access to specific rooms. OIDC is the check-in desk. It verifies who you are, then gives you the key card.

When Do You Need OAuth 2.0?

Use OAuth 2.0 alone when no user is involved and you just need one service to call another.

Machine-to-machine communication. A background service calls your API using the client credentials grant. The service authenticates with its own client ID and secret, gets an access token scoped to the API, and makes requests. No user identity is needed or expected.

Csharp

// Requesting a token with client credentials (using IdentityModel)
using IdentityModel.Client;

using var httpClient = new HttpClient();

var disco = await httpClient.GetDiscoveryDocumentAsync("https://identity.example.com");

var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(
    new ClientCredentialsTokenRequest
    {
        Address = disco.TokenEndpoint,
        ClientId = "service.worker",
        ClientSecret = "secret",
        Scope = "api1"
    });

// Use the access token to call the API
httpClient.SetBearerToken(tokenResponse.AccessToken!);
var result = await httpClient.GetAsync("https://api.example.com/reports");

Other examples: nightly batch jobs pushing data to an API, microservices communicating within a cluster, CI/CD pipelines calling deployment APIs.

When Do You Need OpenID Connect?

Use OIDC when your application needs to know who the user is.

User sign-in. A web application redirects the user to the identity provider, the user authenticates, and the application receives an ID token with the user’s claims. The application now knows the user’s identity and can create a session.

Single sign-on (SSO). Multiple applications share the same identity provider. A user signs in once and is recognized across all applications without entering credentials again.

Profile information. Your application needs the user’s name, email, or other attributes. OIDC standard scopes (profile, email) provide these through the ID token or UserInfo endpoint.

When Do You Need Both?

Most web applications need both protocols. The user signs in (OIDC), and the application calls APIs on the user’s behalf (OAuth 2.0).

Here is what that flow looks like:

sequenceDiagram
    participant User
    participant WebApp
    participant IdentityServer
    participant API

    User->>WebApp: Click "Sign In"
    WebApp->>IdentityServer: Redirect to /authorize (scope: openid profile api1)
    IdentityServer->>User: Show login page
    User->>IdentityServer: Enter credentials
    IdentityServer->>WebApp: Authorization code
    WebApp->>IdentityServer: Exchange code for tokens
    IdentityServer->>WebApp: ID token + access token
    WebApp->>WebApp: Validate ID token, create session
    WebApp->>API: GET /data (Authorization: Bearer {access_token})
    API->>API: Validate access token, check scopes
    API->>WebApp: 200 OK with data

The OIDC flow authenticates the user and returns an ID token (identity) and an access token (authorization). The web app uses the ID token to establish a session, then uses the access token to call the API. One protocol flow, two tokens, two purposes.

How Do OAuth 2.0 and OIDC Work Together in ASP.NET Core?

On the client side, the ASP.NET Core OIDC middleware handles the entire authorization code flow with PKCE (pronounced “pixy”). It redirects to the identity provider, receives the authorization code, exchanges it for tokens, validates the ID token, and creates the user’s authentication session.

Client Configuration

Csharp

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.Authority = "https://identity.example.com";
        options.ClientId = "web.app";
        options.ClientSecret = "secret";
        options.ResponseType = "code";
        options.Scope.Clear();
        options.Scope.Add("openid");   // OIDC: request user identity
        options.Scope.Add("profile");  // OIDC: request profile claims
        options.Scope.Add("api1");     // OAuth: request API access
        options.SaveTokens = true;
        options.MapInboundClaims = false;
    });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// ...

The openid and profile scopes trigger the OIDC behavior: the server returns an ID token with the user’s claims. The api1 scope triggers the OAuth 2.0 behavior: the server returns an access token scoped to the API. The ResponseType = "code" tells the middleware to use the authorization code flow with PKCE.

Server Configuration (Duende IdentityServer)

On the server side, Duende IdentityServer acts as both the OAuth 2.0 authorization server and the OpenID Connect provider. A single client registration handles both protocols:

Csharp

using Duende.IdentityServer.Models;

new Client
{
    ClientId = "web.app",
    ClientName = "Web Application",
    ClientSecrets = { new Secret("secret".Sha256()) },

    AllowedGrantTypes = GrantTypes.Code,
    RequirePkce = true,

    RedirectUris = { "https://webapp.example.com/signin-oidc" },
    PostLogoutRedirectUris = { "https://webapp.example.com/signout-callback-oidc" },

    // Scopes this client can request
    AllowedScopes =
    {
        "openid",   // OIDC: enables ID token with sub claim
        "profile",  // OIDC: adds name, family_name, etc. to ID token
        "api1"      // OAuth: enables access token for the API
    },

    AllowOfflineAccess = true  // Enables refresh tokens
};

The openid and profile entries in AllowedScopes make this client an OIDC relying party. The api1 entry makes it an OAuth 2.0 client. IdentityServer issues the right tokens based on what the client requests.

What Does the Discovery Document Contain?

Every OpenID Connect provider publishes a discovery document at /.well-known/openid-configuration. This JSON document tells clients everything they need to interact with the server: which endpoints exist, what scopes and claims are supported, and where to find the signing keys.

Json

{
  "issuer": "https://identity.example.com",
  "authorization_endpoint": "https://identity.example.com/connect/authorize",
  "token_endpoint": "https://identity.example.com/connect/token",
  "userinfo_endpoint": "https://identity.example.com/connect/userinfo",
  "end_session_endpoint": "https://identity.example.com/connect/endsession",
  "jwks_uri": "https://identity.example.com/.well-known/openid-configuration/jwks",
  "scopes_supported": ["openid", "profile", "email", "api1", "offline_access"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"]
}

Key fields:

  • issuer: The identifier for this server. Tokens include this value, and clients validate it.
  • authorization_endpoint: Where the client redirects the user to start the OIDC/OAuth flow.
  • token_endpoint: Where the client exchanges an authorization code for tokens (backchannel call).
  • userinfo_endpoint: Where the client retrieves additional user claims after authentication.
  • jwks_uri: The public keys used to sign tokens. Clients and APIs fetch these to verify token signatures.

Clients should resolve all endpoint URLs from the discovery document instead of hardcoding them. The ASP.NET Core OIDC middleware does this when you set options.Authority. Duende IdentityServer publishes the discovery document at startup with no extra configuration.

The specification is OpenID Connect Discovery 1.0.

How Does Duende IdentityServer Implement Both Protocols?

Duende IdentityServer is both an OAuth 2.0 authorization server and an OpenID Connect provider in a single deployment. You register clients, define scopes and resources, and IdentityServer handles the protocol details: issuing ID tokens, access tokens, and refresh tokens according to the specs.

What IdentityServer supports:

  • All recommended grant types: authorization code with PKCE, client credentials, refresh token exchange, device authorization, and token exchange (RFC 8693).
  • Token types: ID tokens (always JWT), access tokens (JWT or reference), refresh tokens with configurable rotation.
  • Discovery and JWKS: Published at startup, updated when signing keys rotate.
  • Dynamic Client Registration: Clients can register programmatically (RFC 7591).
  • Server-side sessions: Query, list, and revoke user sessions across clients.
  • Security extensions: DPoP, mTLS, Pushed Authorization Requests (PAR), and FAPI 2.0 compliance.

IdentityServer integrates with ASP.NET Core’s authentication and authorization pipeline. On the server, you call AddIdentityServer() and configure clients, resources, and scopes. On the client, standard ASP.NET Core middleware (AddOpenIdConnect, AddJwtBearer) works without any Duende-specific client libraries.

For a complete overview, see the Duende IdentityServer documentation.

Get Started