Skip to content
This repository has been archived by the owner on Jul 31, 2024. It is now read-only.

Validator tests #2391

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public DeviceAuthorizationRequestValidationLog(ValidatedDeviceAuthorizationReque
ClientName = request.Client.ClientName;
}

if (request.Scopes != null)
if (request.RequestedScopes != null)
{
Scopes = request.Scopes.ToSpaceSeparatedString();
Scopes = request.RequestedScopes.ToSpaceSeparatedString();
}
}

Expand Down
168 changes: 90 additions & 78 deletions src/IdentityServer4/Validation/DeviceAuthorizationRequestValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@


using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -18,134 +17,147 @@ namespace IdentityServer4.Validation
{
internal class DeviceAuthorizationRequestValidator : IDeviceAuthorizationRequestValidator
{
private readonly IdentityServerOptions options;
private readonly ScopeValidator scopeValidator;
private readonly ILogger<DeviceAuthorizationRequestValidator> logger;

private ValidatedDeviceAuthorizationRequest validatedRequest;

private readonly IdentityServerOptions _options;
private readonly ScopeValidator _scopeValidator;
private readonly ILogger<DeviceAuthorizationRequestValidator> _logger;

public DeviceAuthorizationRequestValidator(
IdentityServerOptions options,
ScopeValidator scopeValidator,
ILogger<DeviceAuthorizationRequestValidator> logger)
{
this.options = options;
this.scopeValidator = scopeValidator;
this.logger = logger;
_options = options;
_scopeValidator = scopeValidator;
_logger = logger;
}

public async Task<DeviceAuthorizationRequestValidationResult> ValidateAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult)
{
logger.LogDebug("Start device authorization request validation");
_logger.LogDebug("Start device authorization request validation");

validatedRequest = new ValidatedDeviceAuthorizationRequest
var request = new ValidatedDeviceAuthorizationRequest
{
Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)),
Options = options
Options = _options
};

if (clientValidationResult == null) throw new ArgumentNullException(nameof(clientValidationResult));
validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret);

// check if client Authorized for device flow
if (!validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.DeviceFlow))
var clientResult = ValidateClient(request, clientValidationResult);
if (clientResult.IsError)
{
LogError("Client not authorized for device flow");
return Invalid(OidcConstants.TokenErrors.UnauthorizedClient);
return clientResult;
}

if (!await ValidateRequestedScopesAsync(parameters))
var scopeResult = await ValidateScopeAsync(request);
if (scopeResult.IsError)
{
return Invalid(OidcConstants.TokenErrors.InvalidScope);
return scopeResult;
}

logger.LogDebug("{clientId} device authorization request validation success", validatedRequest.Client.ClientId);
return Valid();
_logger.LogDebug("{clientId} device authorization request validation success", request.Client.ClientId);
return Valid(request);
}

private DeviceAuthorizationRequestValidationResult Valid(ValidatedDeviceAuthorizationRequest request)
{
return new DeviceAuthorizationRequestValidationResult(request);
}

private DeviceAuthorizationRequestValidationResult Invalid(ValidatedDeviceAuthorizationRequest request, string error = OidcConstants.AuthorizeErrors.InvalidRequest, string description = null)
{
return new DeviceAuthorizationRequestValidationResult(request, error, description);
}

private DeviceAuthorizationRequestValidationResult Valid()
private void LogError(string message, ValidatedDeviceAuthorizationRequest request)
{
return new DeviceAuthorizationRequestValidationResult(validatedRequest);
var requestDetails = new DeviceAuthorizationRequestValidationLog(request);
_logger.LogError(message + "\n{requestDetails}", requestDetails);
}

private DeviceAuthorizationRequestValidationResult Invalid(string error, string errorDescription = null)
private void LogError(string message, string detail, ValidatedDeviceAuthorizationRequest request)
{
return new DeviceAuthorizationRequestValidationResult(validatedRequest, error, errorDescription);
var requestDetails = new DeviceAuthorizationRequestValidationLog(request);
_logger.LogError(message + ": {detail}\n{requestDetails}", detail, requestDetails);
}

private void LogError(string message = null, params object[] values)
private DeviceAuthorizationRequestValidationResult ValidateClient(ValidatedDeviceAuthorizationRequest request, ClientSecretValidationResult clientValidationResult)
{
if (!string.IsNullOrWhiteSpace(message))
//////////////////////////////////////////////////////////
// set client & secret
//////////////////////////////////////////////////////////
if (clientValidationResult == null) throw new ArgumentNullException(nameof(clientValidationResult));
request.SetClient(clientValidationResult.Client, clientValidationResult.Secret);

//////////////////////////////////////////////////////////
// check if client protocol type is oidc
//////////////////////////////////////////////////////////
if (request.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect)
{
LogError("Invalid protocol type for OIDC authorize endpoint", request.Client.ProtocolType, request);
return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid protocol");
}

//////////////////////////////////////////////////////////
// check if client allows device flow
//////////////////////////////////////////////////////////
if (!request.Client.AllowedGrantTypes.Contains(GrantType.DeviceFlow))
{
try
{
logger.LogError(message, values);
}
catch (Exception ex)
{
logger.LogError("Error logging {exception}", ex.Message);
}
LogError("Client not configured for device flow", GrantType.DeviceFlow, request);
return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient);
}

var details = new DeviceAuthorizationRequestValidationLog(validatedRequest);
logger.LogError("{details}", details);
return Valid(request);
}

// HACK: Copied from TokenRequestValidator
private async Task<bool> ValidateRequestedScopesAsync(NameValueCollection parameters)
private async Task<DeviceAuthorizationRequestValidationResult> ValidateScopeAsync(ValidatedDeviceAuthorizationRequest request)
{
var scopes = parameters.Get(OidcConstants.TokenRequest.Scope);

if (scopes.IsMissing())
//////////////////////////////////////////////////////////
// scope must be present
//////////////////////////////////////////////////////////
var scope = request.Raw.Get(OidcConstants.AuthorizeRequest.Scope);
if (scope.IsMissing())
{
logger.LogTrace("Client provided no scopes - checking allowed scopes list");

if (!validatedRequest.Client.AllowedScopes.IsNullOrEmpty())
{
var clientAllowedScopes = new List<string>(validatedRequest.Client.AllowedScopes);
if (validatedRequest.Client.AllowOfflineAccess)
{
clientAllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess);
}
scopes = clientAllowedScopes.ToSpaceSeparatedString();
logger.LogTrace("Defaulting to: {scopes}", scopes);
}
else
{
LogError("No allowed scopes configured for {clientId}", validatedRequest.Client.ClientId);
return false;
}
LogError("scope is missing", request);
return Invalid(request, description: "Invalid scope");
}

if (scopes.Length > options.InputLengthRestrictions.Scope)
if (scope.Length > _options.InputLengthRestrictions.Scope)
{
LogError("Scope parameter exceeds max allowed length");
return false;
LogError("scopes too long.", request);
return Invalid(request, description: "Invalid scope");
}

var requestedScopes = scopes.ParseScopesString();
request.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList();

if (request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId))
{
request.IsOpenIdRequest = true;
}

if (requestedScopes == null)
//////////////////////////////////////////////////////////
// check if scopes are valid/supported
//////////////////////////////////////////////////////////
if (await _scopeValidator.AreScopesValidAsync(request.RequestedScopes) == false)
{
LogError("No scopes found in request");
return false;
return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope);
}

if (!await scopeValidator.AreScopesAllowedAsync(validatedRequest.Client, requestedScopes))
if (_scopeValidator.ContainsOpenIdScopes && !request.IsOpenIdRequest)
{
LogError();
return false;
LogError("Identity related scope requests, but no openid scope", request);
return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope);
}

if (!(await scopeValidator.AreScopesValidAsync(requestedScopes)))
//////////////////////////////////////////////////////////
// check scopes and scope restrictions
//////////////////////////////////////////////////////////
if (await _scopeValidator.AreScopesAllowedAsync(request.Client, request.RequestedScopes) == false)
{
LogError();
return false;
return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid scope");
}

validatedRequest.Scopes = requestedScopes;
validatedRequest.ValidatedScopes = scopeValidator;
return true;
request.ValidatedScopes = _scopeValidator;

return Valid(request);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ namespace IdentityServer4.Validation
public class ValidatedDeviceAuthorizationRequest : ValidatedRequest
{
/// <summary>
/// Gets or sets the scopes.
/// Gets or sets the requested scopes.
/// </summary>
/// <value>
/// The scopes.
/// </value>
public IEnumerable<string> Scopes { get; set; }
public IEnumerable<string> RequestedScopes { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this instance is open identifier request.
Expand Down
Loading