Last year, Mike Rousos posted a great post about token authentication on the .NET blog and demonstrated how you could leverage ASP.NET Core Identity and OpenIddict to create your own tokens in a completely standard way.
Since then, many people emailed me to know if using ASP.NET Core Identity was really mandatory. Good news! While the first OpenIddict alpha bits were tied to Identity, the two have been completely decoupled as part of OpenIddict beta1 and beta2. Concretely, this means you can now use OpenIddict with your own authentication method or your own membership stack.
Get started
This post was updated to include code snippets demonstrating how to register OpenIddict in an ASP.NET Core 2.x application. When using OpenIddict in an ASP.NET Core 2.x application, make sure you're referencing the OpenIddict 2.x packages.
Update your .csproj file to reference the OpenIddict packages
For this demo, you'll need to reference 2 packages:
OpenIddict, that references the core services, the token server and the validation services.
OpenIddict.EntityFrameworkCore, that contains the Entity Framework Core stores.
services.AddDbContext<DbContext>(options => { // Configure the context to use an in-memory store. options.UseInMemoryDatabase();
// Register the entity sets needed by OpenIddict. // Note: use the generic overload if you need // to replace the default OpenIddict entities. options.UseOpenIddict(); });
services.AddOpenIddict()
// Register the OpenIddict core services. .AddCore(options => { // Configure OpenIddict to use the EF Core stores/models. options.UseEntityFrameworkCore() .UseDbContext<DbContext>(); })
// Register the OpenIddict server handler. .AddServer(options => { // Register the ASP.NET Core MVC services used by OpenIddict. // Note: if you don't call this method, you won't be able to // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. options.UseMvc();
// Enable the token endpoint. options.EnableTokenEndpoint("/connect/token");
// Enable the password flow. options.AllowPasswordFlow();
// Accept anonymous clients (i.e clients that don't send a client_id). options.AcceptAnonymousClients();
// During development, you can disable the HTTPS requirement. options.DisableHttpsRequirement(); })
// Register the OpenIddict validation handler. // Note: the OpenIddict validation handler is only compatible with the // default token format or with reference tokens and cannot be used with // JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler. .AddValidation(); }
// Register the OpenIddict server middleware. app.UseOpenIddictServer();
app.UseMvcWithDefaultRoute(); } }
Make sure to always register the validation middleware very early in your pipeline: if the validation middleware is not at the right place, requests won't be correctly authenticated when reaching the next middleware (e.g MVC).
The same remark applies to OpenIddict, that must be inserted before MVC to validate token requests before they reach your own code. If you don't register it correctly, an exception will be thrown at runtime.
services.AddDbContext<DbContext>(options => { // Configure the context to use an in-memory store. options.UseInMemoryDatabase(nameof(DbContext));
// Register the entity sets needed by OpenIddict. // Note: use the generic overload if you need // to replace the default OpenIddict entities. options.UseOpenIddict(); });
services.AddOpenIddict()
// Register the OpenIddict core services. .AddCore(options => { // Configure OpenIddict to use the EF Core stores/models. options.UseEntityFrameworkCore() .UseDbContext<DbContext>(); })
// Register the OpenIddict server handler. .AddServer(options => { // Register the ASP.NET Core MVC services used by OpenIddict. // Note: if you don't call this method, you won't be able to // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. options.UseMvc();
// Enable the token endpoint. options.EnableTokenEndpoint("/connect/token");
// Enable the password flow. options.AllowPasswordFlow();
// Accept anonymous clients (i.e clients that don't send a client_id). options.AcceptAnonymousClients();
// During development, you can disable the HTTPS requirement. options.DisableHttpsRequirement(); })
// Register the OpenIddict validation handler. // Note: the OpenIddict validation handler is only compatible with the // default token format or with reference tokens and cannot be used with // JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler. .AddValidation();
publicclassAuthorizationController : Controller { [HttpPost("~/connect/token"), Produces("application/json")] public IActionResult Exchange(OpenIdConnectRequest request) { if (request.IsPasswordGrantType()) { // Validate the user credentials.
// Note: to mitigate brute force attacks, you SHOULD strongly consider // applying a key derivation function like PBKDF2 to slow down // the password validation process. You SHOULD also consider // using a time-constant comparer to prevent timing attacks. if (request.Username != "alice@wonderland.com" || request.Password != "P@ssw0rd") { return Forbid(OpenIddictServerDefaults.AuthenticationScheme); }
// Create a new ClaimsIdentity holding the user identity. var identity = new ClaimsIdentity( OpenIddictServerDefaults.AuthenticationScheme, OpenIdConnectConstants.Claims.Name, OpenIdConnectConstants.Claims.Role);
// Add a "sub" claim containing the user identifier, and attach // the "access_token" destination to allow OpenIddict to store it // in the access token, so it can be retrieved from your controllers. identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "71346D62-9BA5-4B6D-9ECA-755574D628D8", OpenIdConnectConstants.Destinations.AccessToken);
// Ask OpenIddict to generate a new token and return an OAuth 2.0 token response. return SignIn(principal, OpenIddictServerDefaults.AuthenticationScheme); }
thrownew InvalidOperationException("The specified grant type is not supported."); } }
To send an authenticated request, simply attach the bearer token to the Authorization header using the following syntax: Authorization: Bearer [your bearer token] (without the square brackets)