Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to specify whether s_hash should be emitted. #68

Merged
merged 4 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,17 @@ public class IdentityServerOptions
/// Emits an aud claim with the format issuer/resources. That's needed for some older access token validation plumbing. Defaults to false.
/// </summary>
public bool EmitStaticAudienceClaim { get; set; } = false;

/// <summary>
/// Specifies whether scopes in JWTs are emitted as array or string
/// </summary>
public bool EmitScopesAsSpaceDelimitedStringInJwt { get; set; } = false;

/// <summary>
/// Specifies whether the s_hash claim gets emitted in identity tokens. Defaults to false.
/// </summary>
public bool EmitStateHash { get; set; } = false;

/// <summary>
/// Specifies whether the JWT typ and content-type for JWT secured authorization requests is checked according to IETF spec.
/// This might break older OIDC conformant request objects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Duende.IdentityServer.Services;
using Duende.IdentityServer.Validation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;

namespace Duende.IdentityServer.ResponseHandling
{
Expand All @@ -24,6 +23,11 @@ namespace Duende.IdentityServer.ResponseHandling
/// <seealso cref="IAuthorizeResponseGenerator" />
public class AuthorizeResponseGenerator : IAuthorizeResponseGenerator
{
/// <summary>
/// The options
/// </summary>
protected IdentityServerOptions Options;

/// <summary>
/// The token service
/// </summary>
Expand Down Expand Up @@ -57,20 +61,23 @@ public class AuthorizeResponseGenerator : IAuthorizeResponseGenerator
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizeResponseGenerator"/> class.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="clock">The clock.</param>
/// <param name="logger">The logger.</param>
/// <param name="tokenService">The token service.</param>
/// <param name="keyMaterialService"></param>
/// <param name="authorizationCodeStore">The authorization code store.</param>
/// <param name="events">The events.</param>
public AuthorizeResponseGenerator(
IdentityServerOptions options,
ISystemClock clock,
ITokenService tokenService,
IKeyMaterialService keyMaterialService,
IAuthorizationCodeStore authorizationCodeStore,
ILogger<AuthorizeResponseGenerator> logger,
IEventService events)
{
Options = options;
Clock = clock;
TokenService = tokenService;
KeyMaterialService = keyMaterialService;
Expand Down Expand Up @@ -181,7 +188,8 @@ protected virtual async Task<AuthorizeResponse> CreateImplicitFlowResponseAsync(
if (responseTypes.Contains(OidcConstants.ResponseTypes.IdToken))
{
string stateHash = null;
if (request.State.IsPresent())

if (Options.EmitStateHash && request.State.IsPresent())
{
var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.Client.AllowedIdentityTokenSigningAlgorithms);
if (credential == null)
Expand Down Expand Up @@ -229,7 +237,7 @@ protected virtual async Task<AuthorizeResponse> CreateImplicitFlowResponseAsync(
protected virtual async Task<AuthorizationCode> CreateCodeAsync(ValidatedAuthorizeRequest request)
{
string stateHash = null;
if (request.State.IsPresent())
if (Options.EmitStateHash && request.State.IsPresent())
{
var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.Client.AllowedIdentityTokenSigningAlgorithms);
if (credential == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ public void ConfigureServices(IServiceCollection services)

services.AddIdentityServer(options =>
{
Options = options;
//if (Options != null)
//{
// options.EmitStateHash = Options.EmitStateHash;
//}

options.Events = new EventsOptions
{
Expand All @@ -136,6 +139,8 @@ public void ConfigureServices(IServiceCollection services)
RaiseSuccessEvents = true
};
options.KeyManagement.Enabled = false;

Options = options;
})
.AddInMemoryClients(Clients)
.AddInMemoryIdentityResources(IdentityScopes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,14 @@ public async Task No_state_should_not_result_in_shash()
s_hash.Should().BeNull();
}

[Fact]
[Theory]
[InlineData(true)]
[InlineData(false)]
[Trait("Category", Category)]
public async Task State_should_result_in_shash()
public async Task StateHash_should_be_emitted_based_on_options(bool emitStateHash)
{
_pipeline.Options.EmitStateHash = emitStateHash;

await _pipeline.LoginAsync("bob");

var nonce = Guid.NewGuid().ToString();
Expand Down Expand Up @@ -158,8 +162,16 @@ public async Task State_should_result_in_shash()
var token = new JwtSecurityToken(tokenResult.IdentityToken);

var s_hash = token.Claims.FirstOrDefault(c => c.Type == "s_hash");
s_hash.Should().NotBeNull();
s_hash.Value.Should().Be(CryptoHelper.CreateHashClaimValue("state", "RS256"));

if (emitStateHash)
{
s_hash.Should().NotBeNull();
s_hash.Value.Should().Be(CryptoHelper.CreateHashClaimValue("state", "RS256"));
}
else
{
s_hash.Should().BeNull();
}
}
}
}