Requesting a Token
A typical architecture is composed of two application (aka client) types - machine to machine calls and interactive applications.
Machine to Machine Communication
Section titled “Machine to Machine Communication”In this scenario a headless application with no interactive user (e.g. a server daemon, batch job etc.) wants to call an API.
Prerequisites are:
- define a client for the client credentials grant type
- define an API scope (and optionally a resource)
- grant the client access to the scope via the
AllowedScopes
property
According to the OAuth specification, you request a token by posting to the token endpoint:
POST /connect/tokenCONTENT-TYPE application/x-www-form-urlencoded
client_id=client1& client_secret=secret& grant_type=client_credentials& scope=scope1
In the success case, this will return a JSON response containing the access token:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache
{ "access_token": "2YotnFZFEjr1zCsicMWpAA", "token_type": "bearer", "expires_in": 3600,}
.NET Client Library
Section titled “.NET Client Library”In .NET you can leverage the IdentityModel client library to request tokens.
The above token request would look like this in C#:
using IdentityModel.Client;
var client = new HttpClient();
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest{ Address = "https://demo.duendesoftware.com/connect/token",
ClientId = "m2m", ClientSecret = "secret", Scope = "api"});
Automating Token Requests In ASP.NET Core And Worker Applications
Section titled “Automating Token Requests In ASP.NET Core And Worker Applications”The Duende.AccessTokenManagement library can automate client credential request and token lifetime management for you.
Using this library, you only need to register the token client in DI:
builder.Services.AddAccessTokenManagement(options =>{ options.Client.Clients.Add("client", new ClientCredentialsTokenRequest { Address = "https://demo.duendesoftware.com/connect/token", ClientId = "m2m", ClientSecret = "secret", Scope = "api" });});
You can then add token management to an HTTP-factory provided client:
builder.Services.AddClientAccessTokenClient("client", configureClient: client =>{ client.BaseAddress = new Uri("https://demo.duendesoftware.com/api/");});
…and finally use the client with automatic token management in your application code:
public class DataController : Controller{ IHttpClientFactory _factory;
public DataController(IHttpClientFactory factory) { _factory = factory; }
public IActionResult Index() { var client = _factory.CreateClient("client");
// rest omitted }}
Interactive Applications
Section titled “Interactive Applications”In this scenario, an interactive application like a web application or mobile/desktop app wants to call an API in the context of an authenticated user (see spec here).
You will receive three tokens - an identity token containing details about the end-user authentication, the access token to call the API, and a refresh token for access token lifetime management. The access token will also contain some information about the end-user (e.g. the user ID), so that the API can do authorization based on the user’s identity.
In this scenario you typically use the authorization code flow which first involves a call to the authorize endpoint for all human interactions (e.g. login and/or consent). This returns a code, which you then redeem at the token endpoint to retrieve identity and access tokens.
Prerequisites are:
- define a client for the authorization code grant type
- define an identity resource, e.g.
openid
- define an API scope (and optionally a resource)
- grant the client access to both scopes via the
AllowedScopes
property
Front-channel
Section titled “Front-channel”The call to the authorize endpoint is done using a redirect in the browser:
GET /connect/authorize? client_id=client1& scope=openid api1& response_type=code& redirect_uri=https://myapp/callback&
On success, the browser will ultimately redirect to the callback endpoint transmitting the authorization code (and other parameters like the granted scopes):
GET /callback? code=abc& scope=openid api1
Back-channel
Section titled “Back-channel”The client then opens a back-channel communication to the token service to retrieve the tokens:
POST /connect/tokenCONTENT-TYPE application/x-www-form-urlencoded
client_id=client1& client_secret=secret& grant_type=authorization_code& code=abc& redirect_uri=https://myapp/callback
In this scenario, the token response will contain three tokens:
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache
{ "id_token": "...", "access_token": "...", "refresh_token": "...", "token_type": "bearer", "expires_in": 3600,}
.NET Client Library
Section titled “.NET Client Library”The most common client library for .NET is the OpenID Connect authentication handler for ASP.NET Core. This library handles the complete front- and back-channel interaction and coordination.
You only need to configure it in your startup code:
builder.Services.AddAuthentication(options =>{ options.DefaultScheme = "cookie"; options.DefaultChallengeScheme = "duende";}) .AddCookie("cookie") .AddOpenIdConnect("duende", "IdentityServer", options => { options.Authority = "https://demo.duendesoftware.com"; options.ClientId = "interactive.confidential";
options.ResponseType = "code"; options.ResponseMode = "query"; options.SaveTokens = true;
options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("api"); options.Scope.Add("offline_access");
options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" };
// Disable x-client-SKU and x-client-ver headers options.DisableTelemetry = true; });
Automating Token Management In ASP.NET Core
Section titled “Automating Token Management In ASP.NET Core”The Duende.AccessTokenManagement library can also be used to automate token lifetime management in ASP.NET Core applications for you.