Skip to content

Commit

Permalink
Merge pull request #1270 from DuendeSoftware/joe/dcr-refactor-processor
Browse files Browse the repository at this point in the history
Joe/dcr refactor processor
  • Loading branch information
brockallen committed May 9, 2023
2 parents c6a048a + 76c46c3 commit 9a0f56b
Show file tree
Hide file tree
Showing 22 changed files with 393 additions and 545 deletions.
40 changes: 18 additions & 22 deletions hosts/Configuration/CustomClientRegistrationProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Configuration.RequestProcessing;
using Duende.IdentityServer.Configuration.Configuration;
using Duende.IdentityServer.Configuration.Validation.DynamicClientRegistration;
using Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;
using Duende.IdentityServer.Configuration.Models;

namespace IdentityServerHost;

Expand All @@ -21,44 +22,39 @@ internal class CustomClientRegistrationProcessor : DynamicClientRegistrationRequ
_clients = clients;
}


protected override async Task<RequestProcessingStep> AddClientId(DynamicClientRegistrationValidatedRequest validatedRequest)
protected override async Task<IStepResult> AddClientId(DynamicClientRegistrationContext context)
{
if (validatedRequest.OriginalRequest.Extensions.TryGetValue("client_id", out var clientIdParameter))
if (context.Request.Extensions.TryGetValue("client_id", out var clientIdParameter))
{
var clientId = clientIdParameter.ToString();
if(_clients.Any(c => c.ClientId == clientId))
{

return new RequestProcessingStepFailure
{
Error = "Duplicate client id",
ErrorDescription = "Attempt to register a client with a client id that has already been registered"
};
return new DynamicClientRegistrationError(
"Duplicate client id",
"Attempt to register a client with a client id that has already been registered"
);
}
else
{
validatedRequest.Client.ClientId = clientId;
return new RequestProcessingStepSuccess();
context.Client.ClientId = clientId;
return new SuccessfulStep();
}
}
return await base.AddClientId(validatedRequest);
return await base.AddClientId(context);
}

protected override async Task<RequestProcessingStep<(Secret secret, string plainText)>> GenerateSecret(DynamicClientRegistrationValidatedRequest validatedRequest)
protected override async Task<(Secret, string)> GenerateSecret(DynamicClientRegistrationContext context)
{
if(validatedRequest.OriginalRequest.Extensions.TryGetValue("client_secret", out var secretParam))
if(context.Request.Extensions.TryGetValue("client_secret", out var secretParam))
{
var secretPlainText = secretParam.ToString();
var secret = new Secret(secretPlainText.Sha256());
return new RequestProcessingStepSuccess<(Secret secret, string plainText)>
{
StepResult = (secret, secretPlainText)
};
var plainText = secretParam.ToString();
var secret = new Secret(plainText.Sha256());

return (secret, plainText);
}
else
{
return await base.GenerateSecret(validatedRequest);
return await base.GenerateSecret(context);
}

}
Expand Down
31 changes: 17 additions & 14 deletions src/Configuration/Endpoints/DynamicClientRegistrationEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Net.Http.Headers;
using System.Text.Json;
using Duende.IdentityServer.Configuration.Models;
using Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;
using Duende.IdentityServer.Configuration.RequestProcessing;
using Duende.IdentityServer.Configuration.ResponseGeneration;
Expand Down Expand Up @@ -40,40 +41,42 @@ public class DynamicClientRegistrationEndpoint
/// <summary>
/// Processes requests to the dynamic client registration endpoint
/// </summary>
public async Task Process(HttpContext context)
public async Task Process(HttpContext httpContext)
{
// Check content type
if (!HasCorrectContentType(context.Request))
if (!HasCorrectContentType(httpContext.Request))
{
await _responseGenerator.WriteContentTypeError(context);
await _responseGenerator.WriteContentTypeError(httpContext);
return;
}

// Parse body
var request = await TryParseAsync(context.Request);
var request = await TryParseAsync(httpContext.Request);
if (request == null)
{
await _responseGenerator.WriteBadRequestError(context);
await _responseGenerator.WriteBadRequestError(httpContext);
return;
}

var dcrContext = new DynamicClientRegistrationContext(request, httpContext.User);

// Validate request values
var result = await _validator.ValidateAsync(request, context.User);
var validationResult = await _validator.ValidateAsync(dcrContext);

if (result is DynamicClientRegistrationValidationError validationError)
if (validationResult is DynamicClientRegistrationError validationError)
{
await _responseGenerator.WriteValidationError(context, validationError);
await _responseGenerator.WriteError(httpContext, validationError);
}
else if (result is DynamicClientRegistrationValidatedRequest validatedRequest)
else
{
var response = await _processor.ProcessAsync(validatedRequest);
if(response is DynamicClientRegistrationErrorResponse processingFailure)
var processingResult = await _processor.ProcessAsync(dcrContext);
if(processingResult is DynamicClientRegistrationError processingFailure)
{
await _responseGenerator.WriteProcessingError(context, processingFailure);
await _responseGenerator.WriteError(httpContext, processingFailure);
}
else if (response is DynamicClientRegistrationResponse success)
else if (processingResult is DynamicClientRegistrationResponse success)
{
await _responseGenerator.WriteSuccessResponse(context, success);
await _responseGenerator.WriteSuccessResponse(httpContext, success);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,31 @@

using System.Security.Claims;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;

namespace Duende.IdentityServer.Configuration.Validation.DynamicClientRegistration;
namespace Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;

/// <summary>
/// Represents the context of validation for dynamic client registration,
/// including the original DCR request, the client model that is built up
/// through validation, the caller who made the DCR request, and other
/// Represents the context for a dynamic client registration request, including
/// the original DCR request, the client model that is built up through
/// validation and processing, the caller who made the DCR request, and other
/// contextual information.
/// </summary>
public class DynamicClientRegistrationValidationContext
public class DynamicClientRegistrationContext
{
/// <summary>
/// Initializes a new instance of the <see
/// cref="DynamicClientRegistrationValidationContext"/> class.
/// cref="DynamicClientRegistrationContext"/> class.
/// </summary>
/// <param name="request">The original dynamic client registration request.</param>
/// <param name="caller">The <see cref="ClaimsPrincipal"/> that made the DCR request.</param>
public DynamicClientRegistrationValidationContext(DynamicClientRegistrationRequest request, ClaimsPrincipal caller)
public DynamicClientRegistrationContext(DynamicClientRegistrationRequest request, ClaimsPrincipal caller)
{
Request = request;
Caller = caller;
}

/// <summary>
/// The client model that is built up through validation.
/// The client model that is built up through validation and processing.
/// </summary>
public Client Client { get; set; } = new();

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;

/// <summary>
/// Represents the response to a successful dynamic client registration client.
/// Represents the response to a successful dynamic client registration request.
/// </summary>
public class DynamicClientRegistrationResponse : DynamicClientRegistrationRequest, IDynamicClientRegistrationResponse
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
namespace Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;

/// <summary>
/// Represents the response to a dynamic client registration request.
/// Marker interface for the response to a dynamic client registration request.
/// </summary>
public interface IDynamicClientRegistrationResponse
{
// This interface deliberately does not define any members
// It serves as a marker interface for classes that implement it
}
3 changes: 0 additions & 3 deletions src/Configuration/Models/DynamicClientRegistration/KeySet.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


namespace Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;

// REVIEW - Should we rename this type?

/// <summary>
/// Represents a JSON Web Key Set.
/// </summary>
Expand Down
40 changes: 40 additions & 0 deletions src/Configuration/Models/DynamicClientRegistrationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using Duende.IdentityServer.Configuration.Models.DynamicClientRegistration;
using Duende.IdentityServer.Configuration.Validation.DynamicClientRegistration;

namespace Duende.IdentityServer.Configuration.Models;

/// <summary>
/// Represents an error during dynamic client registration.
/// </summary>
public class DynamicClientRegistrationError : IStepResult, IDynamicClientRegistrationResponse, IDynamicClientRegistrationValidationResult
{
/// <summary>
/// Initializes a new instance of the <see
/// cref="DynamicClientRegistrationError"/> class.
/// </summary>
/// <param name="error">The error code for the error that occurred. Error
/// codes defined by RFC 7591 are defined as constants in the <see
/// cref="DynamicClientRegistrationErrors" /> class.</param>
/// <param name="errorDescription">A human-readable description of the error
/// that occurred during validation or processing.</param>
public DynamicClientRegistrationError(string error, string errorDescription)
{
Error = error;
ErrorDescription = errorDescription;
}

/// <summary>
/// Gets or sets the error code for the error that occurred. Error codes
/// defined by RFC 7591 are defined as constants in the <see
/// cref="DynamicClientRegistrationErrors" /> class.
/// </summary>
public string Error { get; set; }

/// <summary>
/// Gets or sets a human-readable description of the error that occurred.
/// </summary>
public string ErrorDescription { get; set; }
}
38 changes: 38 additions & 0 deletions src/Configuration/Models/IStepResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

namespace Duende.IdentityServer.Configuration.Models;

/// <summary>
/// Represents the result of a step in dynamic client registration validation or
/// processing.
/// </summary>
public interface IStepResult
{
}

/// <summary>
/// Static helper class for creating instances of IStepResult implementations,
/// wrapped in tasks.
/// </summary>
public static class StepResult
{
/// <summary>
/// Creates a step result that represents failure, wrapped in a task.
/// </summary>
/// <param name="errorDescription"></param>
/// <param name="error"></param>
/// <returns>A task that returns an <see cref="IStepResult"/>, which either
/// represents that this step succeeded or failed.</returns>
public static Task<IStepResult> Failure(string errorDescription,
string error = DynamicClientRegistrationErrors.InvalidClientMetadata) =>
Task.FromResult<IStepResult>(new DynamicClientRegistrationError(error, errorDescription));

/// <summary>
/// Creates a step result that represents success, wrapped in a task.
/// </summary>
/// <returns>A task that returns an <see cref="IStepResult"/>, which either
/// represents that this step succeeded or failed.</returns>
public static Task<IStepResult> Success() =>
Task.FromResult<IStepResult>(new SuccessfulStep());
}
11 changes: 11 additions & 0 deletions src/Configuration/Models/SuccessfulStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

namespace Duende.IdentityServer.Configuration.Models;

/// <summary>
/// Represents a successful validation or processing step.
/// </summary>
public class SuccessfulStep : IStepResult
{
}
Loading

0 comments on commit 9a0f56b

Please sign in to comment.