OpenIddict Module¶
The OpenIddict module is a comprehensive OpenID Connect and OAuth 2.0 authentication server implementation built on top of OpenIddict.
Getting Started¶
You can set up and run it in your development environment. The following section provides step-by-step instructions for configuring and launching the module using Visual Studio or other compatible IDEs.
User Interface¶
This module provides MVC/Razor
Pages UI.
Menu Items¶
This module adds an Open Id menu item under the Administration menu:
- Applications: Register and manage OAuth/OIDC client applications.
- Scopes: Define and manage OAuth 2.0 scopes.
OpenIddictMenus
class has the constants for the menu item names.
Features¶
This section covers some other features provided by this module.
Application Management¶
Applications page is used to manage OpenIddict applications. An application
represent hosted applications that can request tokens from your authentication server.
You can create new application or edit existing applications in this page:
Scope Management¶
OpenIddict module allows to manage API scope. To allow applications to request access tokens for APIs, you need to define API scopes.
You can create a new API resource or edit an existing API resource in this page:
Options¶
OpenIddictBuilder¶
OpenIddictBuilder
can be configured in the PreConfigureServices
method of your OpenIddict module.
Example:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictBuilder>(builder =>
{
//Set options here...
});
}
OpenIddictBuilder
contains various extension methods to configure the OpenIddict services:
.AddServer()
registers the OpenIddict token server services in the DI container. Contains OpenIddictServerBuilder configurations..AddCore()
registers the OpenIddict core services in the DI container. Contains OpenIddictCoreBuilder configurations..AddValidation()
registers the OpenIddict token validation services in the DI container. Contains OpenIddictValidationBuilder configurations.
OpenIddictCoreBuilder¶
OpenIddictCoreBuilder
contains extension methods to configure the OpenIddict core services.
Example:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictCoreBuilder>(builder =>
{
//Set options here...
});
}
These services contain:
- Adding
ApplicationStore
,AuthorizationStore
,ScopeStore
,TokenStore
. - Replacing
ApplicationManager
,AuthorizationManager
,ScopeManager
,TokenManager
. - Replacing
ApplicationStoreResolver
,AuthorizationStoreResolver
,ScopeStoreResolver
,TokenStoreResolver
. - Setting
DefaultApplicationEntity
,DefaultAuthorizationEntity
,DefaultScopeEntity
,DefaultTokenEntity
.
OpenIddictServerBuilder¶
OpenIddictServerBuilder
contains extension methods to configure OpenIddict server services.
Example:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictServerBuilder>(builder =>
{
//Set options here...
});
}
These services contain:
- Registering claims, scopes.
- Setting the
Issuer
URI that is used as the base address for the endpoint URIs returned from the discovery endpoint. - Adding development signing keys, encryption/signing keys, credentials, and certificates.
- Adding/removing event handlers.
- Enabling/disabling grant types.
- Setting authentication server endpoint URIs.
OpenIddictValidationBuilder¶
OpenIddictValidationBuilder
contains extension methods to configure OpenIddict server services.
Example:
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictValidationBuilder>(builder =>
{
//Set options here...
});
}
These services contain:
.AddAudiances()
for resource servers..SetIssuer()
URI that is used to determine the actual location of the OAuth 2.0/OpenID Connect configuration document when using provider discovery..SetConfiguration()
to configureOpenIdConnectConfiguration
..UseIntrospection()
to use introspection instead of local/direct validation.- Adding encryption key, credentials, and certificates.
- Adding/removing event handlers.
.SetClientId()
to set the client identifierclient_id
when communicating with the remote authorization server (e.g for introspection)..SetClientSecret()
to set the identifierclient_secret
when communicating with the remote authorization server (e.g for introspection)..EnableAuthorizationEntryValidation()
to enable authorization validation to ensure theaccess token
is still valid by making a database call for each API request. Note: This may have a negative impact on performance and can only be used with an OpenIddict-based authorization server..EnableTokenEntryValidation()
to enable authorization validation to ensure theaccess token
is still valid by making a database call for each API request. Note: This may have a negative impact on performance and it is required when the OpenIddict server is configured to use reference tokens..UseLocalServer()
to register the OpenIddict validation/server integration services..UseAspNetCore()
to register the OpenIddict validation services for ASP.NET Core in the DI container.
Data seed¶
This module adds some initial data to the database when you run .DbMigrator
if the IsOpenIddictSeedEnabled
option is set:
Configure<DefaultOpenIddictDataSeederOptions>(options =>
{
options.IsOpenIddictSeedEnabled = true; // Enable/disable seeding
});
Using AddApplication
and AddScope
for defining Clients
var configurationSection = configuration.GetSection("OpenIddict:Applications");
Configure<DefaultOpenIddictDataSeederOptions>(options =>
{
options.IsOpenIddictSeedEnabled = true;
});
Configure<OpenIddictDataSeederOptions>(options =>
{
// Add scopes with configuration actions
options.AddScope("OpenIddict", "OpenIddict API", scope =>
{
scope.Resources = new HashSet<string> { "OpenIddict" };
});
// Add applications with configuration actions
options.AddApplication(configurationSection["OpenIddict_Web:ClientId"], app =>
{
var rootUrl = configurationSection["OpenIddict_Web:RootUrl"]!.EnsureEndsWith('/');
app.ApplicationType = OpenIddictConstants.ApplicationTypes.Web;
app.ClientType = OpenIddictConstants.ClientTypes.Confidential;
app.ClientSecret = configurationSection["OpenIddict_Web:ClientSecret"];
app.ClientUri = rootUrl;
app.RedirectUri = $"{rootUrl}signin-oidc";
app.PostLogoutRedirectUri = $"{rootUrl}signout-callback-oidc";
app.LogoUri = "/images/clients/aspnetcore.svg";
app.ConsentType = OpenIddictConstants.ConsentTypes.Implicit;
app.DisplayName = "Web Application";
app.GrantTypes = new List<string> // Hybrid flow
{
OpenIddictConstants.GrantTypes.AuthorizationCode,
OpenIddictConstants.GrantTypes.Implicit
};
});
options.AddApplication(configurationSection["OpenIddict_App:ClientId"], app =>
{
var rootUrl = configurationSection["OpenIddict_App:RootUrl"]?.TrimEnd('/');
app.ApplicationType = OpenIddictConstants.ApplicationTypes.Web;
app.ClientType = OpenIddictConstants.ClientTypes.Public;
app.ClientSecret = null;
app.ClientUri = rootUrl;
app.RedirectUri = rootUrl;
app.PostLogoutRedirectUri = rootUrl;
app.LogoUri = "/images/clients/angular.svg";
app.ConsentType = OpenIddictConstants.ConsentTypes.Implicit;
app.DisplayName = "Console Test / Angular Application";
app.GrantTypes = new List<string>
{
OpenIddictConstants.GrantTypes.AuthorizationCode,
OpenIddictConstants.GrantTypes.Password,
OpenIddictConstants.GrantTypes.ClientCredentials,
OpenIddictConstants.GrantTypes.RefreshToken,
"LinkLogin",
"Impersonation"
};
});
options.AddApplication(configurationSection["OpenIddict_Swagger:ClientId"], app =>
{
var rootUrl = configurationSection["OpenIddict_Swagger:RootUrl"]?.TrimEnd('/');
app.ApplicationType = OpenIddictConstants.ApplicationTypes.Web;
app.ClientType = OpenIddictConstants.ClientTypes.Public;
app.ClientSecret = null;
app.ClientUri = rootUrl.EnsureEndsWith('/') + "swagger";
app.RedirectUri = $"{rootUrl}/swagger/oauth2-redirect.html";
app.LogoUri = "/images/clients/swagger.svg";
app.ConsentType = OpenIddictConstants.ConsentTypes.Implicit;
app.DisplayName = "Swagger Application";
app.GrantTypes = new List<string>
{
OpenIddictConstants.GrantTypes.AuthorizationCode
};
});
});
ASP.NET Core Module¶
This module integrates ASP NET Core, with built-in MVC controllers for four protocols. It uses OpenIddict's Pass-through mode.
The Account Management Module's GridLab.PSSX.Account.Public.Web.OpenIddict
package directly leverages the Volo.Abp.OpenIddict.AspNetCore
package, benefiting from its comprehensive features and capabilities.
This integration provides native ASP.NET Core MVC controllers for all major OpenID Connect endpoints, delivering a complete and production-ready authentication solution.
AuthorizeController -> connect/authorize
TokenController -> connect/token
LogoutController -> connect/logout
UserInfoController -> connect/userinfo
AbpOpenIddictAspNetCoreOptions¶
AbpOpenIddictAspNetCoreOptions
can be configured in the PreConfigureServices
method of your OpenIddict module.
Example:
PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
{
//Set options here...
});
AbpOpenIddictAspNetCoreOptions
properties:
UpdateAbpClaimTypes(default: true)
: UpdatesAbpClaimTypes
to be compatible with the Openiddict claims.AddDevelopmentEncryptionAndSigningCertificate(default: true)
: Registers (and generates if necessary) a user-specific development encryption/development signing certificate.
Automatically Removing Orphaned Tokens/Authorizations¶
The background task that automatically removes orphaned tokens/authorizations. This can be configured by TokenCleanupOptions
to manage it.
TokenCleanupOptions
can be configured in the PreConfigureServices
method of your OpenIddict module.
Example:
PreConfigure<TokenCleanupOptions>(options =>
{
//Set options here...
});
TokenCleanupOptions
properties:
IsCleanupEnabled
(default: true): Enable/disable token clean up.CleanupPeriod
(default: 3,600,000 ms): Setting clean up period.DisableAuthorizationPruning
: Setting a boolean indicating whether authorizations pruning should be disabled.DisableTokenPruning
: Setting a boolean indicating whether token pruning should be disabled.MinimumAuthorizationLifespan
(default: 14 days): Setting the minimum lifespan authorizations must have to be pruned. Cannot be less than 10 minutes.MinimumTokenLifespan
(default: 14 days): Setting the minimum lifespan tokens must have to be pruned. Cannot be less than 10 minutes.
Updating Claims In Access_token and Id_token¶
Claims Principal Factory can be used to add/remove claims to the ClaimsPrincipal
.
The AbpDefaultOpenIddictClaimDestinationsProvider
service will add Name
, Email,
and Role
types of Claims to access_token
and id_token
, other claims are only added to access_token
by default, and remove the SecurityStampClaimType
secret claim of Identity
.
Create a service that inherits from IAbpOpenIddictClaimDestinationsProvider
and add it to DI to fully control the destinations of claims.
public class MyClaimDestinationsProvider : IAbpOpenIddictClaimDestinationsProvider, ITransientDependency
{
public virtual Task SetDestinationsAsync(AbpOpenIddictClaimDestinationsProviderContext context)
{
// ...
return Task.CompletedTask;
}
}
Configure<AbpOpenIddictClaimDestinationsOptions>(options =>
{
options.ClaimDestinationsProvider.Add<MyClaimDestinationsProvider>();
});
For detailed information, please refer to: OpenIddict claim destinations
Disable AccessToken Encryption¶
ABP framework disables the access token encryption
by default for compatibility, it can be enabled manually if needed.
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.Configure(options => options.DisableAccessTokenEncryption = false);
});
}
Disable Transport Security Requirement¶
By default, OpenIddict requires the use of HTTPS for all endpoints. You can disable it if it's needed. You just need to configure the OpenIddictServerAspNetCoreOptions
and set DisableTransportSecurityRequirement
as true:
Configure<OpenIddictServerAspNetCoreOptions>(options =>
{
options.DisableTransportSecurityRequirement = true;
});
https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption
Request/Response Process¶
The OpenIddict.Server.AspNetCore
adds an authentication scheme(Name: OpenIddict.Server.AspNetCore, handler: OpenIddictServerAspNetCoreHandler
) and implements the IAuthenticationRequestHandler
interface.
It will be executed first in AuthenticationMiddleware
and can short-circuit the current request. Otherwise, DefaultAuthenticateScheme
will be called and continue to execute the pipeline.
OpenIddictServerAspNetCoreHandler
will call various built-in handlers (handling requests and responses), And the handler will process according to the context or skip logic that has nothing to do with it.
Example of a token request:
POST /connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=password&
client_id=OpenIddict_App&
client_secret=1q2w3e*&
username=platon&
password=1q2w3E*&
scope=OpenIddict offline_access
This request will be processed by various handlers. They will confirm the endpoint type of the request, check HTTP/HTTPS
, verify that the request parameters (client. scope, etc
) are valid and exist in the database, etc. Various protocol checks. And build a OpenIddictRequest
object, If there are any errors, the response content may be set and directly short-circuit the current request.
If everything is ok, the request will go to our processing controller(eg TokenController
), we can get an OpenIddictRequest
from the HTTP request at this time. The rest will be based on this object.
Check the username
and password
in the request. If it is correct create a ClaimsPrincipal
object and return a SignInResult
, which uses the OpenIddict.Validation.AspNetCore
authentication scheme name, will calls OpenIddictServerAspNetCoreHandler
for processing.
OpenIddictServerAspNetCoreHandler
do some checks to generate json and replace the http response content.
The ForbidResult
ChallengeResult
are all the above types of processing.
If you need to customize OpenIddict, you need to replace/delete/add new handlers and make it execute in the correct order.
Please refer to: https://documentation.openiddict.com/guides/index.html#events-model
PKCE¶
https://documentation.openiddict.com/configuration/proof-key-for-code-exchange.html
Setting Tokens Lifetime¶
Update PreConfigureServices
method of AuthServerModule (or HttpApiHostModule if you don't have tiered/separate-authserver) file:
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(30));
builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(30));
builder.SetIdentityTokenLifetime(TimeSpan.FromMinutes(30));
builder.SetRefreshTokenLifetime(TimeSpan.FromDays(14));
});
Refresh Token¶
To use refresh token, it must be supported by OpenIddictServer and the refresh_token
must be requested by the application.
Configuring OpenIddictServer¶
There are two ways to allow an application to use the refresh_token
.
- From OpenIddictDataSeedContributor, add
OpenIddictConstants.GrantTypes.RefreshToken
to grant types inCreateApplicationAsync
method:
await CreateApplicationAsync(
...
grantTypes: new List<string> //Hybrid flow
{
OpenIddictConstants.GrantTypes.AuthorizationCode,
OpenIddictConstants.GrantTypes.Implicit,
OpenIddictConstants.GrantTypes.RefreshToken,
},
...
Note: You need to re-create this client if you have generated the database already.
- Or from OpenIddict Management UI, edit your application and
Allow Refresh Token Flow
:
Note: Console / Angular application is already configured to use
refresh_token
.
Configuring Application:¶
You need to request the offline_access scope to be able to receive refresh_token
.
In Razor/MVC, Blazor-Server applications, add options.Scope.Add("offline_access");
to OpenIdConnect options. These application templates are using cookie authentication by default and has default cookie expire options set as:
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(365);
})
Cookie ExpireTimeSpan will ignore access_token expiration and expired access_token will still be valid if it is set to higher value than the refresh_token lifetime
. It is recommended to keep Cookie ExpireTimeSpan and the Refresh Token lifetime same, hence the new token will be persisted in the cookie.
In Blazor wasm applications, add options.ProviderOptions.DefaultScopes.Add("offline_access");
to AddOidcAuthentication options.
In Angular applications, add offline_access
to oAuthConfig scopes in environment.ts file. (Angular applications already have this configuration).