Browse Source

Update the token endpoint validation logic to reject scope=offline_access requests if the client application is not allowed to use the refresh token flow

pull/627/head
Kévin Chalet 8 years ago
parent
commit
e7abdab60d
  1. 4
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs
  2. 37
      src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs
  3. 46
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs
  4. 47
      test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs

4
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs

@ -374,8 +374,8 @@ namespace OpenIddict.Server
return; return;
} }
// Reject the request if the offline_access scope was request and if the // Reject the request if the offline_access scope was request and if
// application is not allowed to use the authorization code/implicit flows. // the application is not allowed to use the refresh token grant type.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) && if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.GrantTypes.RefreshToken)) !await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.GrantTypes.RefreshToken))
{ {

37
src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs

@ -182,19 +182,36 @@ namespace OpenIddict.Server
return; return;
} }
// Reject the request if the application is not allowed to use the specified grant type. if (!options.IgnoreGrantTypePermissions)
if (!options.IgnoreGrantTypePermissions &&
!await _applicationManager.HasPermissionAsync(application,
OpenIddictConstants.Permissions.Prefixes.GrantType + context.Request.GrantType))
{ {
_logger.LogError("The token request was rejected because the application '{ClientId}' was not allowed to " + // Reject the request if the application is not allowed to use the specified grant type.
"use the specified grant type: {GrantType}.", context.ClientId, context.Request.GrantType); if (!await _applicationManager.HasPermissionAsync(application,
OpenIddictConstants.Permissions.Prefixes.GrantType + context.Request.GrantType))
{
_logger.LogError("The token request was rejected because the application '{ClientId}' was not allowed to " +
"use the specified grant type: {GrantType}.", context.ClientId, context.Request.GrantType);
context.Reject( context.Reject(
error: OpenIdConnectConstants.Errors.UnauthorizedClient, error: OpenIdConnectConstants.Errors.UnauthorizedClient,
description: "This client application is not allowed to use the specified grant type."); description: "This client application is not allowed to use the specified grant type.");
return; return;
}
// Reject the request if the offline_access scope was request and if
// the application is not allowed to use the refresh token grant type.
if (context.Request.HasScope(OpenIdConnectConstants.Scopes.OfflineAccess) &&
!await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.GrantTypes.RefreshToken))
{
_logger.LogError("The token request was rejected because the application '{ClientId}' " +
"was not allowed to request the 'offline_access' scope.", context.ClientId);
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "The client application is not allowed to use the 'offline_access' scope.");
return;
}
} }
if (await _applicationManager.IsPublicAsync(application)) if (await _applicationManager.IsPublicAsync(application))

46
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs

@ -655,6 +655,52 @@ namespace OpenIddict.Server.Tests
Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application, permissions[0], It.IsAny<CancellationToken>()), Times.Once()); Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application, permissions[0], It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact]
public async Task ValidateAuthorizationRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenPermissionIsNotGranted()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
instance.Setup(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
builder.Configure(options => options.IgnoreGrantTypePermissions = false);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(AuthorizationEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = OpenIdConnectConstants.ResponseTypes.Code,
Scope = OpenIdConnectConstants.Scopes.OfflineAccess
});
// Assert
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error);
Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact] [Fact]
public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsInvalid() public async Task ValidateAuthorizationRequest_RequestIsRejectedWhenRedirectUriIsInvalid()
{ {

47
test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs

@ -387,6 +387,53 @@ namespace OpenIddict.Server.Tests
OpenIddictConstants.Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()), Times.Once()); OpenIddictConstants.Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact]
public async Task ValidateTokenRequest_RequestWithOfflineAccessScopeIsRejectedWhenRefreshTokenPermissionIsNotGranted()
{
// Arrange
var application = new OpenIddictApplication();
var manager = CreateApplicationManager(instance =>
{
instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.Password, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
instance.Setup(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
});
var server = CreateAuthorizationServer(builder =>
{
builder.Services.AddSingleton(manager);
builder.Configure(options => options.IgnoreGrantTypePermissions = false);
});
var client = new OpenIdConnectClient(server.CreateClient());
// Act
var response = await client.PostAsync(TokenEndpoint, new OpenIdConnectRequest
{
ClientId = "Fabrikam",
GrantType = OpenIdConnectConstants.GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w",
Scope = OpenIdConnectConstants.Scopes.OfflineAccess
});
// Assert
Assert.Equal(OpenIdConnectConstants.Errors.InvalidRequest, response.Error);
Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.HasPermissionAsync(application,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.Once());
}
[Fact] [Fact]
public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected() public async Task ValidateTokenRequest_ClientCredentialsRequestFromPublicClientIsRejected()
{ {

Loading…
Cancel
Save