Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIBA Customization Extensibility Points #1497

Merged
merged 6 commits into from
Jan 3, 2024
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 @@ -345,6 +345,20 @@ public static IIdentityServerBuilder AddCustomTokenRequestValidator<T>(this IIde
return builder;
}

/// <summary>
/// Adds the custom backchannel authentication request validator.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static IIdentityServerBuilder AddCustomBackchannelAuthenticationRequestValidator<T>(this IIdentityServerBuilder builder)
where T : class, ICustomBackchannelAuthenticationValidator
{
builder.Services.AddTransient<ICustomBackchannelAuthenticationValidator, T>();

return builder;
}

/// <summary>
/// Adds support for client authentication using JWT bearer assertions.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ public static IIdentityServerBuilder AddValidators(this IIdentityServerBuilder b
// optional
builder.Services.TryAddTransient<ICustomTokenValidator, DefaultCustomTokenValidator>();
builder.Services.TryAddTransient<ICustomAuthorizeRequestValidator, DefaultCustomAuthorizeRequestValidator>();
builder.Services.TryAddTransient<ICustomBackchannelAuthenticationValidator, DefaultCustomBackchannelAuthenticationValidator>();

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Duende.IdentityServer.Extensions;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Duende.IdentityServer.Hosting;
using Duende.IdentityServer.ResponseHandling;
Expand Down Expand Up @@ -67,7 +69,9 @@ await context.Response.WriteJsonAsync(new SuccessResultDto
{
auth_req_id = result.Response.AuthenticationRequestId,
expires_in = result.Response.ExpiresIn,
interval = result.Response.Interval
interval = result.Response.Interval,

Properties = result.Response.Properties
});
}
}
Expand All @@ -78,6 +82,9 @@ internal class SuccessResultDto
public string auth_req_id { get; set; }
public int expires_in { get; set; }
public int interval { get; set; }

[JsonExtensionData]
public Dictionary<string, object> Properties { get; set; }
#pragma warning restore IDE1006 // Naming Styles
}

Expand Down
7 changes: 7 additions & 0 deletions src/IdentityServer/Models/BackchannelUserLoginRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Duende.IdentityServer.Validation;
using System.Collections.Generic;
using System.Security.Claims;
using Duende.IdentityServer.ResponseHandling;

namespace Duende.IdentityServer.Models;

Expand Down Expand Up @@ -59,4 +60,10 @@ public class BackchannelUserLoginRequest
/// Gets or sets the validated resources.
/// </summary>
public ResourceValidationResult ValidatedResources { get; set; } = default!;

/// <summary>
/// Gets or sets a dictionary of custom properties that can pass additional
/// state to the notification process.
/// </summary>
public Dictionary<string, object> Properties { get; set; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public virtual async Task<BackchannelAuthenticationResponse> ProcessAsync(Backch
Tenant = validationResult.ValidatedRequest.Tenant,
IdP = validationResult.ValidatedRequest.IdP,
BindingMessage = validationResult.ValidatedRequest.BindingMessage,
Properties = validationResult.ValidatedRequest.Properties,
};

var requestId = await BackChannelAuthenticationRequestStore.CreateRequestAsync(request);
Expand All @@ -98,6 +99,7 @@ public virtual async Task<BackchannelAuthenticationResponse> ProcessAsync(Backch
AuthenticationRequestId = requestId,
ExpiresIn = request.Lifetime,
Interval = interval,
Properties = validationResult.ValidatedRequest.Properties
};

await UserLoginService.SendLoginRequestAsync(new BackchannelUserLoginRequest
Expand All @@ -111,6 +113,7 @@ await UserLoginService.SendLoginRequestAsync(new BackchannelUserLoginRequest
AuthenticationContextReferenceClasses = validationResult.ValidatedRequest.AuthenticationContextReferenceClasses,
Tenant = validationResult.ValidatedRequest.Tenant,
IdP = validationResult.ValidatedRequest.IdP,
Properties = validationResult.ValidatedRequest.Properties,
});

return response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


using System;
using System.Collections.Generic;

namespace Duende.IdentityServer.ResponseHandling;

Expand Down Expand Up @@ -58,4 +59,10 @@ public BackchannelAuthenticationResponse(string error, string errorDescription =
/// Gets or sets the interval.
/// </summary>
public int Interval { get; set; }

/// <summary>
/// Gets or sets a dictionary of custom properties that can pass additional
/// state in the response to the client application.
/// </summary>
public Dictionary<string, object> Properties { get; set; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ async Task<BackchannelUserLoginRequest> CreateAsync(BackChannelAuthenticationReq
RequestedResourceIndicators = request.RequestedResourceIndicators,
AuthenticationContextReferenceClasses = request.AuthenticationContextReferenceClasses,
BindingMessage = request.BindingMessage,
Properties = request.Properties
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

namespace Duende.IdentityServer.Validation;

/// <summary>
/// The validation context for a custom CIBA validator.
/// </summary>
public class CustomBackchannelAuthenticationRequestValidationContext
{
/// <summary>
/// Creates a new instance of the <see cref="CustomBackchannelAuthenticationRequestValidationContext"/>
/// </summary>
public CustomBackchannelAuthenticationRequestValidationContext(BackchannelAuthenticationRequestValidationResult validatedRequest)
{
ValidationResult = validatedRequest;
}
/// <summary>
/// Gets or sets the CIBA validation result.
/// </summary>
public BackchannelAuthenticationRequestValidationResult ValidationResult { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class BackchannelAuthenticationRequestValidator : IBackchannelAuthentic
private readonly ITokenValidator _tokenValidator;
private readonly IBackchannelAuthenticationUserValidator _backchannelAuthenticationUserValidator;
private readonly IJwtRequestValidator _jwtRequestValidator;
private readonly IJwtRequestUriHttpClient _jwtRequestUriHttpClient;
private readonly ICustomBackchannelAuthenticationValidator _customValidator;
private readonly ILogger<BackchannelAuthenticationRequestValidator> _logger;

private ValidatedBackchannelAuthenticationRequest _validatedRequest;
Expand All @@ -35,15 +35,15 @@ public BackchannelAuthenticationRequestValidator(
ITokenValidator tokenValidator,
IBackchannelAuthenticationUserValidator backchannelAuthenticationUserValidator,
IJwtRequestValidator jwtRequestValidator,
IJwtRequestUriHttpClient jwtRequestUriHttpClient,
ICustomBackchannelAuthenticationValidator customValidator,
ILogger<BackchannelAuthenticationRequestValidator> logger)
{
_options = options;
_resourceValidator = resourceValidator;
_tokenValidator = tokenValidator;
_backchannelAuthenticationUserValidator = backchannelAuthenticationUserValidator;
_jwtRequestValidator = jwtRequestValidator;
_jwtRequestUriHttpClient = jwtRequestUriHttpClient;
_customValidator = customValidator;
_logger = logger;
}

Expand Down Expand Up @@ -418,9 +418,18 @@ public async Task<BackchannelAuthenticationRequestValidationResult> ValidateRequ
}

_validatedRequest.Subject = userResult.Subject;
var result = new BackchannelAuthenticationRequestValidationResult(_validatedRequest);

var customValidationContext = new CustomBackchannelAuthenticationRequestValidationContext(result);
await _customValidator.ValidateAsync(customValidationContext);
if(customValidationContext.ValidationResult.IsError)
{
LogError("Custom validation of backchannel authorize request failed");
return Invalid(OidcConstants.BackchannelAuthenticationRequestErrors.InvalidRequest);
}

LogSuccess();
return new BackchannelAuthenticationRequestValidationResult(_validatedRequest);
return result;
}

private async Task<(bool Success, BackchannelAuthenticationRequestValidationResult ErrorResult)> TryValidateRequestObjectAsync()
Expand Down Expand Up @@ -528,4 +537,4 @@ private void LogSuccess()
{
LogWithRequestDetails(LogLevel.Information, "Backchannel authentication request validation success");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


using System.Threading.Tasks;

namespace Duende.IdentityServer.Validation;

/// <summary>
/// Default implementation of the CIBA validator extensibility point. This
/// validator deliberately does nothing.
/// </summary>
public class DefaultCustomBackchannelAuthenticationValidator : ICustomBackchannelAuthenticationValidator
{
/// <inheritdoc/>
public Task ValidateAsync(CustomBackchannelAuthenticationRequestValidationContext customValidationContext)
{
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


using System.Threading.Tasks;

namespace Duende.IdentityServer.Validation;

/// <summary>
/// Extensibility point for CIBA authentication request validation.
/// </summary>
public interface ICustomBackchannelAuthenticationValidator
{
/// <summary>
/// Validates a CIBA authentication request.
/// </summary>
/// <param name="customValidationContext"></param>
/// <returns></returns>
Task ValidateAsync(CustomBackchannelAuthenticationRequestValidationContext customValidationContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,10 @@ public class ValidatedBackchannelAuthenticationRequest : ValidatedRequest
/// Gets or sets the request object (either passed by value or retrieved by reference)
/// </summary>
public string? RequestObject { get; set; }

/// <summary>
/// Gets or sets a dictionary of custom properties that can pass
/// additional state to the back channel authentication process.
/// </summary>
public Dictionary<string, object> Properties { get; set; } = new();
}
7 changes: 6 additions & 1 deletion src/Storage/Models/BackChannelAuthenticationRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ public class BackChannelAuthenticationRequest
public string? SessionId { get; set; }

/// <summary>
/// Gets the description the user assigned to the client being authorized.
/// Gets or sets the description the user assigned to the client being authorized.
/// </summary>
public string? Description { get; set; }

/// <summary>
/// Gets or sets a dictionary of custom properties associated with this instance.
/// </summary>
public Dictionary<string, object> Properties { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.


using Duende.IdentityServer.Validation;
using System;
using System.Threading.Tasks;

namespace IntegrationTests.Common;

internal class MockCustomBackchannelAuthenticationValidator : ICustomBackchannelAuthenticationValidator
{
public CustomBackchannelAuthenticationRequestValidationContext Context { get; set; }


/// <summary>
/// An action that will be performed by the mock custom validator.
/// </summary>
public Action<CustomBackchannelAuthenticationRequestValidationContext> Thunk { get; set; } = delegate { };

public Task ValidateAsync(CustomBackchannelAuthenticationRequestValidationContext customValidationContext)
{
Thunk(customValidationContext);
Context = customValidationContext;
return Task.CompletedTask;
}
}
Loading