Tweak the documentation routes and revamp the menus

This commit is contained in:
Kévin Chalet
2018-09-13 13:48:01 +02:00
parent aedcc6a70a
commit 87725369a5
13 changed files with 50 additions and 23 deletions

View File

@@ -0,0 +1,191 @@
# Application permissions
Starting with RC2, OpenIddict includes a built-in feature codenamed "application permissions" that
**allows controlling and limiting the OAuth2/OpenID Connect features a client application is able to use**.
3 categories of permissions are currently supported:
- Endpoint permissions
- Grant type/flow permissions
- Scope permissions.
> [!WARNING]
> Note: **prior to OpenIddict RC3, application permissions were mostly optional** and OpenIddict had a fallback mechanism
> called "implicit permissions" it used to determine whether an application could perform the requested action.
>
> If no permission was explicitly attached to the application, it was considered fully trusted and was granted all the permissions.
> Similarly, if you granted the "token endpoint" permission to an application but NO "grant type" permission,
> it was assumed the client application was allowed to use the password or client credentials grants.
>
> Retrospectively, this logic was too complex and it removed in RC3 and **application permissions MUST now be explicitly granted**.
## Endpoint permissions
### Definition
Endpoint permissions limit the endpoints a client application can use.
### Supported permissions
| Endpoint | Constant |
|:---------------------------:|:---------------------------------------------------------:|
| Authorization endpoint | `OpenIddictConstants.Permissions.Endpoints.Authorization` |
| Introspection endpoint | `OpenIddictConstants.Permissions.Endpoints.Introspection` |
| Logout/end session endpoint | `OpenIddictConstants.Permissions.Endpoints.Logout` |
| Revocation endpoint | `OpenIddictConstants.Permissions.Endpoints.Revocation` |
| Token endpoint | `OpenIddictConstants.Permissions.Endpoints.Token` |
### Example
In the following example, the `mvc` application is allowed to use the authorization, logout and
token endpoints but will get an error when trying to send an introspection or revocation request:
```csharp
if (await manager.FindByClientIdAsync("mvc") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
DisplayName = "MVC client application",
PostLogoutRedirectUris = { new Uri("http://localhost:53507/signout-callback-oidc") },
RedirectUris = { new Uri("http://localhost:53507/signin-oidc") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Logout,
OpenIddictConstants.Permissions.Endpoints.Token
}
});
}
```
### Disabling endpoint permissions
If you don't want to use endpoint permissions, call `options.IgnoreEndpointPermissions()` to ignore them:
```csharp
services.AddOpenIddict()
.AddServer(options =>
{
options.IgnoreEndpointPermissions();
});
```
## Grant type permissions
### Definition
Grant type permissions limit the flows a client application is allowed to use.
### Supported permissions
| Grant type | Constant |
|:-----------------------:|:--------------------------------------------------------------:|
| Authorization code flow | `OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode` |
| Client credentials flow | `OpenIddictConstants.Permissions.GrantTypes.ClientCredentials` |
| Implicit flow | `OpenIddictConstants.Permissions.GrantTypes.Implicit` |
| Password flow | `OpenIddictConstants.Permissions.GrantTypes.Password` |
| Refresh token flow | `OpenIddictConstants.Permissions.GrantTypes.RefreshToken` |
To add a custom flow permission, you can use the following pattern:
```csharp
OpenIddictConstants.Permissions.Prefixes.GrantType + "custom_flow_name"
```
### Example
In the following example, the `postman` application can only use the authorization code flow
while `console` is restricted to the `password` and `refresh_token` flows:
```csharp
if (await manager.FindByClientIdAsync("postman") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "postman",
DisplayName = "Postman",
RedirectUris = { new Uri("https://www.getpostman.com/oauth2/callback") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode
}
});
}
if (await manager.FindByClientIdAsync("console") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "console",
DisplayName = "Console",
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.Password,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken
}
});
}
```
### Disabling grant type permissions
If you don't want to use grant type permissions, call `options.IgnoreGrantTypePermissions()` to ignore them:
```csharp
services.AddOpenIddict()
.AddServer(options =>
{
options.IgnoreGrantTypePermissions();
});
```
## Scope permissions
### Definition
Scope permissions limit the scopes (standard or custom) a client application is allowed to use.
> The `openid` and `offline_access` scopes are special-cased by OpenIddict and don't require explicit permissions.
### Example
In the following sample, the `angular` client is allowed to request the `address`,
`profile` and `marketing_api` scopes: any other scope will result in an error being returned.
```csharp
if (await manager.FindByClientIdAsync("angular") == null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ClientId = "angular",
DisplayName = "Angular",
RedirectUris = { new Uri("https://localhost:34422/callback") },
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.GrantTypes.Implicit,
OpenIddictConstants.Permissions.Scopes.Address,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Prefixes.Scope + "marketing_api"
}
});
}
```
### Disabling scope permissions
If you don't want to use scope permissions, call `options.IgnoreScopePermissions()` to ignore them:
```csharp
services.AddOpenIddict()
.AddServer(options =>
{
options.IgnoreScopePermissions();
});
```

20
configuration/index.md Normal file
View File

@@ -0,0 +1,20 @@
# Configuration and settings
<div class="row">
<div class="col-md-4">
<div class="panel panel-default" style="min-height: 120px;">
<div class="panel-body">
<p><strong><a href="application-permissions.md">Application permissions</a></strong></p>
<p>Learn how to leverage permissions to control the OIDC features a client is allowed to use.</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default" style="min-height: 120px;">
<div class="panel-body">
<p><strong><a href="token-setup-and-validation.md">Token setup and API validation</a></strong></p>
<p>Learn how to change the default token format and register the API token validation components.</p>
</div>
</div>
</div>
</div>

8
configuration/toc.yml Normal file
View File

@@ -0,0 +1,8 @@
- name: Introduction
href: index.md
- name: Token setup and API validation
href: token-setup-and-validation.md
- name: Application permissions
href: application-permissions.md

View File

@@ -0,0 +1,404 @@
# Token setup and validation
For an overview of the different token formats, see: [here](../guide/token-formats.md)
In OpenID Connect there are three types of tokens: access tokens, id tokens, and refresh tokens [See spec](https://openid.net/specs/openid-connect-core-1_0.html#Introduction). When this guide refers to _tokens_ it is referring to access tokens.
Authorization servers are responsible for token generation. Clients (server app or web app) request tokens and then use tokens to request resources. For example, a javascript web application may make API calls that require authorization, so the token is sent along in the header of every request.
Token validation needs to be configured for servers that have API endpoints that require authorization. This could be authorization servers or standalone servers, called resource servers. An example authorization server API endpoint may be '/api/User' that returns the current user. A resource server API endpoint for a note-taking app may be '/notes' that returns the user's notes.
Below shows code snippets for token generation and token validation for each token format.
# Default configuration: Opaque tokens
## Default token generation
### Authorization server
```csharp
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//If tokens need to be validated in a separate resource server, configure a shared ASP.NET Core DataProtection with shared key store and application name
//See https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-2.1&tabs=aspnetcore2x#setapplicationname
services.AddDataProtection()
.PersistKeysToFileSystem(new System.IO.DirectoryInfo(@"[UNC PATH]"))
.SetApplicationName("[APP NAME]");
// Register the OpenIddict services.
// Additional configuration is only needed if using Introspection on resource servers
services.AddOpenIddict()
.AddCore(...)
.AddServer(options =>
{
//...
//This is required if using introspection on resource servers
//This is not needed if resource servers will use shared ASP.NET Core DataProtection
//options.EnableIntrospectionEndpoint("/connect/introspect");
})
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
## Default token validation
### Authorization server
```csharp
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenIddict()
.AddCore(...)
.AddServer(...)
.AddValidation();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Resource server shared ASP.NET Core DataProtection
```csharp
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//If tokens need to be validated in a separate resource server, configure a shared ASP.NET Core DataProtection with shared key store and application name
//See https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-2.1&tabs=aspnetcore2x#setapplicationname
services.AddDataProtection()
.PersistKeysToFileSystem(new System.IO.DirectoryInfo(@"[UNC PATH]"))
.SetApplicationName("[APP NAME]");
services.AddOpenIddict()
//This adds a "Bearer" authentication scheme
.AddValidation();
//Optionally set Bearer token authentication as default
//services.AddAuthentication(options =>
//{
// options.DefaultAuthenticateScheme = OpenIddictValidationDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = OpenIddictValidationDefaults.AuthenticationScheme;
//});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Resource server - introspection
```csharp
// Introspection requires a request to auth server for every token so shared ASP.NET Core DataProtection is preferred.
// To use introspection, you need to create a new client application and grant it the introspection endpoint permission.
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = OAuthIntrospectionDefaults.AuthenticationScheme;
})
.AddOAuthIntrospection(options =>
{
//example settings
options.Authority = new Uri("http://localhost:12345/");
options.Audiences.Add("resource-server-1");
options.ClientId = "resource-server-1";
options.ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342";
options.RequireHttpsMetadata = false;
// Note: you can override the default name and role claims:
// options.NameClaimType = "custom_name_claim";
// options.RoleClaimType = "custom_role_claim";
});
//Optionally set Bearer token authentication as default
//services.AddAuthentication(options =>
//{
// options.DefaultAuthenticateScheme = OAuthIntrospectionDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = OAuthIntrospectionDefaults.AuthenticationScheme;
//});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Api controller
```csharp
//specify "Bearer" authentication scheme if it's not set as default
[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
//or if using introspection on resource server:
// [Authorize(AuthenticationSchemes = OAuthIntrospectionDefaults.AuthenticationScheme)]
public class MyController : Controller
```
# Reference token format
## Reference token generation
### Authorization server
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Register OpenIddict stores
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
// Register the OpenIddict services.
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>();
})
.AddServer(options =>
{
//...
options.UseReferenceTokens();
});
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
## Reference token validation
### Authorization server
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenIddict()
.AddCore(...) //see above
.AddServer(...) // see above
.AddValidation(options =>
{
options.UseReferenceTokens();
});
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Resource server
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Register OpenIddict stores
services.AddDbContext<AuthServerDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("AuthServerConnection"));
options.UseOpenIddict();
});
services.AddOpenIddict()
.AddCore(options =>
{
// Register the Entity Framework entities and stores.
options.UseEntityFrameworkCore()
.UseDbContext<AuthServerDbContext>();
})
//This adds a "Bearer" authentication scheme
.AddValidation(options =>
{
options.UseReferenceTokens();
});
//Optionally set Bearer token authentication as default
//services.AddAuthentication(options =>
//{
// options.DefaultAuthenticateScheme = OpenIddictValidationDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = OpenIddictValidationDefaults.AuthenticationScheme;
//});
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Api controller
```c#
// Note: both OpenIddictValidationDefaults.AuthenticationScheme and JwtBearerDefaults.AuthenticationScheme are "Bearer"
//If you did not set the default authentication scheme then specify it here.
//If you get a 302 redirect to login page instead of a 401 Unauthorized then Cookie authentication is handling the request
//so scheme must be specified
[Authorize(AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
public class MyController : Controller
```
# JWTs
## JWT generation
### Authorization server
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenIddict()
.AddCore(...)
.AddServer(options =>
{
//...
options.UseJsonWebTokens();
//JWTs must be signed by a self-signing certificate or a symmetric key
//Here a certificate is used. I used IIS to create a self-signed certificate
//and saved it in /FolderName folder. See below for .csproj configuration
options.AddSigningCertificate(
assembly: typeof(Startup).GetTypeInfo().Assembly,
resource: "AppName.FolderName.certname.pfx",
password: "anypassword");
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
// .csproj
// if using a certificate and its stored in your app's Resources then make sure it's published
//<ItemGroup>
// <EmbeddedResource Include="FolderName\certname.pfx" />
// </ItemGroup>
```
## JWT validation
### Authorization server
> [!WARNING]
> Remember, this is only needed if you have API endpoints that require token authorization. If your authorization server generates tokens that are only used by separate resource servers, then this is not needed.
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//this must come after registering ASP.NET Core Identity
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddJwtBearer(options =>
{
//Authority must be a url. It does not have a default value.
options.Authority = "this server's url, e.g. http://localhost:5051/ or https://auth.example.com/";
options.Audience = "example: auth_server_api"; //This must be included in ticket creation
options.RequireHttpsMetadata = false;
options.IncludeErrorDetails = true; //
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role,
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...
//must come before using MVC
app.UseAuthentication();
//...
}
```
### Resource server
```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
//Add authentication and set default authentication scheme
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) //same as "Bearer"
.AddJwtBearer(options =>
{
//Authority must be a url. It does not have a default value.
options.Authority = "auth server's url, e.g. http://localhost:5051/ or https://auth.example.com/";
options.Audience = "example: api_server_1"; //This must be included in ticket creation
options.RequireHttpsMetadata = false;
options.IncludeErrorDetails = true; //
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role,
};
});
}
```
### Api controller
```c#
// Note: both OpenIddictValidationDefaults.AuthenticationScheme and JwtBearerDefaults.AuthenticationScheme are "Bearer"
//If you didn't set the default authentication scheme then specify it here.
//If you get a 302 redirect to login page instead of a 401 Unauthorized then Cookie authentication is handling the request
//so scheme must be specified
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class MyController : Controller
```