Example: Authenticating an external user without user interaction in .NET
A .NET example generating a signed ID token and using it to initiate a backchannel_external_token authentication flow.
warning
This example is intended to illustrate the authentication flow and help you understand how the pieces fit together. It is not a recommended, production-ready implementation.
When dealing with security-sensitive operations such as token signing, always use well-established, well-maintained libraries rather than building your own. Doing so helps you avoid subtle mistakes that can lead to serious security vulnerabilities.
#:property TargetFramework=net10.0
#:package System.IdentityModel.Tokens.Jwt@8.22.0
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using Microsoft.IdentityModel.Tokens;
const string MyPrivateKeyPEM = "..."; // Your generated private key in PEM format
const string MyKeyId = "..."; // The id of your generated private key
const string MyClientId = "..."; // Client ID provided by Future Ordering
const string MyClientSecret = "..."; // Client secret provided by Future Ordering
const string TokenIssuer = "..."; // e.g. `https://myissuer.example.com`
const string TokenAudience = "..."; // e.g. `https://<tenantId>.login.futureordering.com`
// Prepare signing credentials
var ecdsa = ECDsa.Create();
ecdsa.ImportFromPem(MyPrivateKeyPEM);
var signingCredentials = new SigningCredentials(
new ECDsaSecurityKey(ecdsa)
{
KeyId = MyKeyId,
},
SecurityAlgorithms.EcdsaSha256);
// Generate ID token
var handler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var token = new JwtSecurityToken(
new JwtHeader(signingCredentials),
new JwtPayload(
issuer: TokenIssuer,
audience: TokenAudience,
claims: [
new("sub", "my-unique-subject-id"),
new("email", "johndoe@example.com"),
new("given_name", "John"),
new("family_name", "Doe"),
],
issuedAt: now,
notBefore: now,
expires: now.Add(TimeSpan.FromMinutes(5))));
var signedJwt = handler.WriteToken(token);
// Send request
var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, $"{TokenAudience}/connect/token")
{
Content = new FormUrlEncodedContent(
[
new("grant_type", "backchannel_external_token"),
new("client_id", MyClientId),
new("client_secret", MyClientSecret),
new("subject_token", signedJwt),
new("subject_token_type", "urn:ietf:params:oauth:token-type:id_token"),
new("scope", "fo:auth"),
])
};
var response = await httpClient.SendAsync(request);
// Receive and parse request
var result = await response.Content.ReadFromJsonAsync<TokenResponse>();
Console.WriteLine($"Got access token jwt: {result!.AccessToken}");
record TokenResponse(
[property: JsonPropertyName("access_token")] string AccessToken,
[property: JsonPropertyName("expires_in")] int? ExpiresIn);