Grant Types and Flows
Every client declares one or more grant types under Allowed Grants. A grant type
determines how a client obtains tokens from IdentityServerNET — which endpoints are
involved, whether a user is present, and how credentials are exchanged.
This page explains the available grant types and walks through the recommended flows with
sequence diagrams and example HTTP requests. All examples use the authority
https://localhost:44300 and the demo client is-webclient-test.
Grant Types Overview
The following grant types can be enabled under Allowed Grants. The descriptions match the
ones shown in the Admin UI:
Grant |
Description |
Status |
authorization_code
|
Recommended for web apps — exchanges a short-lived code for tokens after user login. |
Recommended |
client_credentials
|
Machine-to-machine — client authenticates with its own secret, no user involved. |
Recommended |
refresh_token
|
Allows clients to obtain new access tokens without re-authentication using a long-lived refresh token. |
Recommended |
urn:ietf:params:oauth:grant-type:device_code
|
For input-constrained devices (TV, CLI) — user authorizes on a secondary device with a browser. |
Recommended |
urn:ietf:params:oauth:grant-type:jwt-bearer
|
Exchange a signed JWT assertion for an OAuth access token (federation / service accounts). |
Advanced |
urn:ietf:params:oauth:grant-type:saml2-bearer
|
Exchange a SAML 2.0 assertion for an OAuth access token. |
Advanced |
urn:ietf:params:oauth:grant-type:token-exchange
|
Exchange one token for another; supports impersonation and delegation scenarios. |
Advanced |
urn:openid:params:grant-type:ciba
|
Client-Initiated Backchannel Authentication — authenticate the user via an out-of-band channel (e.g. push notification). |
Advanced |
implicit
|
Legacy browser flow — tokens returned directly from the authorization endpoint. Deprecated, prefer Authorization Code + PKCE. |
Deprecated |
hybrid
|
Combination of code and implicit. Prefer Authorization Code + PKCE. |
Deprecated |
password
|
Resource Owner Password — user provides credentials directly to the client. Avoid for public clients. |
Deprecated |
Note
When you create a client from a template (WebApplication, ApiClient,
JavascriptClient), the appropriate grant types are set automatically. You rarely need to
edit Allowed Grants by hand.
Warning
The implicit, hybrid and password grants are considered legacy. Modern clients
should use Authorization Code with PKCE (for interactive logins) or Client Credentials
(for machine-to-machine). They are documented here only for completeness and receive no
dedicated flow diagram.
Authorization Code + PKCE
The standard flow for web applications and single-page apps. The user authenticates at
IdentityServerNET, the client receives a short-lived code, and exchanges it for tokens on
the back channel. PKCE (Proof Key for Code Exchange) binds the code to the client that
started the flow, preventing code interception attacks.
Used by the WebApplication and JavascriptClient templates. Enable via the
authorization_code grant and the RequirePkce option.
sequenceDiagram
participant B as Browser
participant C as Client (web app)
participant IS as IdentityServerNET
B->>C: Open protected page
C->>B: Redirect to /connect/authorize (+ code_challenge)
B->>IS: GET /connect/authorize
IS->>B: Show login page
B->>IS: Submit credentials
IS->>B: Redirect to redirect_uri (+ code)
B->>C: Deliver code
C->>IS: POST /connect/token (code + code_verifier + secret)
IS->>C: access_token, id_token, refresh_token
C->>B: Set session cookie
Step 1 — Authorization request (browser):
GET https://localhost:44300/connect/authorize?
client_id=is-webclient-test&
response_type=code&
scope=openid profile offline_access&
redirect_uri=https://localhost:44360/signin-oidc&
code_challenge=K2-ltc0...&
code_challenge_method=S256&
state=xyz&
nonce=abc
Step 2 — Token request (back channel, after the code is returned):
POST https://localhost:44300/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
client_id=is-webclient-test&
client_secret=secret&
code=8f1a...&
code_verifier=dBjftJeZ...&
redirect_uri=https://localhost:44360/signin-oidc
Note
In an ASP.NET Core client the OIDC middleware performs both steps automatically — see
Web Application. PKCE parameters (code_challenge / code_verifier) are generated and
validated for you.
Pushed Authorization Request (PAR)
A hardening of the Authorization Code flow (RFC 9126). Instead of putting all parameters into the
browser URL, the client first POSTs them to the /connect/par endpoint on the back channel
and receives a short-lived request_uri. The browser redirect then only carries client_id
and request_uri — no scope, code_challenge or client_secret is ever visible in the
URL or server logs.
sequenceDiagram
participant B as Browser
participant C as Client
participant IS as IdentityServerNET
C->>IS: POST /connect/par (all params + secret)
IS->>C: request_uri (expires in 60s)
C->>B: Redirect to /connect/authorize?client_id=...&request_uri=...
B->>IS: GET /connect/authorize (request_uri only)
IS->>B: Show login page
B->>IS: Submit credentials
IS->>B: Redirect to redirect_uri (+ code)
B->>C: Deliver code
C->>IS: POST /connect/token
IS->>C: tokens
Step 1 — Push the parameters (back channel):
POST https://localhost:44300/connect/par
Content-Type: application/x-www-form-urlencoded
client_id=is-webclient-test&
client_secret=secret&
response_type=code&
scope=openid profile&
redirect_uri=https://localhost:44360/signin-oidc&
code_challenge=K2-ltc0...&
code_challenge_method=S256
Response:
HTTP/1.1 201 Created
{
"request_uri": "urn:ietf:params:oauth:request_uri:8f1a...",
"expires_in": 60
}
Step 2 — Browser redirect uses only the request_uri:
GET https://localhost:44300/connect/authorize?
client_id=is-webclient-test&
request_uri=urn:ietf:params:oauth:request_uri:8f1a...
PAR can be enforced per client via the RequirePushedAuthorization option (see
Client Reference (Advanced Options)). The server also advertises the pushed_authorization_request_endpoint
in its discovery document so the ASP.NET Core OIDC middleware (.NET 9+) uses it automatically. See
the Stores section in Configuration for server-side storage of the
request_uri.
Client Credentials
Machine-to-machine access with no user involved. The client authenticates with its own secret and
receives an access token. Used by the ApiClient template.
sequenceDiagram
participant C as Client (service)
participant IS as IdentityServerNET
participant API as Web API
C->>IS: POST /connect/token (client_id + secret)
IS->>C: access_token
C->>API: Request + Bearer access_token
API->>C: Protected resource
POST https://localhost:44300/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=my-api-commands&
client_secret=secret&
scope=my-api my-api.command
See API Client for a full walk-through including how to secure the target Web API.
Device Flow
For input-constrained devices (smart TVs, CLI tools) that cannot easily show a browser. The device
requests a user_code and asks the user to enter it on a second device (phone, laptop), while it
polls the token endpoint until the user has authorized. Enable via the
urn:ietf:params:oauth:grant-type:device_code grant.
sequenceDiagram
participant D as Device (TV/CLI)
participant IS as IdentityServerNET
participant U as User's phone
D->>IS: POST /connect/deviceauthorization
IS->>D: device_code, user_code, verification_uri
D->>U: Show "go to URL, enter CODE"
U->>IS: Open verification_uri, log in, enter user_code
loop until authorized
D->>IS: POST /connect/token (device_code)
IS->>D: authorization_pending
end
IS->>D: access_token, refresh_token
Step 1 — Device requests a code:
POST https://localhost:44300/connect/deviceauthorization
Content-Type: application/x-www-form-urlencoded
client_id=device-client&scope=openid profile
Response:
{
"device_code": "GmRh...",
"user_code": "WDJB-MJHT",
"verification_uri": "https://localhost:44300/device",
"expires_in": 300,
"interval": 5
}
Step 2 — Device polls the token endpoint:
POST https://localhost:44300/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&
client_id=device-client&
device_code=GmRh...
Refresh Token
Lets a client obtain a fresh access token when the old one expires, without sending the user
through the login flow again. Requires the AllowOfflineAccess option and the
offline_access scope in the original request. The client receives a refresh_token alongside
the access token and exchanges it later.
sequenceDiagram
participant C as Client
participant IS as IdentityServerNET
Note over C: access_token expired
C->>IS: POST /connect/token (grant_type=refresh_token)
IS->>C: new access_token (+ new refresh_token)
POST https://localhost:44300/connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
client_id=is-webclient-test&
client_secret=secret&
refresh_token=8xLOxBtZp8...
The lifetime and rotation behaviour of refresh tokens is controlled by the Advanced Properties
(AbsoluteRefreshTokenLifetime, SlidingRefreshTokenLifetime, RefreshTokenUsage,
RefreshTokenExpiration) — see Client Reference (Advanced Options).