/* * 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.Security.Claims; using System.Threading; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; using OpenIddict.Core; using OpenIddict.Models; using Xunit; namespace OpenIddict.Tests { public partial class OpenIddictProviderTests { [Fact] public async Task ExtractIntrospectionRequest_GetRequestsAreRejected() { // Arrange var server = CreateAuthorizationServer(); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.GetAsync(IntrospectionEndpoint, new OpenIdConnectRequest { Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); Assert.Equal("The specified HTTP method is not valid.", response.ErrorDescription); } [Theory] [InlineData("client_id", "")] [InlineData("", "client_secret")] public async Task ValidateIntrospectionRequest_ClientCredentialsRequestIsRejectedWhenCredentialsAreMissing(string identifier, string secret) { // Arrange var server = CreateAuthorizationServer(); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = identifier, ClientSecret = secret, Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error); Assert.Equal("The mandatory 'client_id' and/or 'client_secret' parameters are missing.", response.ErrorDescription); } [Fact] public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCannotBeFound() { // Arrange var manager = CreateApplicationManager(instance => { instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(value: null); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(manager); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); Assert.Equal("The specified 'client_id' parameter is invalid.", response.ErrorDescription); Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once()); } [Fact] public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(instance => { instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(false); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(manager); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.UnauthorizedClient, response.Error); Assert.Equal("This client application is not allowed to use the introspection endpoint.", response.ErrorDescription); Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateIntrospectionRequest_RequestsSentByPublicClientsAreRejected() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(instance => { instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Public)); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(manager); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); Assert.Equal("This client application is not allowed to use the introspection endpoint.", response.ErrorDescription); Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application, It.IsAny()), Times.Once()); } [Fact] public async Task ValidateIntrospectionRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() { // Arrange var application = new OpenIddictApplication(); var manager = CreateApplicationManager(instance => { instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(false); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(manager); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Equal(OpenIdConnectConstants.Errors.InvalidClient, response.Error); Assert.Equal("The specified client credentials are invalid.", response.ErrorDescription); Mock.Get(manager).Verify(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetClientTypeAsync(application, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()), Times.Once()); } [Theory] [InlineData(OpenIdConnectConstants.TokenUsages.AuthorizationCode)] [InlineData(OpenIdConnectConstants.TokenUsages.IdToken)] [InlineData(OpenIdConnectConstants.TokenUsages.RefreshToken)] public async Task HandleIntrospectionRequest_RequestIsRejectedWhenTokenIsNotAnAccessToken(string type) { // Arrange var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56"); ticket.SetTokenUsage(type); var format = new Mock>(); format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) .Returns(ticket); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(CreateApplicationManager(instance => { var application = new OpenIddictApplication(); instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); })); builder.Configure(options => options.AccessTokenFormat = format.Object); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); } [Fact] public async Task HandleIntrospectionRequest_RequestIsRejectedWhenAudienceIsMissing() { // Arrange var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56"); ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken); var format = new Mock>(); format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) .Returns(ticket); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(CreateApplicationManager(instance => { var application = new OpenIddictApplication(); instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); })); builder.Configure(options => options.AccessTokenFormat = format.Object); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); } [Fact] public async Task HandleIntrospectionRequest_RequestIsRejectedWhenClientIsNotAValidAudience() { // Arrange var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetAudiences("Contoso"); ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56"); ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken); var format = new Mock>(); format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) .Returns(ticket); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(CreateApplicationManager(instance => { var application = new OpenIddictApplication(); instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); })); builder.Configure(options => options.AccessTokenFormat = format.Object); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "2YotnFZFEjr1zCsicMWpAA" }); // Assert Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); } [Fact] public async Task HandleIntrospectionRequest_RequestIsRejectedWhenReferenceTokenIsUnknown() { // Arrange var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); var manager = CreateTokenManager(instance => { instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny())) .ReturnsAsync(value: null); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(CreateApplicationManager(instance => { var application = new OpenIddictApplication(); instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); })); builder.Services.AddSingleton(manager); builder.UseReferenceTokens(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI" }); // Assert Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Once()); } [Fact] public async Task HandleIntrospectionRequest_RequestIsRejectedWhenReferenceTokenIsInvalid() { // Arrange var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur"); var ticket = new AuthenticationTicket( new ClaimsPrincipal(identity), new AuthenticationProperties(), OpenIdConnectServerDefaults.AuthenticationScheme); ticket.SetAudiences("Fabrikam"); ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56"); ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken); var format = new Mock>(); format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA")) .Returns(ticket); var token = new OpenIddictToken(); var manager = CreateTokenManager(instance => { instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .Returns(new ValueTask("3E228451-1555-46F7-A471-951EFBA23A56")); instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .Returns(new ValueTask("2YotnFZFEjr1zCsicMWpAA")); instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.IsValidAsync(token, It.IsAny())) .ReturnsAsync(false); }); var server = CreateAuthorizationServer(builder => { builder.Services.AddSingleton(CreateApplicationManager(instance => { var application = new OpenIddictApplication(); instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny())) .ReturnsAsync(application); instance.Setup(mock => mock.HasPermissionAsync(application, OpenIddictConstants.Permissions.Endpoints.Introspection, It.IsAny())) .ReturnsAsync(true); instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny())) .Returns(new ValueTask(OpenIddictConstants.ClientTypes.Confidential)); instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny())) .ReturnsAsync(true); })); builder.Services.AddSingleton(manager); builder.Configure(options => options.AccessTokenFormat = format.Object); builder.UseReferenceTokens(); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest { ClientId = "Fabrikam", ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", Token = "QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI" }); // Assert Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } } }