Browse Source

Introduce a list of supported grant types

pull/161/head
Kévin Chalet 10 years ago
parent
commit
05a69e3131
  1. 5
      samples/Mvc.Server/Startup.cs
  2. 47
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  3. 20
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Discovery.cs
  4. 63
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
  5. 55
      src/OpenIddict.Core/OpenIddictBuilder.cs
  6. 36
      src/OpenIddict.Core/OpenIddictExtensions.cs
  7. 5
      src/OpenIddict.Core/OpenIddictOptions.cs

5
samples/Mvc.Server/Startup.cs

@ -38,6 +38,11 @@ namespace Mvc.Server {
.EnableTokenEndpoint("/connect/token") .EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/connect/userinfo") .EnableUserinfoEndpoint("/connect/userinfo")
// Note: the Mvc.Client sample only uses the authorization code flow but you can enable
// the other flows if you need to support implicit, password or client credentials.
.AllowAuthorizationCodeFlow()
.AllowRefreshTokenFlow()
// During development, you can disable the HTTPS requirement. // During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement(); .DisableHttpsRequirement();

47
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -88,6 +88,43 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
// Reject code flow authorization requests if the authorization code flow is not enabled.
if (context.Request.IsAuthorizationCodeFlow() &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode)) {
services.Logger.LogError("The authorization request was rejected because " +
"the authorization code flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedResponseType,
description: "The specified response_type parameter is not allowed.");
return;
}
// Reject implicit flow authorization requests if the implicit flow is not enabled.
if (context.Request.IsImplicitFlow() && !services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)) {
services.Logger.LogError("The authorization request was rejected because the implicit flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedResponseType,
description: "The specified response_type parameter is not allowed.");
return;
}
// Reject hybrid flow authorization requests if the authorization code or the implicit flows are not enabled.
if (context.Request.IsHybridFlow() && (!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
services.Logger.LogError("The authorization request was rejected because the " +
"authorization code flow or the implicit flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedResponseType,
description: "The specified response_type parameter is not allowed.");
return;
}
// Note: the OpenID Connect server middleware supports the query, form_post and fragment response modes // Note: the OpenID Connect server middleware supports the query, form_post and fragment response modes
// and doesn't reject unknown/custom modes until the ApplyAuthorizationResponse event is invoked. // and doesn't reject unknown/custom modes until the ApplyAuthorizationResponse event is invoked.
// To ensure authorization requests are rejected early enough, an additional check is made by OpenIddict. // To ensure authorization requests are rejected early enough, an additional check is made by OpenIddict.
@ -116,6 +153,16 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
// Reject authorization requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed.");
return;
}
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByClientIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {

20
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Discovery.cs

@ -4,6 +4,7 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
@ -16,6 +17,25 @@ namespace OpenIddict.Infrastructure {
public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) { public override Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) {
var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>(); var services = context.HttpContext.RequestServices.GetRequiredService<OpenIddictServices<TUser, TApplication, TAuthorization, TScope, TToken>>();
Debug.Assert(services.Options.GrantTypes.Count != 0, "At least one flow should be enabled.");
// Note: the OpenID Connect server middleware automatically populates grant_types_supported
// by determining whether the authorization and token endpoints are enabled or not but
// OpenIddict uses a different approach and relies on a configurable "supported list".
context.GrantTypes.Clear();
// Copy the supported grant types list to the discovery document.
foreach (var type in services.Options.GrantTypes) {
Debug.Assert(type == OpenIdConnectConstants.GrantTypes.AuthorizationCode ||
type == OpenIdConnectConstants.GrantTypes.ClientCredentials ||
type == OpenIdConnectConstants.GrantTypes.Implicit ||
type == OpenIdConnectConstants.GrantTypes.Password ||
type == OpenIdConnectConstants.GrantTypes.RefreshToken,
"Unsupported or non-standard OAuth2/OIDC grant types should not be exposed.");
context.GrantTypes.Add(type);
}
// Note: the "openid" scope is automatically // Note: the "openid" scope is automatically
// added by the OpenID Connect server middleware. // added by the OpenID Connect server middleware.
context.Scopes.Add(OpenIdConnectConstants.Scopes.Profile); context.Scopes.Add(OpenIdConnectConstants.Scopes.Profile);

63
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs

@ -37,6 +37,69 @@ namespace OpenIddict.Infrastructure {
return; return;
} }
// Reject token requests using grant_type=authorization_code
// if the authorization code flow support is not enabled.
if (context.Request.IsAuthorizationCodeGrantType() &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode)) {
services.Logger.LogError("The token request was rejected because the authorization code flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "The specified grant_type parameter is not allowed.");
return;
}
// Reject token requests using grant_type=client_credentials
// if the client credentials flow support is not enabled.
else if (context.Request.IsClientCredentialsGrantType() &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials)) {
services.Logger.LogError("The token request was rejected because the client credentials flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "The specified grant_type parameter is not allowed.");
return;
}
// Reject token requests using grant_type=password if the
// resource owner password credentials flow support is not enabled.
else if (context.Request.IsPasswordGrantType() &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Password)) {
services.Logger.LogError("The token request was rejected because the resource " +
"owner password credentials flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "The specified grant_type parameter is not allowed.");
return;
}
// Reject token requests using grant_type=refresh_token
// if the refresh token flow support is not enabled.
else if (context.Request.IsRefreshTokenGrantType() &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
services.Logger.LogError("The token request was rejected because the refresh token flow was not enabled.");
context.Reject(
error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
description: "The specified grant_type parameter is not allowed.");
return;
}
// Reject token requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!services.Options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed.");
return;
}
// Note: the OpenID Connect server middleware allows returning a refresh token with grant_type=client_credentials, // Note: the OpenID Connect server middleware allows returning a refresh token with grant_type=client_credentials,
// though it's usually not recommended by the OAuth2 specification. To encourage developers to make a new // though it's usually not recommended by the OAuth2 specification. To encourage developers to make a new
// grant_type=client_credentials request instead of using refresh tokens, OpenIddict uses a stricter policy // grant_type=client_credentials request instead of using refresh tokens, OpenIddict uses a stricter policy

55
src/OpenIddict.Core/OpenIddictBuilder.cs

@ -11,6 +11,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using AspNet.Security.OpenIdConnect.Extensions;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -391,6 +392,60 @@ namespace Microsoft.AspNetCore.Builder {
return Configure(options => options.SigningCredentials.AddCertificate(thumbprint, name, location)); return Configure(options => options.SigningCredentials.AddCertificate(thumbprint, name, location));
} }
/// <summary>
/// Enables authorization code flow support. For more information
/// about this specific OAuth2/OpenID Connect flow, visit
/// https://tools.ietf.org/html/rfc6749#section-4.1 and
/// http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AllowAuthorizationCodeFlow() {
return Configure(options => options.GrantTypes.Add(
OpenIdConnectConstants.GrantTypes.AuthorizationCode));
}
/// <summary>
/// Enables client credentials flow support. For more information about this
/// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.4.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AllowClientCredentialsFlow() {
return Configure(options => options.GrantTypes.Add(
OpenIdConnectConstants.GrantTypes.ClientCredentials));
}
/// <summary>
/// Enables implicit flow support. For more information
/// about this specific OAuth2/OpenID Connect flow, visit
/// https://tools.ietf.org/html/rfc6749#section-4.2 and
/// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AllowImplicitFlow() {
return Configure(options => options.GrantTypes.Add(
OpenIdConnectConstants.GrantTypes.Implicit));
}
/// <summary>
/// Enables password flow support. For more information about this specific
/// OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.3.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AllowPasswordFlow() {
return Configure(options => options.GrantTypes.Add(
OpenIdConnectConstants.GrantTypes.Password));
}
/// <summary>
/// Enables refresh token flow support. For more information about this
/// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-6.
/// </summary>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public virtual OpenIddictBuilder AllowRefreshTokenFlow() {
return Configure(options => options.GrantTypes.Add(
OpenIdConnectConstants.GrantTypes.RefreshToken));
}
/// <summary> /// <summary>
/// Disables the HTTPS requirement during development. /// Disables the HTTPS requirement during development.
/// </summary> /// </summary>

36
src/OpenIddict.Core/OpenIddictExtensions.cs

@ -6,6 +6,7 @@
using System; using System;
using System.Linq; using System.Linq;
using AspNet.Security.OpenIdConnect.Extensions;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -92,6 +93,41 @@ namespace Microsoft.AspNetCore.Builder {
options.Cache = app.ApplicationServices.GetRequiredService<IDistributedCache>(); options.Cache = app.ApplicationServices.GetRequiredService<IDistributedCache>();
} }
// Ensure at least one flow has been enabled.
if (options.GrantTypes.Count == 0) {
throw new InvalidOperationException("At least one OAuth2/OpenID Connect flow must be enabled.");
}
// Ensure only supported grant types are listed to prevent
// unknown flows from being exposed in the discovery document.
if (options.GrantTypes.Any(type => type != OpenIdConnectConstants.GrantTypes.AuthorizationCode &&
type != OpenIdConnectConstants.GrantTypes.ClientCredentials &&
type != OpenIdConnectConstants.GrantTypes.Implicit &&
type != OpenIdConnectConstants.GrantTypes.Password &&
type != OpenIdConnectConstants.GrantTypes.RefreshToken)) {
throw new InvalidOperationException("Only supported flows can be enabled.");
}
// Ensure the authorization endpoint has been enabled when
// the authorization code or implicit grants are supported.
if (!options.AuthorizationEndpointPath.HasValue &&
(options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
throw new InvalidOperationException("The authorization endpoint must be enabled to use " +
"the authorization code and implicit flows.");
}
// Ensure the token endpoint has been enabled when the authorization code,
// client credentials, password or refresh token grants are supported.
else if (!options.TokenEndpointPath.HasValue &&
(options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Password) ||
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))) {
throw new InvalidOperationException("The token endpoint must be enabled to use the authorization code, " +
"client credentials, password and refresh token flows.");
}
// Get the modules registered by the application // Get the modules registered by the application
// and add the OpenID Connect server middleware. // and add the OpenID Connect server middleware.
var modules = options.Modules.ToList(); var modules = options.Modules.ToList();

5
src/OpenIddict.Core/OpenIddictOptions.cs

@ -30,6 +30,11 @@ namespace OpenIddict {
/// </summary> /// </summary>
public IDistributedCache Cache { get; set; } public IDistributedCache Cache { get; set; }
/// <summary>
/// Gets the OAuth2/OpenID Connect flows enabled for this application.
/// </summary>
public ICollection<string> GrantTypes { get; } = new HashSet<string>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets the list of the OpenIddict modules registered in the application. /// Gets the list of the OpenIddict modules registered in the application.
/// </summary> /// </summary>

Loading…
Cancel
Save