OpenIDDict example with client credentials flow

Set entityframework models required for OpenIDDict to work


builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
    options.UseOpenIddict();
});

Configure OpenIDDict service


services.AddOpenIddict()
        .AddCore(options =>
         {    
         	options.UseEntityFrameworkCore()
                       .UseDbContext<ApplicationDbContext>();
         })
		/* This part is required for integration with the ASP.NET core request pipline */   
         .AddServer(options =>
         {
            // Enable the token endpoint.
            options.SetTokenEndpointUris("/connect/token");

            // Enable the client credentials flow.
            options.AllowClientCredentialsFlow();

            // Register the signing and encryption credentials.
            options.AddDevelopmentEncryptionCertificate()
                   .AddDevelopmentSigningCertificate();

            // Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
            options.UseAspNetCore()
                  .EnableTokenEndpointPassthrough(); /* It indicates to OpenIddict that you don't want to use the events model to handle token requests and that you want the requests to be handled later in the ASP.NET Core pipeline. */
         })

         // Register the OpenIddict validation components.
            .AddValidation(options =>
         {
                // Import the configuration from the local OpenIddict server instance, i.e from the settings defined in the "AddServer" call above.
                options.UseLocalServer();

                // Register the ASP.NET Core host.
                options.UseAspNetCore();
         });

Seed an application


IOpenIddictApplicationManager _applicationManager;
...

string clientId = "...";
string clientSecret = "...";
string clientName = "...";

if (await _applicationManager.FindByClientIdAsync(clientId) == null)
{
	await _applicationManager.CreateAsync(new OpenIddictApplicationDescriptor
    {
    	ClientId = clientId,
        ClientSecret = clientSecret,
        DisplayName = clientName,
        Permissions =
        {
			Permissions.Endpoints.Token,
			Permissions.GrantTypes.ClientCredentials
        }
    });
}

Create a controller with an exchange method to handle token requests


[HttpPost("~/connect/token"), IgnoreAntiforgeryToken, Produces("application/json")]
    public async Task<IActionResult> Exchange()
    {
        OpenIddictRequest? request = HttpContext.GetOpenIddictServerRequest();
        if (request == null)
        {
            return BadRequest();
        }

        if (request.IsClientCredentialsGrantType())
        {
            // Note: the client credentials are automatically validated by OpenIddict:
            // if client_id or client_secret are invalid, this action won't be invoked.

            if (await _applicationManager.FindByClientIdAsync(request.ClientId!) is not OpenIddictEntityFrameworkCoreApplication application)
            {
                throw new InvalidOperationException("The application details cannot be found in the database.");
            }

            // Create the claims-based identity that will be used by OpenIddict to generate tokens.
            ClaimsIdentity identity = new(
                authenticationType: TokenValidationParameters.DefaultAuthenticationType,
                nameType: Claims.Name,
                roleType: Claims.Role);

            // Add the claims that will be persisted in the tokens (use the client_id as the subject identifier).
            identity.AddClaim(Claims.Subject, application.ClientId!);
            identity.AddClaim(Claims.Name, application.DisplayName!);

            ClaimsPrincipal principal = new (identity);

            
            // Set the list of scopes granted to the client application in access_token.
            
            principal.SetScopes(request.GetScopes());

            Dictionary<string, string[]> destinations = new ()
            {
                 [Claims.Name] = new[] { Destinations.AccessToken, Destinations.IdentityToken },
                 [Claims.Subject] = new[] { Destinations.AccessToken, Destinations.IdentityToken }
            };
            
            
            principal.SetDestinations(destinations.ToImmutableDictionary());

            return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }

        throw new NotImplementedException("The specified grant type is not implemented.");
    }

Add authorization with the openid scheme to handle request validation and authorization


[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]
[HttpGet("test")]
public async Task<IActionResult> Test()
{
	string? subject = User.FindFirst(Claims.Subject)?.Value;
    return Ok(");
}
    

Post a Comment

Previous Post Next Post