|
|
@ -1192,7 +1192,7 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted() |
|
|
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1202,38 +1202,71 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
Permissions.Endpoints.Token, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
.ReturnsAsync(false); |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
options.Configure(options => options.IgnoreEndpointPermissions = false); |
|
|
// Act
|
|
|
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
|
|
|
{ |
|
|
|
|
|
ClientId = "Fabrikam", |
|
|
|
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
|
|
|
GrantType = GrantTypes.ClientCredentials |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
|
|
|
Assert.Equal("The specified 'grant_type' parameter is not valid for this client application.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
public async Task ValidateTokenRequest_ClientSecretCannotBeUsedByPublicClients() |
|
|
|
|
|
{ |
|
|
|
|
|
// Arrange
|
|
|
|
|
|
var application = new OpenIddictApplication(); |
|
|
|
|
|
|
|
|
|
|
|
var manager = CreateApplicationManager(mock => |
|
|
|
|
|
{ |
|
|
|
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.Services.AddSingleton(manager); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
|
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.Password, |
|
|
Username = "johndoe", |
|
|
Username = "johndoe", |
|
|
Password = "A3ddj3w" |
|
|
Password = "A3ddj3w" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal("This client application is not allowed to use the token endpoint.", response.ErrorDescription); |
|
|
Assert.Equal("The 'client_secret' parameter is not valid for this client application.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Permissions.Endpoints.Token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenGrantTypePermissionIsNotGranted() |
|
|
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1243,38 +1276,73 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Confidential); |
|
|
.ReturnsAsync(false); |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
options.Configure(options => options.IgnoreGrantTypePermissions = false); |
|
|
// Act
|
|
|
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
|
|
|
{ |
|
|
|
|
|
ClientId = "Fabrikam", |
|
|
|
|
|
ClientSecret = null, |
|
|
|
|
|
GrantType = GrantTypes.Password, |
|
|
|
|
|
Username = "johndoe", |
|
|
|
|
|
Password = "A3ddj3w" |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
|
|
|
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
public async Task ValidateTokenRequest_ClientSecretIsRequiredForHybridClients() |
|
|
|
|
|
{ |
|
|
|
|
|
// Arrange
|
|
|
|
|
|
var application = new OpenIddictApplication(); |
|
|
|
|
|
|
|
|
|
|
|
var manager = CreateApplicationManager(mock => |
|
|
|
|
|
{ |
|
|
|
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(ClientTypes.Hybrid); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.Services.AddSingleton(manager); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
|
|
|
ClientSecret = null, |
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.Password, |
|
|
Username = "johndoe", |
|
|
Username = "johndoe", |
|
|
Password = "A3ddj3w" |
|
|
Password = "A3ddj3w" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
Assert.Equal("This client application is not allowed to use the specified grant type.", response.ErrorDescription); |
|
|
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenPermissionIsNotGranted() |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1284,12 +1352,50 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Confidential); |
|
|
.ReturnsAsync(true); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(false); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
|
|
|
{ |
|
|
|
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Act
|
|
|
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
|
|
|
{ |
|
|
|
|
|
ClientId = "Fabrikam", |
|
|
|
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
|
|
|
GrantType = GrantTypes.Password, |
|
|
|
|
|
Username = "johndoe", |
|
|
|
|
|
Password = "A3ddj3w" |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
|
|
|
Assert.Equal("The specified client credentials are invalid.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenEndpointPermissionIsNotGranted() |
|
|
|
|
|
{ |
|
|
|
|
|
// Arrange
|
|
|
|
|
|
var application = new OpenIddictApplication(); |
|
|
|
|
|
|
|
|
|
|
|
var manager = CreateApplicationManager(mock => |
|
|
|
|
|
{ |
|
|
|
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>())) |
|
|
Permissions.Endpoints.Token, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(false); |
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -1297,7 +1403,7 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
options.Configure(options => options.IgnoreGrantTypePermissions = false); |
|
|
options.Configure(options => options.IgnoreEndpointPermissions = false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
@ -1306,20 +1412,20 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.Password, |
|
|
Username = "johndoe", |
|
|
Username = "johndoe", |
|
|
Password = "A3ddj3w", |
|
|
Password = "A3ddj3w" |
|
|
Scope = Scopes.OfflineAccess |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription); |
|
|
Assert.Equal("This client application is not allowed to use the token endpoint.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Permissions.Endpoints.Token, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenGrantTypePermissionIsNotGranted() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1329,33 +1435,38 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
|
|
|
options.Configure(options => options.IgnoreGrantTypePermissions = false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.ClientCredentials |
|
|
Username = "johndoe", |
|
|
|
|
|
Password = "A3ddj3w" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
Assert.Equal(Errors.UnauthorizedClient, response.Error); |
|
|
Assert.Equal("The specified 'grant_type' parameter is not valid for this client application.", response.ErrorDescription); |
|
|
Assert.Equal("This client application is not allowed to use the specified grant type.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenScopePermissionIsNotGranted() |
|
|
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenPermissionIsNotGranted() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1365,15 +1476,12 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>())) |
|
|
Permissions.GrantTypes.Password, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(true); |
|
|
.ReturnsAsync(true); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>())) |
|
|
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(false); |
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -1381,8 +1489,7 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
options.RegisterScopes(Scopes.Email, Scopes.Profile); |
|
|
options.Configure(options => options.IgnoreGrantTypePermissions = false); |
|
|
options.Configure(options => options.IgnoreScopePermissions = false); |
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
@ -1392,25 +1499,19 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.Password, |
|
|
Username = "johndoe", |
|
|
Username = "johndoe", |
|
|
Password = "A3ddj3w", |
|
|
Password = "A3ddj3w", |
|
|
Scope = "openid offline_access profile email" |
|
|
Scope = Scopes.OfflineAccess |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal("This client application is not allowed to use the specified scope.", response.ErrorDescription); |
|
|
Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Permissions.Prefixes.Scope + Scopes.OpenId, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.OfflineAccess, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_ClientSecretCannotBeUsedByPublicClients() |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenScopePermissionIsNotGranted() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1422,33 +1523,50 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(true); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
|
|
|
options.RegisterScopes(Scopes.Email, Scopes.Profile); |
|
|
|
|
|
options.Configure(options => options.IgnoreScopePermissions = false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
|
|
|
GrantType = GrantTypes.Password, |
|
|
GrantType = GrantTypes.Password, |
|
|
Username = "johndoe", |
|
|
Username = "johndoe", |
|
|
Password = "A3ddj3w" |
|
|
Password = "A3ddj3w", |
|
|
|
|
|
Scope = "openid offline_access profile email" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal("The 'client_secret' parameter is not valid for this client application.", response.ErrorDescription); |
|
|
Assert.Equal("This client application is not allowed to use the specified scope.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Permissions.Prefixes.Scope + Scopes.OpenId, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.OfflineAccess, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, |
|
|
|
|
|
Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_ClientSecretIsRequiredForConfidentialClients() |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenCodeVerifierIsMissingWithPkceFeatureEnforced() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1459,7 +1577,11 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Confidential); |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasRequirementAsync(application, |
|
|
|
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(true); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
@ -1471,22 +1593,22 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
ClientSecret = null, |
|
|
Code = "SplxlOBeZQQYbYS6WxSbIA", |
|
|
GrantType = GrantTypes.Password, |
|
|
CodeVerifier = null, |
|
|
Username = "johndoe", |
|
|
GrantType = GrantTypes.AuthorizationCode, |
|
|
Password = "A3ddj3w" |
|
|
RedirectUri = "http://www.fabrikam.com/path" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
Assert.Equal(Errors.InvalidRequest, response.Error); |
|
|
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription); |
|
|
Assert.Equal("The mandatory 'code_verifier' parameter is missing.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_ClientSecretIsRequiredForHybridClients() |
|
|
public async Task ValidateTokenRequest_RequestIsValidatedWhenCodeVerifierIsMissingWithPkceFeatureNotEnforced() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1497,34 +1619,59 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Hybrid); |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.HasRequirementAsync(application, |
|
|
|
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>())) |
|
|
|
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
|
|
|
options.AddEventHandler<ProcessAuthenticationContext>(builder => |
|
|
|
|
|
{ |
|
|
|
|
|
builder.UseInlineHandler(context => |
|
|
|
|
|
{ |
|
|
|
|
|
Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); |
|
|
|
|
|
Assert.Equal(TokenTypeHints.AuthorizationCode, context.TokenType); |
|
|
|
|
|
|
|
|
|
|
|
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) |
|
|
|
|
|
.SetPresenters("Fabrikam") |
|
|
|
|
|
.SetInternalTokenId("3E228451-1555-46F7-A471-951EFBA23A56") |
|
|
|
|
|
.SetClaim(Claims.Subject, "Bob le Bricoleur"); |
|
|
|
|
|
|
|
|
|
|
|
return default; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
|
|
|
options.SetRevocationEndpointUris(Array.Empty<Uri>()); |
|
|
|
|
|
options.DisableTokenStorage(); |
|
|
|
|
|
options.DisableSlidingExpiration(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
ClientSecret = null, |
|
|
Code = "SplxlOBeZQQYbYS6WxSbIA", |
|
|
GrantType = GrantTypes.Password, |
|
|
CodeVerifier = null, |
|
|
Username = "johndoe", |
|
|
GrantType = GrantTypes.AuthorizationCode, |
|
|
Password = "A3ddj3w" |
|
|
RedirectUri = "http://www.fabrikam.com/path" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
Assert.NotNull(response.AccessToken); |
|
|
Assert.Equal("The 'client_secret' parameter required for this client application is missing.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), Times.Once()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public async Task ValidateTokenRequest_RequestIsRejectedWhenClientCredentialsAreInvalid() |
|
|
public async Task ValidateTokenRequest_RequestIsValidatedWhenCodeVerifierIsPresentWithPkceFeatureEnforced() |
|
|
{ |
|
|
{ |
|
|
// Arrange
|
|
|
// Arrange
|
|
|
var application = new OpenIddictApplication(); |
|
|
var application = new OpenIddictApplication(); |
|
|
@ -1535,34 +1682,57 @@ namespace OpenIddict.Server.FunctionalTests |
|
|
.ReturnsAsync(application); |
|
|
.ReturnsAsync(application); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(ClientTypes.Confidential); |
|
|
.ReturnsAsync(ClientTypes.Public); |
|
|
|
|
|
|
|
|
mock.Setup(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>())) |
|
|
mock.Setup(manager => manager.HasRequirementAsync(application, |
|
|
|
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>())) |
|
|
.ReturnsAsync(false); |
|
|
.ReturnsAsync(false); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
var client = CreateClient(options => |
|
|
var client = CreateClient(options => |
|
|
{ |
|
|
{ |
|
|
|
|
|
options.AddEventHandler<ProcessAuthenticationContext>(builder => |
|
|
|
|
|
{ |
|
|
|
|
|
builder.UseInlineHandler(context => |
|
|
|
|
|
{ |
|
|
|
|
|
Assert.Equal("SplxlOBeZQQYbYS6WxSbIA", context.Token); |
|
|
|
|
|
Assert.Equal(TokenTypeHints.AuthorizationCode, context.TokenType); |
|
|
|
|
|
|
|
|
|
|
|
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) |
|
|
|
|
|
.SetPresenters("Fabrikam") |
|
|
|
|
|
.SetInternalTokenId("3E228451-1555-46F7-A471-951EFBA23A56") |
|
|
|
|
|
.SetClaim(Claims.Subject, "Bob le Bricoleur") |
|
|
|
|
|
.SetClaim(Claims.Private.CodeChallenge, "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM") |
|
|
|
|
|
.SetClaim(Claims.Private.CodeChallengeMethod, CodeChallengeMethods.Sha256); |
|
|
|
|
|
|
|
|
|
|
|
return default; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
options.Services.AddSingleton(manager); |
|
|
options.Services.AddSingleton(manager); |
|
|
|
|
|
|
|
|
|
|
|
options.SetRevocationEndpointUris(Array.Empty<Uri>()); |
|
|
|
|
|
options.DisableTokenStorage(); |
|
|
|
|
|
options.DisableSlidingExpiration(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Act
|
|
|
// Act
|
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
var response = await client.PostAsync("/connect/token", new OpenIddictRequest |
|
|
{ |
|
|
{ |
|
|
ClientId = "Fabrikam", |
|
|
ClientId = "Fabrikam", |
|
|
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw", |
|
|
Code = "SplxlOBeZQQYbYS6WxSbIA", |
|
|
GrantType = GrantTypes.Password, |
|
|
CodeVerifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", |
|
|
Username = "johndoe", |
|
|
GrantType = GrantTypes.AuthorizationCode, |
|
|
Password = "A3ddj3w" |
|
|
RedirectUri = "http://www.fabrikam.com/path" |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Assert
|
|
|
// Assert
|
|
|
Assert.Equal(Errors.InvalidClient, response.Error); |
|
|
Assert.NotNull(response.AccessToken); |
|
|
Assert.Equal("The specified client credentials are invalid.", response.ErrorDescription); |
|
|
|
|
|
|
|
|
|
|
|
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, |
|
|
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce()); |
|
|
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), Times.Never()); |
|
|
Mock.Get(manager).Verify(manager => manager.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()), Times.Once()); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[Theory] |
|
|
[Theory] |
|
|
|