Browse Source

Relax the default signing credentials policy to allow using OpenIddict in degraded mode

pull/346/head
Kévin Chalet 9 years ago
parent
commit
ea08d4d894
  1. 23
      samples/Mvc.Server/Startup.cs
  2. 111
      src/OpenIddict/OpenIddictExtensions.cs
  3. 13
      src/OpenIddict/OpenIddictProvider.Authentication.cs
  4. 5
      src/OpenIddict/OpenIddictProvider.Discovery.cs
  5. 4
      src/OpenIddict/OpenIddictProvider.Exchange.cs
  6. 129
      test/OpenIddict.Tests/OpenIddictExtensionsTests.cs
  7. 7
      test/OpenIddict.Tests/OpenIddictProviderTests.cs

23
samples/Mvc.Server/Startup.cs

@ -70,28 +70,7 @@ namespace Mvc.Server {
// is redirected to the same page with a single parameter (request_id). // is redirected to the same page with a single parameter (request_id).
// This allows flowing large OpenID Connect requests even when using // This allows flowing large OpenID Connect requests even when using
// an external authentication provider like Google, Facebook or Twitter. // an external authentication provider like Google, Facebook or Twitter.
.EnableRequestCaching() .EnableRequestCaching();
// Register a new ephemeral key, that is discarded when the application
// shuts down. Tokens signed using this key are automatically invalidated.
// This method should only be used during development.
.AddEphemeralSigningKey();
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
// services.AddOpenIddict()
// .AddSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
//
// Alternatively, you can also store the certificate as an embedded .pfx resource
// directly in this assembly or in a file published alongside this project:
//
// services.AddOpenIddict()
// .AddSigningCertificate(
// assembly: typeof(Startup).GetTypeInfo().Assembly,
// resource: "Mvc.Server.Certificate.pfx",
// password: "OpenIddict");
services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>();

111
src/OpenIddict/OpenIddictExtensions.cs

@ -7,6 +7,7 @@
using System; using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
@ -65,13 +66,6 @@ namespace Microsoft.AspNetCore.Builder {
} }
} }
// Ensure at least one signing certificate/key has been registered.
if (options.SigningCredentials.Count == 0) {
throw new InvalidOperationException("At least one signing key must be registered. Consider registering a X.509 " +
"certificate using 'services.AddOpenIddict().AddSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.");
}
// Ensure at least one flow has been enabled. // Ensure at least one flow has been enabled.
if (options.GrantTypes.Count == 0) { if (options.GrantTypes.Count == 0) {
throw new InvalidOperationException("At least one OAuth2/OpenID Connect flow must be enabled."); throw new InvalidOperationException("At least one OAuth2/OpenID Connect flow must be enabled.");
@ -79,18 +73,18 @@ namespace Microsoft.AspNetCore.Builder {
// Ensure the authorization endpoint has been enabled when // Ensure the authorization endpoint has been enabled when
// the authorization code or implicit grants are supported. // the authorization code or implicit grants are supported.
if (!options.AuthorizationEndpointPath.HasValue && (options.IsAuthorizationCodeFlowEnabled() || if (!options.AuthorizationEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.IsImplicitFlowEnabled())) { options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
throw new InvalidOperationException("The authorization endpoint must be enabled to use " + throw new InvalidOperationException("The authorization endpoint must be enabled to use " +
"the authorization code and implicit flows."); "the authorization code and implicit flows.");
} }
// Ensure the token endpoint has been enabled when the authorization code, // Ensure the token endpoint has been enabled when the authorization code,
// client credentials, password or refresh token grants are supported. // client credentials, password or refresh token grants are supported.
if (!options.TokenEndpointPath.HasValue && (options.IsAuthorizationCodeFlowEnabled() || if (!options.TokenEndpointPath.HasValue && (options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
options.IsClientCredentialsFlowEnabled() || options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials) ||
options.IsPasswordFlowEnabled() || options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Password) ||
options.IsRefreshTokenFlowEnabled())) { options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken))) {
throw new InvalidOperationException("The token endpoint must be enabled to use the authorization code, " + throw new InvalidOperationException("The token endpoint must be enabled to use the authorization code, " +
"client credentials, password and refresh token flows."); "client credentials, password and refresh token flows.");
} }
@ -99,6 +93,14 @@ namespace Microsoft.AspNetCore.Builder {
throw new InvalidOperationException("The revocation endpoint cannot be enabled when token revocation is disabled."); throw new InvalidOperationException("The revocation endpoint cannot be enabled when token revocation is disabled.");
} }
// Ensure at least one asymmetric signing certificate/key was registered if the implicit flow was enabled.
if (!options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey) &&
options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)) {
throw new InvalidOperationException("At least one asymmetric signing key must be registered when enabling the implicit flow. "+
"Consider registering a X.509 certificate using 'services.AddOpenIddict().AddSigningCertificate()' " +
"or call 'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.");
}
return app.UseOpenIdConnectServer(options); return app.UseOpenIdConnectServer(options);
} }
@ -126,7 +128,7 @@ namespace Microsoft.AspNetCore.Builder {
} }
/// <summary> /// <summary>
/// Registers a new ephemeral key used to sign the tokens issued by OpenIddict: the key /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key
/// is discarded when the application shuts down and tokens signed using this key are /// is discarded when the application shuts down and tokens signed using this key are
/// automatically invalidated. This method should only be used during development. /// automatically invalidated. This method should only be used during development.
/// On production, using a X.509 certificate stored in the machine store is recommended. /// On production, using a X.509 certificate stored in the machine store is recommended.
@ -142,7 +144,7 @@ namespace Microsoft.AspNetCore.Builder {
} }
/// <summary> /// <summary>
/// Registers a new ephemeral key used to sign the tokens issued by OpenIddict: the key /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key
/// is discarded when the application shuts down and tokens signed using this key are /// is discarded when the application shuts down and tokens signed using this key are
/// automatically invalidated. This method should only be used during development. /// automatically invalidated. This method should only be used during development.
/// On production, using a X.509 certificate stored in the machine store is recommended. /// On production, using a X.509 certificate stored in the machine store is recommended.
@ -164,7 +166,7 @@ namespace Microsoft.AspNetCore.Builder {
} }
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> that is used to sign the tokens issued by OpenIddict. /// Registers a <see cref="X509Certificate2"/> that is used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="certificate">The certificate used to sign the security tokens issued by the server.</param> /// <param name="certificate">The certificate used to sign the security tokens issued by the server.</param>
@ -189,7 +191,7 @@ namespace Microsoft.AspNetCore.Builder {
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from an /// Registers a <see cref="X509Certificate2"/> retrieved from an
/// embedded resource and used to sign the tokens issued by OpenIddict. /// embedded resource and used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="assembly">The assembly containing the certificate.</param> /// <param name="assembly">The assembly containing the certificate.</param>
@ -220,7 +222,7 @@ namespace Microsoft.AspNetCore.Builder {
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted from a /// Registers a <see cref="X509Certificate2"/> extracted from a
/// stream and used to sign the tokens issued by OpenIddict. /// stream and used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="stream">The stream containing the certificate.</param> /// <param name="stream">The stream containing the certificate.</param>
@ -246,7 +248,7 @@ namespace Microsoft.AspNetCore.Builder {
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> extracted from a /// Registers a <see cref="X509Certificate2"/> extracted from a
/// stream and used to sign the tokens issued by OpenIddict. /// stream and used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="stream">The stream containing the certificate.</param> /// <param name="stream">The stream containing the certificate.</param>
@ -276,7 +278,7 @@ namespace Microsoft.AspNetCore.Builder {
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the X.509 /// Registers a <see cref="X509Certificate2"/> retrieved from the X.509
/// machine store and used to sign the tokens issued by OpenIddict. /// machine store and used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param> /// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
@ -296,7 +298,7 @@ namespace Microsoft.AspNetCore.Builder {
/// <summary> /// <summary>
/// Registers a <see cref="X509Certificate2"/> retrieved from the given /// Registers a <see cref="X509Certificate2"/> retrieved from the given
/// X.509 store and used to sign the tokens issued by OpenIddict. /// X.509 store and used to sign the JWT tokens issued by OpenIddict.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param> /// <param name="thumbprint">The thumbprint of the certificate used to identify it in the X.509 store.</param>
@ -318,7 +320,7 @@ namespace Microsoft.AspNetCore.Builder {
} }
/// <summary> /// <summary>
/// Registers a <see cref="SecurityKey"/> used to sign the tokens issued by OpenIddict. /// Registers a <see cref="SecurityKey"/> used to sign the JWT tokens issued by OpenIddict.
/// Note: using <see cref="RsaSecurityKey"/> asymmetric keys is recommended on production. /// Note: using <see cref="RsaSecurityKey"/> asymmetric keys is recommended on production.
/// </summary> /// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <param name="builder">The services builder used by OpenIddict to register new services.</param>
@ -765,70 +767,5 @@ namespace Microsoft.AspNetCore.Builder {
return builder.Configure(options => options.AccessTokenHandler = new JwtSecurityTokenHandler()); return builder.Configure(options => options.AccessTokenHandler = new JwtSecurityTokenHandler());
} }
/// <summary>
/// Determines whether the authorization code flow has been enabled.
/// </summary>
/// <param name="options">The OpenIddict options.</param>
/// <returns><c>true</c> if the authorization code flow has been enabled, <c>false</c> otherwise.</returns>
public static bool IsAuthorizationCodeFlowEnabled([NotNull] this OpenIddictOptions options) {
if (options == null) {
throw new ArgumentNullException(nameof(options));
}
return options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode);
}
/// <summary>
/// Determines whether the client credentials flow has been enabled.
/// </summary>
/// <param name="options">The OpenIddict options.</param>
/// <returns><c>true</c> if the client credentials flow has been enabled, <c>false</c> otherwise.</returns>
public static bool IsClientCredentialsFlowEnabled([NotNull] this OpenIddictOptions options) {
if (options == null) {
throw new ArgumentNullException(nameof(options));
}
return options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials);
}
/// <summary>
/// Determines whether the implicit flow has been enabled.
/// </summary>
/// <param name="options">The OpenIddict options.</param>
/// <returns><c>true</c> if the implicit flow has been enabled, <c>false</c> otherwise.</returns>
public static bool IsImplicitFlowEnabled([NotNull] this OpenIddictOptions options) {
if (options == null) {
throw new ArgumentNullException(nameof(options));
}
return options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit);
}
/// <summary>
/// Determines whether the password flow has been enabled.
/// </summary>
/// <param name="options">The OpenIddict options.</param>
/// <returns><c>true</c> if the password flow has been enabled, <c>false</c> otherwise.</returns>
public static bool IsPasswordFlowEnabled([NotNull] this OpenIddictOptions options) {
if (options == null) {
throw new ArgumentNullException(nameof(options));
}
return options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Password);
}
/// <summary>
/// Determines whether the refresh token flow has been enabled.
/// </summary>
/// <param name="options">The OpenIddict options.</param>
/// <returns><c>true</c> if the refresh token flow has been enabled, <c>false</c> otherwise.</returns>
public static bool IsRefreshTokenFlowEnabled([NotNull] this OpenIddictOptions options) {
if (options == null) {
throw new ArgumentNullException(nameof(options));
}
return options.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken);
}
} }
} }

13
src/OpenIddict/OpenIddictProvider.Authentication.cs

@ -11,7 +11,6 @@ using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
@ -119,7 +118,8 @@ namespace OpenIddict {
} }
// Reject code flow authorization requests if the authorization code flow is not enabled. // Reject code flow authorization requests if the authorization code flow is not enabled.
if (context.Request.IsAuthorizationCodeFlow() && !options.Value.IsAuthorizationCodeFlowEnabled()) { if (context.Request.IsAuthorizationCodeFlow() &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode)) {
logger.LogError("The authorization request was rejected because " + logger.LogError("The authorization request was rejected because " +
"the authorization code flow was not enabled."); "the authorization code flow was not enabled.");
@ -131,7 +131,7 @@ namespace OpenIddict {
} }
// Reject implicit flow authorization requests if the implicit flow is not enabled. // Reject implicit flow authorization requests if the implicit flow is not enabled.
if (context.Request.IsImplicitFlow() && !options.Value.IsImplicitFlowEnabled()) { if (context.Request.IsImplicitFlow() && !options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit)) {
logger.LogError("The authorization request was rejected because the implicit flow was not enabled."); logger.LogError("The authorization request was rejected because the implicit flow was not enabled.");
context.Reject( context.Reject(
@ -142,8 +142,8 @@ namespace OpenIddict {
} }
// Reject hybrid flow authorization requests if the authorization code or the implicit flows are not enabled. // Reject hybrid flow authorization requests if the authorization code or the implicit flows are not enabled.
if (context.Request.IsHybridFlow() && (!options.Value.IsAuthorizationCodeFlowEnabled() || if (context.Request.IsHybridFlow() && (!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode) ||
!options.Value.IsImplicitFlowEnabled())) { !options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.Implicit))) {
logger.LogError("The authorization request was rejected because the " + logger.LogError("The authorization request was rejected because the " +
"authorization code flow or the implicit flow was not enabled."); "authorization code flow or the implicit flow was not enabled.");
@ -155,7 +155,8 @@ namespace OpenIddict {
} }
// Reject authorization requests that specify scope=offline_access if the refresh token flow is not enabled. // Reject authorization requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) && !options.Value.IsRefreshTokenFlowEnabled()) { if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
context.Reject( context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest, error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed."); description: "The 'offline_access' scope is not allowed.");

5
src/OpenIddict/OpenIddictProvider.Discovery.cs

@ -39,9 +39,8 @@ namespace OpenIddict {
context.Scopes.Add(OpenIdConnectConstants.Scopes.Phone); context.Scopes.Add(OpenIdConnectConstants.Scopes.Phone);
context.Scopes.Add(OpenIddictConstants.Scopes.Roles); context.Scopes.Add(OpenIddictConstants.Scopes.Roles);
// Only add the "offline_access" scope if the refresh // Only add the "offline_access" scope if the refresh token grant is enabled.
// token flow is enabled in the OpenIddict options. if (context.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
if (options.Value.IsRefreshTokenFlowEnabled()) {
context.Scopes.Add(OpenIdConnectConstants.Scopes.OfflineAccess); context.Scopes.Add(OpenIdConnectConstants.Scopes.OfflineAccess);
} }

4
src/OpenIddict/OpenIddictProvider.Exchange.cs

@ -10,7 +10,6 @@ using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -37,7 +36,8 @@ namespace OpenIddict {
} }
// Reject token requests that specify scope=offline_access if the refresh token flow is not enabled. // Reject token requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) && !options.Value.IsRefreshTokenFlowEnabled()) { if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!options.Value.GrantTypes.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken)) {
context.Reject( context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest, error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The 'offline_access' scope is not allowed."); description: "The 'offline_access' scope is not allowed.");

129
test/OpenIddict.Tests/OpenIddictExtensionsTests.cs

@ -44,32 +44,11 @@ namespace OpenIddict.Tests {
"or in the dependency injection container when enabling request caching support.", exception.Message); "or in the dependency injection container when enabling request caching support.", exception.Message);
} }
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoSigningCredentialsIsRegistered() {
// Arrange
var services = new ServiceCollection();
services.AddOpenIddict();
var builder = new ApplicationBuilder(services.BuildServiceProvider());
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => builder.UseOpenIddict());
Assert.Equal("At least one signing key must be registered. Consider registering a X.509 " +
"certificate using 'services.AddOpenIddict().AddSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.", exception.Message);
}
[Fact] [Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoFlowIsEnabled() { public void UseOpenIddict_ThrowsAnExceptionWhenNoFlowIsEnabled() {
// Arrange // Arrange
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddOpenIddict();
services.AddOpenIddict()
.AddSigningCertificate(
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly,
resource: "OpenIddict.Tests.Certificate.pfx",
password: "OpenIddict");
var builder = new ApplicationBuilder(services.BuildServiceProvider()); var builder = new ApplicationBuilder(services.BuildServiceProvider());
@ -87,10 +66,6 @@ namespace OpenIddict.Tests {
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddOpenIddict() services.AddOpenIddict()
.AddSigningCertificate(
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly,
resource: "OpenIddict.Tests.Certificate.pfx",
password: "OpenIddict")
.Configure(options => options.GrantTypes.Add(flow)) .Configure(options => options.GrantTypes.Add(flow))
.Configure(options => options.AuthorizationEndpointPath = PathString.Empty); .Configure(options => options.AuthorizationEndpointPath = PathString.Empty);
@ -113,10 +88,6 @@ namespace OpenIddict.Tests {
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddOpenIddict() services.AddOpenIddict()
.AddSigningCertificate(
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly,
resource: "OpenIddict.Tests.Certificate.pfx",
password: "OpenIddict")
.EnableAuthorizationEndpoint("/connect/authorize") .EnableAuthorizationEndpoint("/connect/authorize")
.Configure(options => options.GrantTypes.Add(flow)) .Configure(options => options.GrantTypes.Add(flow))
.Configure(options => options.TokenEndpointPath = PathString.Empty); .Configure(options => options.TokenEndpointPath = PathString.Empty);
@ -136,10 +107,6 @@ namespace OpenIddict.Tests {
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddOpenIddict() services.AddOpenIddict()
.AddSigningCertificate(
assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly,
resource: "OpenIddict.Tests.Certificate.pfx",
password: "OpenIddict")
.EnableAuthorizationEndpoint("/connect/authorize") .EnableAuthorizationEndpoint("/connect/authorize")
.EnableRevocationEndpoint("/connect/revocation") .EnableRevocationEndpoint("/connect/revocation")
.AllowImplicitFlow() .AllowImplicitFlow()
@ -153,6 +120,25 @@ namespace OpenIddict.Tests {
Assert.Equal("The revocation endpoint cannot be enabled when token revocation is disabled.", exception.Message); Assert.Equal("The revocation endpoint cannot be enabled when token revocation is disabled.", exception.Message);
} }
[Fact]
public void UseOpenIddict_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfTheImplicitFlowIsEnabled() {
// Arrange
var services = new ServiceCollection();
services.AddOpenIddict()
.EnableAuthorizationEndpoint("/connect/authorize")
.AllowImplicitFlow();
var builder = new ApplicationBuilder(services.BuildServiceProvider());
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => builder.UseOpenIddict());
Assert.Equal("At least one asymmetric signing key must be registered when enabling the implicit flow. " +
"Consider registering a X.509 certificate using 'services.AddOpenIddict().AddSigningCertificate()' " +
"or call 'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.", exception.Message);
}
[Fact] [Fact]
public void Configure_OptionsAreCorrectlyAmended() { public void Configure_OptionsAreCorrectlyAmended() {
// Arrange // Arrange
@ -740,80 +726,5 @@ namespace OpenIddict.Tests {
// Assert // Assert
Assert.IsType(typeof(JwtSecurityTokenHandler), options.Value.AccessTokenHandler); Assert.IsType(typeof(JwtSecurityTokenHandler), options.Value.AccessTokenHandler);
} }
[Theory]
[InlineData(true)]
[InlineData(false)]
public void IsAuthorizationCodeFlowEnabled_ReturnsAppropriateResult(bool enabled) {
// Arrange
var options = new OpenIddictOptions();
if (enabled) {
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode);
}
// Act and assert
Assert.Equal(enabled, options.IsAuthorizationCodeFlowEnabled());
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void IsClientCredentialsFlowEnabled_ReturnsAppropriateResult(bool enabled) {
// Arrange
var options = new OpenIddictOptions();
if (enabled) {
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.ClientCredentials);
}
// Act and assert
Assert.Equal(enabled, options.IsClientCredentialsFlowEnabled());
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void IsImplicitFlowEnabled_ReturnsAppropriateResult(bool enabled) {
// Arrange
var options = new OpenIddictOptions();
if (enabled) {
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Implicit);
}
// Act and assert
Assert.Equal(enabled, options.IsImplicitFlowEnabled());
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void IsPasswordFlowEnabled_ReturnsAppropriateResult(bool enabled) {
// Arrange
var options = new OpenIddictOptions();
if (enabled) {
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Password);
}
// Act and assert
Assert.Equal(enabled, options.IsPasswordFlowEnabled());
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void IsRefreshTokenFlowEnabled_ReturnsAppropriateResult(bool enabled) {
// Arrange
var options = new OpenIddictOptions();
if (enabled) {
options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.RefreshToken);
}
// Act and assert
Assert.Equal(enabled, options.IsRefreshTokenFlowEnabled());
}
} }
} }

7
test/OpenIddict.Tests/OpenIddictProviderTests.cs

@ -121,8 +121,7 @@ namespace OpenIddict.Tests {
app.Run(context => { app.Run(context => {
var request = context.GetOpenIdConnectRequest(); var request = context.GetOpenIdConnectRequest();
if (request.IsAuthorizationRequest() || request.IsTokenRequest()) {
if (context.Request.Path == AuthorizationEndpoint || context.Request.Path == TokenEndpoint) {
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Magnifique"); identity.AddClaim(ClaimTypes.NameIdentifier, "Bob le Magnifique");
@ -138,11 +137,11 @@ namespace OpenIddict.Tests {
return context.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties); return context.Authentication.SignInAsync(ticket.AuthenticationScheme, ticket.Principal, ticket.Properties);
} }
else if (context.Request.Path == LogoutEndpoint) { else if (request.IsLogoutRequest()) {
return context.Authentication.SignOutAsync(OpenIdConnectServerDefaults.AuthenticationScheme); return context.Authentication.SignOutAsync(OpenIdConnectServerDefaults.AuthenticationScheme);
} }
else if (context.Request.Path == UserinfoEndpoint) { else if (request.IsUserinfoRequest()) {
context.Response.Headers[HeaderNames.ContentType] = "application/json"; context.Response.Headers[HeaderNames.ContentType] = "application/json";
return context.Response.WriteAsync(JsonConvert.SerializeObject(new { return context.Response.WriteAsync(JsonConvert.SerializeObject(new {

Loading…
Cancel
Save