DPoP token request¶
This example demonstrates how to manually request a DPoP token using the OAuth 2.0 client credentials flow. The code sample demostrates how to request an access token from an OAuth 2.0 /token endpoint using DPoP (Demonstration of Proof of Possession), including support for nonce binding to enhance security.
For a detailed explanation of the code flow, see the tutorial.
Note! Be careful with
new HttpClient()
, it is only for demo purposes.
In [ ]:
Copied!
#r "nuget: Fhi.Authentication.Extensions, 1.0.0"
#r "nuget: Duende.IdentityModel, 7.0.0"
#r "nuget: Microsoft.IdentityModel.Tokens, 8.9.0"
using System;
using System.Net.Http;
using System.Collections.Generic;
using Fhi.Authentication.Tokens;
using Duende.IdentityModel;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
var tokenEndpoint = "https://demo.duendesoftware.com/connect/token";
var clientId = "m2m.dpop.nonce";
var scope = "api";
var clientSecret = "secret";
var dpopKey = JwkGenerator.GenerateRsaJwk();
Console.WriteLine($"DPoP Public Key: {dpopKey.PublicKey}");
Console.WriteLine($"DPoP Private Key: {dpopKey.PrivateKey}");
/************************************************************************
* 1. Request token to get nonce
*************************************************************************/
var nonceRequest = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint)
{
Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("scope", scope)
})
};
var dpopProof = TokenHandlers.CreateDPoPProof(
dpopKey,
new JwtPayload
{
[JwtClaimTypes.JwtId] = Guid.NewGuid().ToString(),
[JwtClaimTypes.DPoPHttpMethod] = HttpMethod.Post.Method.ToString(),
[JwtClaimTypes.DPoPHttpUrl] = tokenEndpoint,
[JwtClaimTypes.IssuedAt] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
});
nonceRequest.Headers.Add("DPoP", dpopProof);
await LogRequest(nonceRequest);
var client = new HttpClient();
var nonceResponse = await client.SendAsync(nonceRequest);
await LogResponse(nonceResponse);
/************************************************************************
* 2. Request token with nonce in DPoP proof
*************************************************************************/
var dpopNonce = nonceResponse.Headers.TryGetValues("DPoP-Nonce", out var nonceValues) ? string.Join(",", nonceValues) : null;
var dpopProofWithNonce = TokenHandlers.CreateDPoPProof(
dpopKey,
new JwtPayload
{
[JwtClaimTypes.JwtId] = Guid.NewGuid().ToString(),
[JwtClaimTypes.DPoPHttpMethod] = HttpMethod.Post.Method.ToString(),
[JwtClaimTypes.DPoPHttpUrl] = tokenEndpoint,
[JwtClaimTypes.IssuedAt] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
[JwtClaimTypes.Nonce] = dpopNonce
});
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint)
{
Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("scope", scope)
})
};
tokenRequest.Headers.Add("DPoP", dpopProofWithNonce);
await LogRequest(tokenRequest);
var tokenResponse = await client.SendAsync(tokenRequest);
await LogResponse(tokenResponse);
public static class TokenHandlers
{
public static string CreateDPoPProof(JwkKeyPair key, JwtPayload payload)
{
var securityKey = new JsonWebKey(key.PrivateKey);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
var jwkDict = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(key.PublicKey);
var jwtHeader = new JwtHeader(signingCredentials)
{
[JwtClaimTypes.TokenType] = "dpop+jwt",
[JwtClaimTypes.JsonWebKey] = jwkDict,
};
var jwt = new JwtSecurityToken(jwtHeader, payload);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
}
#r "nuget: Fhi.Authentication.Extensions, 1.0.0"
#r "nuget: Duende.IdentityModel, 7.0.0"
#r "nuget: Microsoft.IdentityModel.Tokens, 8.9.0"
using System;
using System.Net.Http;
using System.Collections.Generic;
using Fhi.Authentication.Tokens;
using Duende.IdentityModel;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
var tokenEndpoint = "https://demo.duendesoftware.com/connect/token";
var clientId = "m2m.dpop.nonce";
var scope = "api";
var clientSecret = "secret";
var dpopKey = JwkGenerator.GenerateRsaJwk();
Console.WriteLine($"DPoP Public Key: {dpopKey.PublicKey}");
Console.WriteLine($"DPoP Private Key: {dpopKey.PrivateKey}");
/************************************************************************
* 1. Request token to get nonce
*************************************************************************/
var nonceRequest = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint)
{
Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("grant_type", "client_credentials"),
new KeyValuePair("client_id", clientId),
new KeyValuePair("client_secret", clientSecret),
new KeyValuePair("scope", scope)
})
};
var dpopProof = TokenHandlers.CreateDPoPProof(
dpopKey,
new JwtPayload
{
[JwtClaimTypes.JwtId] = Guid.NewGuid().ToString(),
[JwtClaimTypes.DPoPHttpMethod] = HttpMethod.Post.Method.ToString(),
[JwtClaimTypes.DPoPHttpUrl] = tokenEndpoint,
[JwtClaimTypes.IssuedAt] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
});
nonceRequest.Headers.Add("DPoP", dpopProof);
await LogRequest(nonceRequest);
var client = new HttpClient();
var nonceResponse = await client.SendAsync(nonceRequest);
await LogResponse(nonceResponse);
/************************************************************************
* 2. Request token with nonce in DPoP proof
*************************************************************************/
var dpopNonce = nonceResponse.Headers.TryGetValues("DPoP-Nonce", out var nonceValues) ? string.Join(",", nonceValues) : null;
var dpopProofWithNonce = TokenHandlers.CreateDPoPProof(
dpopKey,
new JwtPayload
{
[JwtClaimTypes.JwtId] = Guid.NewGuid().ToString(),
[JwtClaimTypes.DPoPHttpMethod] = HttpMethod.Post.Method.ToString(),
[JwtClaimTypes.DPoPHttpUrl] = tokenEndpoint,
[JwtClaimTypes.IssuedAt] = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
[JwtClaimTypes.Nonce] = dpopNonce
});
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint)
{
Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("grant_type", "client_credentials"),
new KeyValuePair("client_id", clientId),
new KeyValuePair("client_secret", clientSecret),
new KeyValuePair("scope", scope)
})
};
tokenRequest.Headers.Add("DPoP", dpopProofWithNonce);
await LogRequest(tokenRequest);
var tokenResponse = await client.SendAsync(tokenRequest);
await LogResponse(tokenResponse);
public static class TokenHandlers
{
public static string CreateDPoPProof(JwkKeyPair key, JwtPayload payload)
{
var securityKey = new JsonWebKey(key.PrivateKey);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
var jwkDict = System.Text.Json.JsonSerializer.Deserialize>(key.PublicKey);
var jwtHeader = new JwtHeader(signingCredentials)
{
[JwtClaimTypes.TokenType] = "dpop+jwt",
[JwtClaimTypes.JsonWebKey] = jwkDict,
};
var jwt = new JwtSecurityToken(jwtHeader, payload);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
}