diff --git a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx index c1ca8120..e707e673 100644 --- a/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/Resources/OpenIddictResources.resx @@ -2184,18 +2184,10 @@ The principal used to create the token contained the following claims: {Claims}. The introspection request was rejected because the received token was of an unsupported type. {Locked} - - The introspection request was rejected because the authorization code was issued to a different client. - {Locked} - The introspection request was rejected because the access token was issued to a different client or for another resource server. {Locked} - - The introspection request was rejected because the identity token was issued to a different client. - {Locked} - The introspection request was rejected because the refresh token was issued to a different client. {Locked} @@ -2236,18 +2228,10 @@ The principal used to create the token contained the following claims: {Claims}. The revocation request was rejected because the received token was of an unsupported type. {Locked} - - The revocation request was rejected because the authorization code was issued to a different client. - {Locked} - The revocation request was rejected because the access token was issued to a different client or for another resource server. {Locked} - - The revocation request was rejected because the identity token was issued to a different client. - {Locked} - The revocation request was rejected because the refresh token was issued to a different client. {Locked} diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs index f9c9437f..e57c08b9 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs @@ -741,8 +741,6 @@ namespace OpenIddict.Server Debug.Assert(context.Principal != null, SR.GetResourceString(SR.ID5006)); if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken) && - !context.Principal.HasTokenType(TokenTypeHints.AuthorizationCode) && - !context.Principal.HasTokenType(TokenTypeHints.IdToken) && !context.Principal.HasTokenType(TokenTypeHints.RefreshToken)) { context.Logger.LogError(SR.GetResourceString(SR.ID7104)); @@ -789,29 +787,6 @@ namespace OpenIddict.Server Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID5000(Parameters.ClientId)); Debug.Assert(context.Principal != null, SR.GetResourceString(SR.ID5006)); - // When the introspected token is an authorization code, the caller must be - // listed as a presenter (i.e the party the authorization code was issued to). - if (context.Principal.HasTokenType(TokenTypeHints.AuthorizationCode)) - { - if (!context.Principal.HasPresenter()) - { - throw new InvalidOperationException(SR.GetResourceString(SR.ID1042)); - } - - if (!context.Principal.HasPresenter(context.ClientId)) - { - context.Logger.LogError(SR.GetResourceString(SR.ID7105)); - - context.Reject( - error: Errors.InvalidToken, - description: context.Localizer[SR.ID3077]); - - return default; - } - - return default; - } - // When the introspected token is an access token, the caller must be listed either as a presenter // (i.e the party the token was issued to) or as an audience (i.e a resource server/API). // If the access token doesn't contain any explicit presenter/audience, the token is assumed @@ -829,22 +804,6 @@ namespace OpenIddict.Server return default; } - // When the introspected token is an identity token, the caller must be listed as an audience - // (i.e the client application the identity token was initially issued to). - // If the identity token doesn't contain any explicit audience, the token is - // assumed to be not specific to any client application and the check is bypassed. - if (context.Principal.HasTokenType(TokenTypeHints.IdToken) && - context.Principal.HasAudience() && !context.Principal.HasAudience(context.ClientId)) - { - context.Logger.LogError(SR.GetResourceString(SR.ID7107)); - - context.Reject( - error: Errors.InvalidToken, - description: context.Localizer[SR.ID3077]); - - return default; - } - // When the introspected token is a refresh token, the caller must be // listed as a presenter (i.e the party the token was issued to). // If the refresh token doesn't contain any explicit presenter, the token is @@ -985,8 +944,8 @@ namespace OpenIddict.Server Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), SR.FormatID5000(Parameters.ClientId)); Debug.Assert(context.Principal != null, SR.GetResourceString(SR.ID5006)); - // Don't return application-specific claims if the token is not an access or identity token. - if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken) && !context.Principal.HasTokenType(TokenTypeHints.IdToken)) + // Don't return application-specific claims if the token is not an access token. + if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken)) { return; } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs index e661341a..ac50598e 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs @@ -687,7 +687,6 @@ namespace OpenIddict.Server Debug.Assert(context.Principal != null, SR.GetResourceString(SR.ID5006)); if (!context.Principal.HasTokenType(TokenTypeHints.AccessToken) && - !context.Principal.HasTokenType(TokenTypeHints.AuthorizationCode) && !context.Principal.HasTokenType(TokenTypeHints.RefreshToken)) { context.Logger.LogError(SR.GetResourceString(SR.ID7117)); @@ -734,29 +733,6 @@ namespace OpenIddict.Server Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID5000(Parameters.ClientId)); Debug.Assert(context.Principal != null, SR.GetResourceString(SR.ID5006)); - // When the revoked token is an authorization code, the caller must be - // listed as a presenter (i.e the party the authorization code was issued to). - if (context.Principal.HasTokenType(TokenTypeHints.AuthorizationCode)) - { - if (!context.Principal.HasPresenter()) - { - throw new InvalidOperationException(SR.GetResourceString(SR.ID1042)); - } - - if (!context.Principal.HasPresenter(context.ClientId)) - { - context.Logger.LogError(SR.GetResourceString(SR.ID7118)); - - context.Reject( - error: Errors.InvalidToken, - description: context.Localizer[SR.ID3080]); - - return default; - } - - return default; - } - // When the revoked token is an access token, the caller must be listed either as a presenter // (i.e the party the token was issued to) or as an audience (i.e a resource server/API). // If the access token doesn't contain any explicit presenter/audience, the token is assumed @@ -774,22 +750,6 @@ namespace OpenIddict.Server return default; } - // When the revoked token is an identity token, the caller must be listed as an audience - // (i.e the client application the identity token was initially issued to). - // If the identity token doesn't contain any explicit audience, the token is - // assumed to be not specific to any client application and the check is bypassed. - if (context.Principal.HasTokenType(TokenTypeHints.IdToken) && - context.Principal.HasAudience() && !context.Principal.HasAudience(context.ClientId)) - { - context.Logger.LogError(SR.GetResourceString(SR.ID7120)); - - context.Reject( - error: Errors.InvalidToken, - description: context.Localizer[SR.ID3080]); - - return default; - } - // When the revoked token is a refresh token, the caller must be // listed as a presenter (i.e the party the token was issued to). // If the refresh token doesn't contain any explicit presenter, the token is diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs index 0e257478..a95f39be 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs @@ -5,7 +5,6 @@ */ using System; -using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Text.Json; @@ -221,49 +220,13 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal(SR.GetResourceString(SR.ID3019), response.ErrorDescription); } - [Fact] - public async Task ValidateIntrospectionRequest_AuthorizationCodeCausesAnErrorWhenPresentersAreMissing() - { - // Arrange - await using var server = await CreateServerAsync(options => - { - options.EnableDegradedMode(); - - options.AddEventHandler(builder => - { - builder.UseInlineHandler(context => - { - Assert.Equal("SlAV32hkKG", context.Token); - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetPresenters(Enumerable.Empty()); - - return default; - }); - - builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); - }); - }); - - await using var client = await server.CreateClientAsync(); - - // Act and assert - var exception = await Assert.ThrowsAsync(delegate - { - return client.PostAsync("/connect/introspect", new OpenIddictRequest - { - ClientId = "Fabrikam", - Token = "SlAV32hkKG", - TokenTypeHint = TokenTypeHints.AuthorizationCode - }); - }); - - Assert.Equal(SR.GetResourceString(SR.ID1042), exception.Message); - } - - [Fact] - public async Task ValidateIntrospectionRequest_AuthorizationCodeCausesAnErrorWhenCallerIsNotAValidPresenter() + [Theory] + [InlineData(TokenTypeHints.AuthorizationCode)] + [InlineData(TokenTypeHints.DeviceCode)] + [InlineData(TokenTypeHints.IdToken)] + [InlineData(TokenTypeHints.UserCode)] + [InlineData("custom_token")] + public async Task ValidateIntrospectionRequest_UnsupportedTokenTypeCausesAnError(string type) { // Arrange await using var server = await CreateServerAsync(options => @@ -274,11 +237,10 @@ namespace OpenIddict.Server.IntegrationTests { builder.UseInlineHandler(context => { - Assert.Equal("SlAV32hkKG", context.Token); + Assert.Equal("5HtRgAtc02", context.Token); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetPresenters("Contoso"); + .SetTokenType(type); return default; }); @@ -295,13 +257,12 @@ namespace OpenIddict.Server.IntegrationTests var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest { ClientId = "Fabrikam", - Token = "SlAV32hkKG", - TokenTypeHint = TokenTypeHints.AuthorizationCode + Token = "5HtRgAtc02" }); // Assert - Assert.Equal(Errors.InvalidToken, response.Error); - Assert.Equal(SR.GetResourceString(SR.ID3077), response.ErrorDescription); + Assert.Equal(Errors.UnsupportedTokenType, response.Error); + Assert.Equal(SR.GetResourceString(SR.ID3076), response.ErrorDescription); } [Fact] @@ -347,48 +308,6 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal(SR.GetResourceString(SR.ID3077), response.ErrorDescription); } - [Fact] - public async Task ValidateIntrospectionRequest_IdentityTokenCausesAnErrorWhenCallerIsNotAValidAudience() - { - // Arrange - await using var server = await CreateServerAsync(options => - { - options.EnableDegradedMode(); - - options.AddEventHandler(builder => - { - builder.UseInlineHandler(context => - { - Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token); - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.IdToken) - .SetAudiences("AdventureWorks"); - - return default; - }); - - builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); - }); - - options.RemoveEventHandler(NormalizeErrorResponse.Descriptor); - }); - - await using var client = await server.CreateClientAsync(); - - // Act - var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest - { - ClientId = "Fabrikam", - Token = "2YotnFZFEjr1zCsicMWpAA", - TokenTypeHint = TokenTypeHints.IdToken - }); - - // Assert - Assert.Equal(Errors.InvalidToken, response.Error); - Assert.Equal(SR.GetResourceString(SR.ID3077), response.ErrorDescription); - } - [Fact] public async Task ValidateIntrospectionRequest_RefreshTokenCausesAnErrorWhenCallerIsNotAValidPresenter() { @@ -847,49 +766,6 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal("Contoso", (string) response[Claims.ClientId]); } - [Fact] - public async Task HandleIntrospectionRequest_NonBasicAuthorizationCodeClaimsAreNotReturned() - { - // Arrange - await using var server = await CreateServerAsync(options => - { - options.EnableDegradedMode(); - - options.AddEventHandler(builder => - { - builder.UseInlineHandler(context => - { - Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token); - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetPresenters("Fabrikam") - .SetClaim(Claims.Username, "Bob") - .SetClaim("custom_claim", "secret_value"); - - return default; - }); - - builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); - }); - }); - - await using var client = await server.CreateClientAsync(); - - // Act - var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest - { - ClientId = "Fabrikam", - ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", - Token = "2YotnFZFEjr1zCsicMWpAA", - TokenTypeHint = TokenTypeHints.AuthorizationCode - }); - - // Assert - Assert.Null(response["custom_claim"]); - Assert.Null(response[Claims.Username]); - } - [Fact] public async Task HandleIntrospectionRequest_NonBasicRefreshTokenClaimsAreNotReturned() { @@ -993,63 +869,6 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal("openid profile", (string) response[Claims.Scope]); } - [Fact] - public async Task HandleIntrospectionRequest_NonBasicIdentityClaimsAreReturnedToTrustedAudiences() - { - // Arrange - var application = new OpenIddictApplication(); - - var manager = CreateApplicationManager(mock => - { - mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) - .ReturnsAsync(application); - - mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Confidential, It.IsAny())) - .ReturnsAsync(true); - - mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) - .ReturnsAsync(true); - }); - - await using var server = await CreateServerAsync(options => - { - options.AddEventHandler(builder => - { - builder.UseInlineHandler(context => - { - Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token); - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.IdToken) - .SetAudiences("Fabrikam") - .SetClaim(Claims.Username, "Bob") - .SetClaim("custom_claim", "secret_value"); - - return default; - }); - - builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); - }); - - options.Services.AddSingleton(manager); - }); - - await using var client = await server.CreateClientAsync(); - - // Act - var response = await client.PostAsync("/connect/introspect", new OpenIddictRequest - { - ClientId = "Fabrikam", - ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", - Token = "2YotnFZFEjr1zCsicMWpAA", - TokenTypeHint = TokenTypeHints.IdToken - }); - - // Assert - Assert.Equal("secret_value", (string) response["custom_claim"]); - Assert.Equal("Bob", (string) response[Claims.Username]); - } - [Fact] public async Task HandleIntrospectionRequest_ClaimValueTypesAreHonored() { diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs index 02ef0258..62d91eb9 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Revocation.cs @@ -153,8 +153,13 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal(SR.FormatID3029(Parameters.Token), response.ErrorDescription); } - [Fact] - public async Task ValidateRevocationRequest_IdentityTokenCausesAnUnsupportedTokenTypeError() + [Theory] + [InlineData(TokenTypeHints.AuthorizationCode)] + [InlineData(TokenTypeHints.DeviceCode)] + [InlineData(TokenTypeHints.IdToken)] + [InlineData(TokenTypeHints.UserCode)] + [InlineData("custom_token")] + public async Task ValidateRevocationRequest_UnsupportedTokenTypeCausesAnError(string type) { // Arrange await using var server = await CreateServerAsync(options => @@ -165,11 +170,10 @@ namespace OpenIddict.Server.IntegrationTests { builder.UseInlineHandler(context => { - Assert.Equal("2YotnFZFEjr1zCsicMWpAA", context.Token); + Assert.Equal("5HtRgAtc02", context.Token); context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.IdToken) - .SetAudiences("AdventureWorks"); + .SetTokenType(type); return default; }); @@ -186,8 +190,7 @@ namespace OpenIddict.Server.IntegrationTests var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest { ClientId = "Fabrikam", - Token = "2YotnFZFEjr1zCsicMWpAA", - TokenTypeHint = TokenTypeHints.IdToken + Token = "5HtRgAtc02" }); // Assert @@ -195,48 +198,6 @@ namespace OpenIddict.Server.IntegrationTests Assert.Equal(SR.GetResourceString(SR.ID3079), response.ErrorDescription); } - [Fact] - public async Task ValidateRevocationRequest_AuthorizationCodeCausesAnErrorWhenCallerIsNotAValidPresenter() - { - // Arrange - await using var server = await CreateServerAsync(options => - { - options.EnableDegradedMode(); - - options.AddEventHandler(builder => - { - builder.UseInlineHandler(context => - { - Assert.Equal("SlAV32hkKG", context.Token); - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) - .SetTokenType(TokenTypeHints.AuthorizationCode) - .SetPresenters("Contoso"); - - return default; - }); - - builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); - }); - - options.RemoveEventHandler(NormalizeErrorResponse.Descriptor); - }); - - await using var client = await server.CreateClientAsync(); - - // Act - var response = await client.PostAsync("/connect/revoke", new OpenIddictRequest - { - ClientId = "Fabrikam", - Token = "SlAV32hkKG", - TokenTypeHint = TokenTypeHints.AuthorizationCode - }); - - // Assert - Assert.Equal(Errors.InvalidToken, response.Error); - Assert.Equal(SR.GetResourceString(SR.ID3080), response.ErrorDescription); - } - [Fact] public async Task ValidateRevocationRequest_AccessTokenCausesAnErrorWhenCallerIsNotAValidAudienceOrPresenter() {