Browse Source

Always require that the oi_tkn_typ claim be present if all token types are considered valid

pull/1307/head
Kévin Chalet 5 years ago
parent
commit
f465efda19
  1. 17
      src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs
  2. 17
      src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs
  3. 566
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs
  4. 552
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

17
src/OpenIddict.Server/OpenIddictServerHandlers.Protection.cs

@ -671,18 +671,15 @@ namespace OpenIddict.Server
// (using the "typ" header) or by ASP.NET Core Data Protection (using per-token-type purposes strings).
// To ensure tokens deserialized using a custom routine are of the expected type, a manual check is used,
// which requires that a special claim containing the token type be present in the security principal.
if (context.ValidTokenTypes.Count > 0)
var type = context.Principal.GetTokenType();
if (string.IsNullOrEmpty(type))
{
var type = context.Principal.GetTokenType();
if (string.IsNullOrEmpty(type))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0004));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0004));
}
if (!context.ValidTokenTypes.Contains(type))
{
throw new InvalidOperationException(SR.FormatID0005(type, string.Join(", ", context.ValidTokenTypes)));
}
if (context.ValidTokenTypes.Count > 0 && !context.ValidTokenTypes.Contains(type))
{
throw new InvalidOperationException(SR.FormatID0005(type, string.Join(", ", context.ValidTokenTypes)));
}
return default;

17
src/OpenIddict.Validation/OpenIddictValidationHandlers.Protection.cs

@ -615,18 +615,15 @@ namespace OpenIddict.Validation
// (using the "typ" header) or by ASP.NET Core Data Protection (using per-token-type purposes strings).
// To ensure tokens deserialized using a custom routine are of the expected type, a manual check is used,
// which requires that a special claim containing the token type be present in the security principal.
if (context.ValidTokenTypes.Count > 0)
var type = context.Principal.GetTokenType();
if (string.IsNullOrEmpty(type))
{
var type = context.Principal.GetTokenType();
if (string.IsNullOrEmpty(type))
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0004));
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0004));
}
if (!context.ValidTokenTypes.Contains(type))
{
throw new InvalidOperationException(SR.FormatID0005(type, string.Join(", ", context.ValidTokenTypes)));
}
if (context.ValidTokenTypes.Count > 0 && !context.ValidTokenTypes.Contains(type))
{
throw new InvalidOperationException(SR.FormatID0005(type, string.Join(", ", context.ValidTokenTypes)));
}
return default;

566
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Protection.cs

@ -0,0 +1,566 @@

/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Immutable;
using System.Security.Claims;
using System.Threading.Tasks;
using OpenIddict.Abstractions;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Server.IntegrationTests
{
public abstract partial class OpenIddictServerIntegrationTests
{
[Fact]
public async Task ValidateToken_IssuedAtIsMappedToCreationDate()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(new Claim(Claims.IssuedAt, "1577836800", ClaimValueTypes.Integer64));
context.Principal = new ClaimsPrincipal(identity)
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(1577836800, (long) response[Claims.IssuedAt]);
Assert.Equal("Wed, 01 Jan 2020 00:00:00 GMT", (string?) response[Claims.Private.CreationDate]);
}
[Fact]
public async Task ValidateToken_ExpiresAtIsMappedToExpirationDate()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(new Claim(Claims.ExpiresAt, "2524608000", ClaimValueTypes.Integer64));
context.Principal = new ClaimsPrincipal(identity)
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(2524608000, (long) response[Claims.ExpiresAt]);
Assert.Equal("Sat, 01 Jan 2050 00:00:00 GMT", (string?) response[Claims.Private.ExpirationDate]);
}
[Fact]
public async Task ValidateToken_AuthorizedPartyIsMappedToPresenter()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.AuthorizedParty, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.AuthorizedParty]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Presenter]);
}
[Fact]
public async Task ValidateToken_ClientIdIsMappedToPresenter()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.ClientId, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.ClientId]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Presenter]);
}
[Fact]
public async Task ValidateToken_SinglePublicAudienceIsMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.Audience, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.Audience]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Audience]);
}
[Fact]
public async Task ValidateToken_MultiplePublicAudiencesAreMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Audience, ImmutableArray.Create("Fabrikam", "Contoso"));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]?) response[Claims.Audience]);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]?) response[Claims.Private.Audience]);
}
[Fact]
public async Task ValidateToken_MultiplePublicScopesAreNormalizedToSingleClaim()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Scope, ImmutableArray.Create(Scopes.OpenId, Scopes.Profile));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("openid profile", (string?) response[Claims.Scope]);
}
[Fact]
public async Task ValidateToken_SinglePublicScopeIsMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.Scope, "openid profile");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { Scopes.OpenId, Scopes.Profile }, (string[]?) response[Claims.Private.Scope]);
}
[Fact]
public async Task ValidateToken_MultiplePublicScopesAreMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Scope, ImmutableArray.Create(Scopes.OpenId, Scopes.Profile));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { Scopes.OpenId, Scopes.Profile }, (string[]?) response[Claims.Private.Scope]);
}
[Fact]
public async Task ValidateToken_MissingTokenTypeThrowsAnException()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(Array.Empty<string>(), context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(null)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/connect/introspect", new OpenIddictRequest
{
Token = "access_token"
});
});
// Assert
Assert.Equal(SR.GetResourceString(SR.ID0004), exception.Message);
}
[Fact]
public async Task ValidateToken_InvalidTokenTypeThrowsAnException()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AuthorizationCode)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
});
// Assert
Assert.Equal(SR.FormatID0005(TokenTypeHints.AuthorizationCode, TokenTypeHints.AccessToken), exception.Message);
}
}
}

552
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

@ -7,7 +7,6 @@
*/
using System;
using System.Collections.Immutable;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
@ -221,557 +220,6 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
}
[Fact]
public async Task ProcessAuthentication_IssuedAtIsMappedToCreationDate()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(new Claim(Claims.IssuedAt, "1577836800", ClaimValueTypes.Integer64));
context.Principal = new ClaimsPrincipal(identity)
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(1577836800, (long) response[Claims.IssuedAt]);
Assert.Equal("Wed, 01 Jan 2020 00:00:00 GMT", (string?) response[Claims.Private.CreationDate]);
}
[Fact]
public async Task ProcessAuthentication_ExpiresAtIsMappedToExpirationDate()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(new Claim(Claims.ExpiresAt, "2524608000", ClaimValueTypes.Integer64));
context.Principal = new ClaimsPrincipal(identity)
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(2524608000, (long) response[Claims.ExpiresAt]);
Assert.Equal("Sat, 01 Jan 2050 00:00:00 GMT", (string?) response[Claims.Private.ExpirationDate]);
}
[Fact]
public async Task ProcessAuthentication_AuthorizedPartyIsMappedToPresenter()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.AuthorizedParty, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.AuthorizedParty]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Presenter]);
}
[Fact]
public async Task ProcessAuthentication_ClientIdIsMappedToPresenter()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.ClientId, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.ClientId]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Presenter]);
}
[Fact]
public async Task ProcessAuthentication_SinglePublicAudienceIsMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.Audience, "Fabrikam");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("Fabrikam", (string?) response[Claims.Audience]);
Assert.Equal("Fabrikam", (string?) response[Claims.Private.Audience]);
}
[Fact]
public async Task ProcessAuthentication_MultiplePublicAudiencesAreMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Audience, ImmutableArray.Create("Fabrikam", "Contoso"));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]?) response[Claims.Audience]);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]?) response[Claims.Private.Audience]);
}
[Fact]
public async Task ProcessAuthentication_MultiplePublicScopesAreNormalizedToSingleClaim()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Scope, ImmutableArray.Create(Scopes.OpenId, Scopes.Profile));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal("openid profile", (string?) response[Claims.Scope]);
}
[Fact]
public async Task ProcessAuthentication_SinglePublicScopeIsMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaim(Claims.Scope, "openid profile");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { Scopes.OpenId, Scopes.Profile }, (string[]?) response[Claims.Private.Scope]);
}
[Fact]
public async Task ProcessAuthentication_MultiplePublicScopesAreMappedToPrivateClaims()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AccessToken)
.SetClaim(Claims.Subject, "Bob le Magnifique")
.SetClaims(Claims.Scope, ImmutableArray.Create(Scopes.OpenId, Scopes.Profile));
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
// Assert
Assert.Equal("Bob le Magnifique", (string?) response[Claims.Subject]);
Assert.Equal(new[] { Scopes.OpenId, Scopes.Profile }, (string[]?) response[Claims.Private.Scope]);
}
[Fact]
public async Task ProcessAuthentication_MissingTokenTypeThrowsAnException()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(null)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
});
// Assert
Assert.Equal(SR.GetResourceString(SR.ID0004), exception.Message);
}
[Fact]
public async Task ProcessAuthentication_InvalidTokenTypeThrowsAnException()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.SetUserinfoEndpointUris("/authenticate");
options.AddEventHandler<HandleUserinfoRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.SkipRequest();
return default;
}));
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("access_token", context.Token);
Assert.Equal(new[] { TokenTypeHints.AccessToken }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetTokenType(TokenTypeHints.AuthorizationCode)
.SetClaim(Claims.Subject, "Bob le Magnifique");
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
});
await using var client = await server.CreateClientAsync();
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(delegate
{
return client.GetAsync("/authenticate", new OpenIddictRequest
{
AccessToken = "access_token"
});
});
// Assert
Assert.Equal(SR.FormatID0005(TokenTypeHints.AuthorizationCode, TokenTypeHints.AccessToken), exception.Message);
}
[Fact]
public async Task ProcessAuthentication_MissingIdTokenHintReturnsNull()
{

Loading…
Cancel
Save