Browse Source

Introduce OpenIddictBuilder.AddDevelopmentSigningCertificate()

pull/429/head
Kévin Chalet 9 years ago
parent
commit
46dceca939
  1. 44
      src/OpenIddict/OpenIddictExtensions.cs
  2. 17
      src/OpenIddict/OpenIddictInitializer.cs
  3. 4
      test/OpenIddict.Tests/OpenIddict.Tests.csproj
  4. 69
      test/OpenIddict.Tests/OpenIddictExtensionsTests.cs
  5. 35
      test/OpenIddict.Tests/OpenIddictInitializerTests.cs

44
src/OpenIddict/OpenIddictExtensions.cs

@ -61,11 +61,11 @@ namespace Microsoft.AspNetCore.Builder
// Note: TryAddEnumerable() is used here to ensure the initializers are only registered once.
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIddictOptions>,
OpenIdConnectServerInitializer>());
OpenIddictInitializer>());
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<IPostConfigureOptions<OpenIddictOptions>,
OpenIddictInitializer>());
OpenIdConnectServerInitializer>());
// Register the OpenID Connect server handler in the authentication options,
// so it can be discovered by the default authentication handler provider.
@ -89,6 +89,46 @@ namespace Microsoft.AspNetCore.Builder
return builder;
}
/// <summary>
/// Registers (and generates if necessary) a user-specific development
/// certificate used to sign the JWT tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddDevelopmentSigningCertificate(
[NotNull] this OpenIddictBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.Configure(options => options.SigningCredentials.AddDevelopmentCertificate());
}
/// <summary>
/// Registers (and generates if necessary) a user-specific development
/// certificate used to sign the JWT tokens issued by OpenIddict.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="subject">The subject name associated with the certificate.</param>
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
public static OpenIddictBuilder AddDevelopmentSigningCertificate(
[NotNull] this OpenIddictBuilder builder, [NotNull] X500DistinguishedName subject)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (subject == null)
{
throw new ArgumentNullException(nameof(subject));
}
return builder.Configure(options => options.SigningCredentials.AddDevelopmentCertificate(subject));
}
/// <summary>
/// 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

17
src/OpenIddict/OpenIddictInitializer.cs

@ -88,13 +88,24 @@ namespace OpenIddict
throw new InvalidOperationException("The revocation endpoint cannot be enabled when token revocation is disabled.");
}
if (options.AccessTokenHandler != null && options.SigningCredentials.Count == 0)
{
throw new InvalidOperationException(
"At least one signing key must be registered when using JWT as the access token format. " +
"Consider registering a X.509 certificate using 'services.AddOpenIddict().AddSigningCertificate()' " +
"or 'services.AddOpenIddict().AddDevelopmentSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.");
}
// 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.");
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 'services.AddOpenIddict().AddDevelopmentSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.");
}
}
}

4
test/OpenIddict.Tests/OpenIddict.Tests.csproj

@ -39,4 +39,8 @@
<DefineConstants>$(DefineConstants);SUPPORTS_ECDSA</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<DefineConstants>$(DefineConstants);SUPPORTS_CERTIFICATE_GENERATION</DefineConstants>
</PropertyGroup>
</Project>

69
test/OpenIddict.Tests/OpenIddictExtensionsTests.cs

@ -36,6 +36,75 @@ namespace OpenIddict.Tests
Assert.Equal(TimeSpan.FromDays(1), options.AccessTokenLifetime);
}
[Fact]
public void AddDevelopmentSigningCertificate_ThrowsAnExceptionForNullBuilder()
{
// Arrange
var builder = (OpenIddictBuilder) null;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(delegate
{
builder.AddDevelopmentSigningCertificate();
});
Assert.Equal("builder", exception.ParamName);
}
[Fact]
public void AddDevelopmentSigningCertificate_ThrowsAnExceptionForNullSubject()
{
// Arrange
var services = CreateServices();
var builder = new OpenIddictBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(delegate
{
builder.AddDevelopmentSigningCertificate(subject: null);
});
Assert.Equal("subject", exception.ParamName);
}
#if SUPPORTS_CERTIFICATE_GENERATION
[Fact]
public void AddDevelopmentSigningCertificate_CanGenerateCertificate()
{
// Arrange
var services = CreateServices();
var builder = new OpenIddictBuilder(services);
// Act
builder.AddDevelopmentSigningCertificate();
var options = GetOptions(services);
// Assert
Assert.Equal(1, options.SigningCredentials.Count);
Assert.Equal(SecurityAlgorithms.RsaSha256, options.SigningCredentials[0].Algorithm);
Assert.NotNull(options.SigningCredentials[0].Kid);
}
#else
[Fact]
public void AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatforms()
{
// Arrange
var services = CreateServices();
var builder = new OpenIddictBuilder(services);
builder.AddDevelopmentSigningCertificate();
// Act and assert
var exception = Assert.Throws<PlatformNotSupportedException>(delegate
{
return GetOptions(services);
});
Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message);
}
#endif
[Fact]
public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded()
{

35
test/OpenIddict.Tests/OpenIddictInitializerTests.cs

@ -111,6 +111,33 @@ namespace OpenIddict.Tests
Assert.Equal("The revocation endpoint cannot be enabled when token revocation is disabled.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfAnAccessTokenHandlerIsSet()
{
// Arrange
var server = CreateAuthorizationServer(builder =>
{
builder.EnableAuthorizationEndpoint("/connect/authorize")
.EnableTokenEndpoint("/connect/token")
.AllowAuthorizationCodeFlow()
.UseJsonWebTokens();
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/");
});
Assert.Equal(
"At least one signing key must be registered when using JWT as the access token format. " +
"Consider registering a X.509 certificate using 'services.AddOpenIddict().AddSigningCertificate()' " +
"or 'services.AddOpenIddict().AddDevelopmentSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.", exception.Message);
}
[Fact]
public async Task PostConfigure_ThrowsAnExceptionWhenNoSigningKeyIsRegisteredIfTheImplicitFlowIsEnabled()
{
@ -129,9 +156,11 @@ namespace OpenIddict.Tests
return client.GetAsync("/");
});
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);
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 'services.AddOpenIddict().AddDevelopmentSigningCertificate()' or call " +
"'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.", exception.Message);
}
private static TestServer CreateAuthorizationServer(Action<OpenIddictBuilder> configuration = null)

Loading…
Cancel
Save