Add documentation for Entity Framework Core and MongoDB integrations
@@ -6,6 +6,7 @@ To protect the tokens it issues, OpenIddict uses 2 types of credentials:
|
|||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Tokens generated using the opt-in ASP.NET Core Data Protection integration rely on their own key ring, distinct from the credentials discussed in this documentation.
|
> Tokens generated using the opt-in ASP.NET Core Data Protection integration rely on their own key ring, distinct from the credentials discussed in this documentation.
|
||||||
|
>
|
||||||
> For more information about Data Protection, visit [ASP.NET Core Data Protection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction).
|
> For more information about Data Protection, visit [ASP.NET Core Data Protection](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/introduction).
|
||||||
|
|
||||||
## Registering credentials in the authorization server options
|
## Registering credentials in the authorization server options
|
||||||
@@ -35,6 +36,7 @@ services.AddOpenIddict()
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> `options.AddEphemeralEncryptionKey()` generates an asymmetric RSA key which is not directly used as-is to encrypt the tokens but is used to encrypt an
|
> `options.AddEphemeralEncryptionKey()` generates an asymmetric RSA key which is not directly used as-is to encrypt the tokens but is used to encrypt an
|
||||||
> intermediate *per-token* symmetric key with which the token content is first encrypted using [AES](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.6).
|
> intermediate *per-token* symmetric key with which the token content is first encrypted using [AES](https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.6).
|
||||||
|
>
|
||||||
> For more information about this mechanism, read [Key Encryption with RSAES OAEP](https://datatracker.ietf.org/doc/html/rfc7518#section-4.3).
|
> For more information about this mechanism, read [Key Encryption with RSAES OAEP](https://datatracker.ietf.org/doc/html/rfc7518#section-4.3).
|
||||||
|
|
||||||
### Registering a development certificate
|
### Registering a development certificate
|
||||||
@@ -110,7 +112,7 @@ var data = certificate.Export(X509ContentType.Pfx, string.Empty);
|
|||||||
The best place to store your certificates will depend on your host:
|
The best place to store your certificates will depend on your host:
|
||||||
- For IIS applications, storing the certificates in the machine store is the recommended option.
|
- For IIS applications, storing the certificates in the machine store is the recommended option.
|
||||||
- On Azure, certificates can be uploaded and exposed to Azure App Services applications using the special `WEBSITE_LOAD_CERTIFICATES` flag.
|
- On Azure, certificates can be uploaded and exposed to Azure App Services applications using the special `WEBSITE_LOAD_CERTIFICATES` flag.
|
||||||
For more information, visit https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate-in-code
|
For more information, visit [Use a TLS/SSL certificate in your code in Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate-in-code).
|
||||||
|
|
||||||
## Importing credentials in the API/resource validation options
|
## Importing credentials in the API/resource validation options
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# Configuration and settings
|
# Configuration
|
||||||
|
|
||||||
OpenIddict 3.0 comes with sensible defaults, but depending on the scenarios, the default settings can be amended to change how OpenIddict reacts to requests.
|
OpenIddict 3.0 comes with sensible defaults, but depending on the scenarios, the default settings can be amended to change how OpenIddict reacts to requests.
|
||||||
@@ -13,9 +13,6 @@
|
|||||||
- name: Encryption and signing credentials
|
- name: Encryption and signing credentials
|
||||||
href: encryption-and-signing-credentials.md
|
href: encryption-and-signing-credentials.md
|
||||||
|
|
||||||
- name: MongoDB integration
|
|
||||||
href: mongodb-integration.md
|
|
||||||
|
|
||||||
- name: Proof Key for Code Exchange
|
- name: Proof Key for Code Exchange
|
||||||
href: proof-key-for-code-exchange.md
|
href: proof-key-for-code-exchange.md
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -26,12 +26,11 @@ If you don't want to start from one of the recommended samples, you'll need to:
|
|||||||
|
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
{
|
{
|
||||||
// Configure the context to use Microsoft SQL Server.
|
// Configure Entity Framework Core to use Microsoft SQL Server.
|
||||||
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
|
||||||
// Register the entity sets needed by OpenIddict.
|
// Register the entity sets needed by OpenIddict.
|
||||||
// Note: use the generic overload if you need
|
// Note: use the generic overload if you need to replace the default OpenIddict entities.
|
||||||
// to replace the default OpenIddict entities.
|
|
||||||
options.UseOpenIddict();
|
options.UseOpenIddict();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -105,38 +104,18 @@ If you don't want to start from one of the recommended samples, you'll need to:
|
|||||||
```csharp
|
```csharp
|
||||||
services.AddDbContext<ApplicationDbContext>(options =>
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
{
|
{
|
||||||
// Configure the context to use Microsoft SQL Server.
|
// Configure Entity Framework Core to use Microsoft SQL Server.
|
||||||
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
|
||||||
// Register the entity sets needed by OpenIddict.
|
// Register the entity sets needed by OpenIddict.
|
||||||
// Note: use the generic overload if you need
|
// Note: use the generic overload if you need to replace the default OpenIddict entities.
|
||||||
// to replace the default OpenIddict entities.
|
|
||||||
options.UseOpenIddict();
|
options.UseOpenIddict();
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
> [!NOTE]
|
||||||
> If you change the default entity primary key (e.g. to `int` or `Guid` instead of `string`), make sure you use the `options.ReplaceDefaultEntities<TKey>()`
|
> By default, the OpenIddict Entity Framework Core integration uses `string` as the default type for primary keys.
|
||||||
> core extension accepting a `TKey` generic argument and use the generic `options.UseOpenIddict<TKey>()` overload to configure EF Core to use the specified type:
|
> To use a different type, read [Entity Framework Core integration : Use a custom primary key type](~/integrations/entity-framework-core.md#use-a-custom-primary-key-type).
|
||||||
>
|
|
||||||
> ```csharp
|
|
||||||
> services.AddOpenIddict()
|
|
||||||
> .AddCore(options =>
|
|
||||||
> {
|
|
||||||
> // Configure OpenIddict to use the default entities with a custom key type.
|
|
||||||
> options.UseEntityFrameworkCore()
|
|
||||||
> .UseDbContext<ApplicationDbContext>()
|
|
||||||
> .ReplaceDefaultEntities<Guid>();
|
|
||||||
> });
|
|
||||||
>
|
|
||||||
> services.AddDbContext<ApplicationDbContext>(options =>
|
|
||||||
> {
|
|
||||||
> // Configure the context to use Microsoft SQL Server.
|
|
||||||
> options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]);
|
|
||||||
>
|
|
||||||
> options.UseOpenIddict<Guid>();
|
|
||||||
> });
|
|
||||||
>```
|
|
||||||
|
|
||||||
- **Create your own authorization controller:**
|
- **Create your own authorization controller:**
|
||||||
Implementing a custom authorization controller is required to allow OpenIddict to create tokens based on the identities and claims you provide.
|
Implementing a custom authorization controller is required to allow OpenIddict to create tokens based on the identities and claims you provide.
|
||||||
4
index.md
@@ -14,7 +14,7 @@ OpenIddict natively supports **[Entity Framework Core](https://www.nuget.org/pac
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="panel panel-default" style="min-height: 120px;">
|
<div class="panel panel-default" style="min-height: 120px;">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p><strong><a href="guide/index.md">Introduction</a></strong></p>
|
<p><strong><a href="guides/index.md">Introduction</a></strong></p>
|
||||||
<p>Read an introduction on OpenIddict and the reason it was created.</p>
|
<p>Read an introduction on OpenIddict and the reason it was created.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -22,7 +22,7 @@ OpenIddict natively supports **[Entity Framework Core](https://www.nuget.org/pac
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="panel panel-default" style="min-height: 120px;">
|
<div class="panel panel-default" style="min-height: 120px;">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<p><strong><a href="guide/getting-started.md">Getting started</a></strong></p>
|
<p><strong><a href="guides/getting-started.md">Getting started</a></strong></p>
|
||||||
<p>Get started quickly by working through this step-by-step guide.</p>
|
<p>Get started quickly by working through this step-by-step guide.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
135
integrations/entity-framework-core.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Entity Framework Core integration
|
||||||
|
|
||||||
|
## Basic configuration
|
||||||
|
|
||||||
|
To configure OpenIddict to use Entity Framework Core as the database for applications, authorizations, scopes and tokens, you'll need to:
|
||||||
|
- **Reference the `OpenIddict.EntityFrameworkCore` package**:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.1.1" />
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Create a database context deriving from `DbContext` (or `IdentityDbContext` when using ASP.NET Core Identity)**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ApplicationDbContext : DbContext
|
||||||
|
{
|
||||||
|
public ApplicationDbContext(DbContextOptions options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Configure OpenIddict to use the Entity Framework Core stores**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddOpenIddict()
|
||||||
|
.AddCore(options =>
|
||||||
|
{
|
||||||
|
options.UseEntityFrameworkCore()
|
||||||
|
.UseDbContext<ApplicationDbContext>();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Configure Entity Framework Core to register the OpenIddict entities in the model**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
{
|
||||||
|
// Configure the Entity Framework Core to use Microsoft SQL Server.
|
||||||
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
|
||||||
|
// Register the entity sets needed by OpenIddict.
|
||||||
|
options.UseOpenIddict();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Use migrations or recreate the database to add the OpenIddict entities**.
|
||||||
|
For more information, read [Migrations Overview](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/).
|
||||||
|
|
||||||
|
## Advanced configuration
|
||||||
|
|
||||||
|
### Use a custom primary key type
|
||||||
|
|
||||||
|
By default, the Entity Framework Core integration uses `string` primary keys, which matches the default key type used by ASP.NET Core Identity.
|
||||||
|
|
||||||
|
To use a different key type (e.g `int`, `long` or `Guid`):
|
||||||
|
- **Call the generic `ReplaceDefaultEntities<TKey>()` method to force OpenIddict to use the default entities with the specified key type**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddOpenIddict()
|
||||||
|
.AddCore(options =>
|
||||||
|
{
|
||||||
|
// Configure OpenIddict to use the default entities with a custom key type.
|
||||||
|
options.UseEntityFrameworkCore()
|
||||||
|
.UseDbContext<ApplicationDbContext>()
|
||||||
|
.ReplaceDefaultEntities<Guid>();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Configure Entity Framework Core to include the default entities with the chosen key type in the model**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
{
|
||||||
|
// Configure Entity Framework Core to use Microsoft SQL Server.
|
||||||
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
|
||||||
|
// Register the entity sets needed by OpenIddict but use a custom key type.
|
||||||
|
options.UseOpenIddict<Guid>();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use custom entities
|
||||||
|
|
||||||
|
For applications that require storing additional data alongside the properties used by OpenIddict, custom entities can be used. For that, you need to:
|
||||||
|
- **Create custom entities**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class CustomApplication : OpenIddictEntityFrameworkCoreApplication<long, CustomAuthorization, CustomToken>
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomAuthorization : OpenIddictEntityFrameworkCoreAuthorization<long, CustomApplication, CustomToken>
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomScope : OpenIddictEntityFrameworkCoreScope<long>
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomToken : OpenIddictEntityFrameworkCoreToken<long, CustomApplication, CustomAuthorization>
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Call the generic `ReplaceDefaultEntities<TApplication, TAuthorization, TScope, TToken, TKey>()` method to force OpenIddict to use the custom entities**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddOpenIddict()
|
||||||
|
.AddCore(options =>
|
||||||
|
{
|
||||||
|
// Configure OpenIddict to use the custom entities.
|
||||||
|
options.UseEntityFrameworkCore()
|
||||||
|
.UseDbContext<ApplicationDbContext>()
|
||||||
|
.ReplaceDefaultEntities<CustomApplication, CustomAuthorization, CustomScope, CustomToken, long>();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Configure Entity Framework Core to include the custom entities in the model**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
|
{
|
||||||
|
// Configure Entity Framework Core to use Microsoft SQL Server.
|
||||||
|
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
|
||||||
|
|
||||||
|
// Register the entity sets needed by OpenIddict but use the specified entities instead of the default ones.
|
||||||
|
options.UseOpenIddict<CustomApplication, CustomAuthorization, CustomScope, CustomToken, long>();
|
||||||
|
});
|
||||||
|
```
|
||||||
6
integrations/index.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Integrations
|
||||||
|
|
||||||
|
OpenIddict offers native integrations with popular Object–relational Mapping tools and Database Clients like Entity Framework 6 and Entity Framework Core or MongoDB.
|
||||||
|
|
||||||
|
Documentation for integrations maintained by the OpenIddict community can be found in their respective repositories:
|
||||||
|
- **[P41.OpenIddict.CouchDB](https://github.com/panoukos41/couchdb-openiddict)** by [Panos Athanasiou](https://github.com/panoukos41): CouchDB stores for OpenIddict.
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
# MongoDB integration
|
# MongoDB integration
|
||||||
|
|
||||||
|
## Basic configuration
|
||||||
|
|
||||||
To configure OpenIddict to use MongoDB as the database for applications, authorizations, scopes and tokens, you'll need to:
|
To configure OpenIddict to use MongoDB as the database for applications, authorizations, scopes and tokens, you'll need to:
|
||||||
- **Reference the `OpenIddict.MongoDb` package**:
|
- **Reference the `OpenIddict.MongoDb` package**:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<PackageReference Include="OpenIddict.MongoDb" Version="3.1.1" />
|
<PackageReference Include="OpenIddict.MongoDb" Version="3.1.1" />
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Configure OpenIddict to use the MongoDB stores**:
|
- **Configure OpenIddict to use the MongoDB stores**:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
services.AddOpenIddict()
|
services.AddOpenIddict()
|
||||||
.AddCore(options =>
|
.AddCore(options =>
|
||||||
@@ -34,6 +38,7 @@ To configure OpenIddict to use MongoDB as the database for applications, authori
|
|||||||
|
|
||||||
- **Create indexes to improve performance** (recommended): for that, you can use the following script to
|
- **Create indexes to improve performance** (recommended): for that, you can use the following script to
|
||||||
initialize the database and create the indexes used by the OpenIddict entities:
|
initialize the database and create the indexes used by the OpenIddict entities:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -113,8 +118,7 @@ initialize the database and create the indexes used by the OpenIddict entities:
|
|||||||
{
|
{
|
||||||
// Note: partial filter expressions are not supported on Azure Cosmos DB.
|
// Note: partial filter expressions are not supported on Azure Cosmos DB.
|
||||||
// As a workaround, the expression and the unique constraint can be removed.
|
// As a workaround, the expression and the unique constraint can be removed.
|
||||||
PartialFilterExpression =
|
PartialFilterExpression = Builders<OpenIddictMongoDbToken>.Filter.Exists(token => token.ReferenceId),
|
||||||
Builders<OpenIddictMongoDbToken>.Filter.Exists(token => token.ReferenceId),
|
|
||||||
Unique = true
|
Unique = true
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -130,3 +134,63 @@ initialize the database and create the indexes used by the OpenIddict entities:
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Advanced configuration
|
||||||
|
|
||||||
|
### Use custom entities
|
||||||
|
|
||||||
|
For applications that require storing additional data alongside the properties used by OpenIddict, custom entities can be used. For that, you need to:
|
||||||
|
- **Create custom entities**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class CustomApplication : OpenIddictMongoDbApplication
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomAuthorization : OpenIddictMongoDbAuthorization
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomScope : OpenIddictMongoDbScope
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomToken : OpenIddictMongoDbToken
|
||||||
|
{
|
||||||
|
public string CustomProperty { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Configure MongoDb to use the custom entities**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddOpenIddict()
|
||||||
|
.AddCore(options =>
|
||||||
|
{
|
||||||
|
options.UseMongoDb()
|
||||||
|
.ReplaceDefaultApplicationEntity<CustomApplication>()
|
||||||
|
.ReplaceDefaultAuthorizationEntity<CustomAuthorization>()
|
||||||
|
.ReplaceDefaultScopeEntity<CustomScope>()
|
||||||
|
.ReplaceDefaultTokenEntity<CustomToken>();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use custom collection names
|
||||||
|
|
||||||
|
By default, OpenIddict uses the `openiddict.[entity name]s` pattern to determine the default collection names.
|
||||||
|
Applications that require using different collection names can use the `Set*CollectionName()` helpers:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddOpenIddict()
|
||||||
|
.AddCore(options =>
|
||||||
|
{
|
||||||
|
options.UseMongoDb()
|
||||||
|
.SetApplicationsCollectionName("custom-applications-collection")
|
||||||
|
.SetAuthorizationsCollectionName("custom-authorizations-collection")
|
||||||
|
.SetScopesCollectionName("custom-scopes-collection")
|
||||||
|
.SetTokensCollectionName("custom-tokens-collection");
|
||||||
|
});
|
||||||
|
```
|
||||||
8
integrations/toc.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
- name: Introduction
|
||||||
|
href: index.md
|
||||||
|
|
||||||
|
- name: Entity Framework Core
|
||||||
|
href: entity-framework-core.md
|
||||||
|
|
||||||
|
- name: MongoDB
|
||||||
|
href: mongodb.md
|
||||||
13
toc.yml
@@ -1,11 +1,14 @@
|
|||||||
- name: User guides
|
- name: User guides
|
||||||
href: guide/index.md
|
href: guides/
|
||||||
|
|
||||||
- name: Configuration and settings
|
- name: Configuration
|
||||||
href: configuration/index.md
|
href: configuration/
|
||||||
|
|
||||||
- name: Samples
|
- name: Integrations
|
||||||
|
href: integrations/
|
||||||
|
|
||||||
|
- name: Samples (external)
|
||||||
href: https://github.com/openiddict/openiddict-samples
|
href: https://github.com/openiddict/openiddict-samples
|
||||||
|
|
||||||
- name: Changelog
|
- name: Changelog (external)
|
||||||
href: https://github.com/openiddict/openiddict-core/releases
|
href: https://github.com/openiddict/openiddict-core/releases
|
||||||