From 48b3e22d96b90816bcd840e49d25245bcdd5014c Mon Sep 17 00:00:00 2001 From: Brock Allen Date: Wed, 7 Jun 2023 16:10:48 -0400 Subject: [PATCH] add more response generators --- .../BuilderExtensions/Core.cs | 17 +++++- .../Results/AuthorizeInteractionPageResult.cs | 5 +- .../Endpoints/Results/AuthorizeResult.cs | 13 ++++- .../BackchannelAuthenticationResult.cs | 15 ++++- .../Endpoints/Results/BadRequestResult.cs | 37 ++++++++---- .../Endpoints/Results/CheckSessionResult.cs | 24 ++++---- .../Results/DeviceAuthorizationResult.cs | 30 +++++++--- .../Results/DiscoveryDocumentResult.cs | 7 +-- .../Endpoints/Results/EndPointResult.cs | 2 +- .../Results/EndSessionCallbackResult.cs | 57 ++++++++++--------- .../Endpoints/Results/EndSessionResult.cs | 38 +++++-------- .../Endpoints/Results/IntrospectionResult.cs | 21 ++++--- .../Endpoints/Results/JsonWebKeysResult.cs | 18 +++--- .../Results/ProtectedResourceErrorResult.cs | 44 ++++++++++---- .../Endpoints/Results/StatusCodeResult.cs | 16 +++--- .../Endpoints/Results/TokenErrorResult.cs | 32 ++++++++--- .../Endpoints/Results/TokenResult.cs | 42 +++++++++----- .../Results/TokenRevocationErrorResult.cs | 23 ++++---- .../Endpoints/Results/UserInfoResult.cs | 26 +++++++-- .../EndSessionCallbackResultTests.cs | 8 +-- .../Results/CheckSessionResultTests.cs | 12 ++-- .../Results/EndSessionCallbackResultTests.cs | 12 ++-- .../Results/EndSessionResultTests.cs | 10 ++-- 23 files changed, 307 insertions(+), 202 deletions(-) diff --git a/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs b/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs index 05c8bb910..23f99af1f 100644 --- a/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs +++ b/src/IdentityServer/Configuration/DependencyInjection/BuilderExtensions/Core.cs @@ -124,10 +124,23 @@ public static IIdentityServerBuilder AddDefaultEndpoints(this IIdentityServerBui builder.AddEndpoint(EndpointNames.Token, ProtocolRoutePaths.Token.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.UserInfo, ProtocolRoutePaths.UserInfo.EnsureLeadingSlash()); - builder.AddEndpointResultGenerator(); - builder.AddEndpointResultGenerator(); builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); + builder.AddEndpointResultGenerator(); return builder; } diff --git a/src/IdentityServer/Endpoints/Results/AuthorizeInteractionPageResult.cs b/src/IdentityServer/Endpoints/Results/AuthorizeInteractionPageResult.cs index f1bb8e0cf..8153e041d 100644 --- a/src/IdentityServer/Endpoints/Results/AuthorizeInteractionPageResult.cs +++ b/src/IdentityServer/Endpoints/Results/AuthorizeInteractionPageResult.cs @@ -52,10 +52,7 @@ public AuthorizeInteractionPageResult(ValidatedAuthorizeRequest request, string public string ReturnUrlParameterName { get; } } -/// -/// Result generator for AuthorizeInteractionPageResult -/// -public class AuthorizeInteractionPageResultGenerator : IEndpointResultGenerator +class AuthorizeInteractionPageResultGenerator : IEndpointResultGenerator { private readonly IServerUrls _urls; private readonly IAuthorizationParametersMessageStore _authorizationParametersMessageStore; diff --git a/src/IdentityServer/Endpoints/Results/AuthorizeResult.cs b/src/IdentityServer/Endpoints/Results/AuthorizeResult.cs index 2ce31a6d6..f0045757c 100644 --- a/src/IdentityServer/Endpoints/Results/AuthorizeResult.cs +++ b/src/IdentityServer/Endpoints/Results/AuthorizeResult.cs @@ -18,10 +18,21 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class AuthorizeResult : EndpointResult +/// +/// Models the result from the authorize endpoint +/// +public class AuthorizeResult : EndpointResult { + /// + /// The authorize response + /// public AuthorizeResponse Response { get; } + /// + /// Ctor + /// + /// + /// public AuthorizeResult(AuthorizeResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); diff --git a/src/IdentityServer/Endpoints/Results/BackchannelAuthenticationResult.cs b/src/IdentityServer/Endpoints/Results/BackchannelAuthenticationResult.cs index f44255b0a..467e74a1b 100644 --- a/src/IdentityServer/Endpoints/Results/BackchannelAuthenticationResult.cs +++ b/src/IdentityServer/Endpoints/Results/BackchannelAuthenticationResult.cs @@ -12,10 +12,21 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class BackchannelAuthenticationResult : EndpointResult +/// +/// Models the result of backchannel authentication +/// +public class BackchannelAuthenticationResult : EndpointResult { - public BackchannelAuthenticationResponse Response { get; set; } + /// + /// The response + /// + public BackchannelAuthenticationResponse Response { get; } + /// + /// Ctor + /// + /// + /// public BackchannelAuthenticationResult(BackchannelAuthenticationResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); diff --git a/src/IdentityServer/Endpoints/Results/BadRequestResult.cs b/src/IdentityServer/Endpoints/Results/BadRequestResult.cs index 1747ef02f..42f1f772c 100644 --- a/src/IdentityServer/Endpoints/Results/BadRequestResult.cs +++ b/src/IdentityServer/Endpoints/Results/BadRequestResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Duende Software. All rights reserved. +// Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. @@ -9,28 +9,45 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class BadRequestResult : IEndpointResult +/// +/// The result of a bad request +/// +public class BadRequestResult : EndpointResult { - public string Error { get; set; } - public string ErrorDescription { get; set; } - + /// + /// The error + /// + public string Error { get; } + /// + /// The error description + /// + public string ErrorDescription { get; } + + /// + /// Ctor + /// + /// + /// public BadRequestResult(string error = null, string errorDescription = null) { Error = error; ErrorDescription = errorDescription; } +} - public async Task ExecuteAsync(HttpContext context) +internal class BadRequestResultGenerator : IEndpointResultGenerator +{ + public async Task ExecuteAsync(BadRequestResult result, HttpContext context) { context.Response.StatusCode = 400; context.Response.SetNoCache(); - if (Error.IsPresent()) + if (result.Error.IsPresent()) { var dto = new ResultDto { - error = Error, - error_description = ErrorDescription + error = result.Error, + error_description = result.ErrorDescription }; await context.Response.WriteJsonAsync(dto); @@ -41,5 +58,5 @@ internal class ResultDto { public string error { get; set; } public string error_description { get; set; } - } + } } \ No newline at end of file diff --git a/src/IdentityServer/Endpoints/Results/CheckSessionResult.cs b/src/IdentityServer/Endpoints/Results/CheckSessionResult.cs index 37f73c6cd..be1c492b3 100644 --- a/src/IdentityServer/Endpoints/Results/CheckSessionResult.cs +++ b/src/IdentityServer/Endpoints/Results/CheckSessionResult.cs @@ -6,18 +6,21 @@ using Duende.IdentityServer.Configuration; using Duende.IdentityServer.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; using Duende.IdentityServer.Extensions; namespace Duende.IdentityServer.Endpoints.Results; -internal class CheckSessionResult : IEndpointResult +/// +/// The resukt of the check session endpoint +/// +public class CheckSessionResult : EndpointResult { - public CheckSessionResult() - { - } +} - internal CheckSessionResult(IdentityServerOptions options) + +internal class CheckSessionResultGenerator : IEndpointResultGenerator +{ + public CheckSessionResultGenerator(IdentityServerOptions options) { _options = options; } @@ -27,15 +30,8 @@ internal CheckSessionResult(IdentityServerOptions options) private static readonly object Lock = new object(); private static volatile string LastCheckSessionCookieName; - private void Init(HttpContext context) + public async Task ExecuteAsync(CheckSessionResult result, HttpContext context) { - _options = _options ?? context.RequestServices.GetRequiredService(); - } - - public async Task ExecuteAsync(HttpContext context) - { - Init(context); - AddCspHeaders(context); var html = GetHtml(_options.Authentication.CheckSessionCookieName); diff --git a/src/IdentityServer/Endpoints/Results/DeviceAuthorizationResult.cs b/src/IdentityServer/Endpoints/Results/DeviceAuthorizationResult.cs index 17cadc0f7..bad114a44 100644 --- a/src/IdentityServer/Endpoints/Results/DeviceAuthorizationResult.cs +++ b/src/IdentityServer/Endpoints/Results/DeviceAuthorizationResult.cs @@ -11,27 +11,41 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class DeviceAuthorizationResult : IEndpointResult +/// +/// The result of device authorization +/// +public class DeviceAuthorizationResult : EndpointResult { + /// + /// The response + /// public DeviceAuthorizationResponse Response { get; } + /// + /// Ctor + /// + /// + /// public DeviceAuthorizationResult(DeviceAuthorizationResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); } +} - public async Task ExecuteAsync(HttpContext context) +internal class DeviceAuthorizationResultGenerator : IEndpointResultGenerator +{ + public async Task ExecuteAsync(DeviceAuthorizationResult result, HttpContext context) { context.Response.SetNoCache(); var dto = new ResultDto { - device_code = Response.DeviceCode, - user_code = Response.UserCode, - verification_uri = Response.VerificationUri, - verification_uri_complete = Response.VerificationUriComplete, - expires_in = Response.DeviceCodeLifetime, - interval = Response.Interval + device_code = result.Response.DeviceCode, + user_code = result.Response.UserCode, + verification_uri = result.Response.VerificationUri, + verification_uri_complete = result.Response.VerificationUriComplete, + expires_in = result.Response.DeviceCodeLifetime, + interval = result.Response.Interval }; await context.Response.WriteJsonAsync(dto); diff --git a/src/IdentityServer/Endpoints/Results/DiscoveryDocumentResult.cs b/src/IdentityServer/Endpoints/Results/DiscoveryDocumentResult.cs index a85a00c4a..0d5b5d54f 100644 --- a/src/IdentityServer/Endpoints/Results/DiscoveryDocumentResult.cs +++ b/src/IdentityServer/Endpoints/Results/DiscoveryDocumentResult.cs @@ -39,17 +39,14 @@ public class DiscoveryDocumentResult : EndpointResult /// The entries. /// The maximum age. /// entries - public DiscoveryDocumentResult(Dictionary entries, int? maxAge) + public DiscoveryDocumentResult(Dictionary entries, int? maxAge = null) { Entries = entries ?? throw new ArgumentNullException(nameof(entries)); MaxAge = maxAge; } } -/// -/// The result generator for DiscoveryDocumentResult. -/// -public class DiscoveryDocumentResultGenerator : Hosting.IEndpointResultGenerator +class DiscoveryDocumentResultGenerator : IEndpointResultGenerator { /// public Task ExecuteAsync(DiscoveryDocumentResult result, HttpContext context) diff --git a/src/IdentityServer/Endpoints/Results/EndPointResult.cs b/src/IdentityServer/Endpoints/Results/EndPointResult.cs index b914591b0..0b5195ea4 100644 --- a/src/IdentityServer/Endpoints/Results/EndPointResult.cs +++ b/src/IdentityServer/Endpoints/Results/EndPointResult.cs @@ -11,7 +11,7 @@ namespace Duende.IdentityServer.Endpoints.Results; /// -/// Provides the base implementation of IEndpointResult. +/// Provides the base implementation of IEndpointResult that invokes the corresponding IEndpointResultGenerator. /// /// public abstract class EndpointResult : IEndpointResult diff --git a/src/IdentityServer/Endpoints/Results/EndSessionCallbackResult.cs b/src/IdentityServer/Endpoints/Results/EndSessionCallbackResult.cs index cc43fd960..d4c32ae0e 100644 --- a/src/IdentityServer/Endpoints/Results/EndSessionCallbackResult.cs +++ b/src/IdentityServer/Endpoints/Results/EndSessionCallbackResult.cs @@ -5,66 +5,69 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; using System.Net; using System; using Duende.IdentityServer.Configuration; -using Duende.IdentityServer.Hosting; using Duende.IdentityServer.Validation; using Duende.IdentityServer.Extensions; using System.Text.Encodings.Web; using System.Text; +using Duende.IdentityServer.Hosting; namespace Duende.IdentityServer.Endpoints.Results; -internal class EndSessionCallbackResult : IEndpointResult +/// +/// Models the result of end session callback +/// +public class EndSessionCallbackResult : EndpointResult { - private readonly EndSessionCallbackValidationResult _result; - + /// + /// The result + /// + public EndSessionCallbackValidationResult Result { get; } + + /// + /// Ctor + /// + /// + /// public EndSessionCallbackResult(EndSessionCallbackValidationResult result) { - _result = result ?? throw new ArgumentNullException(nameof(result)); + Result = result ?? throw new ArgumentNullException(nameof(result)); } +} - internal EndSessionCallbackResult( - EndSessionCallbackValidationResult result, - IdentityServerOptions options) - : this(result) +class EndSessionCallbackResultGenerator : IEndpointResultGenerator +{ + public EndSessionCallbackResultGenerator(IdentityServerOptions options) { _options = options; } private IdentityServerOptions _options; - private void Init(HttpContext context) + public async Task ExecuteAsync(EndSessionCallbackResult result, HttpContext context) { - _options = _options ?? context.RequestServices.GetRequiredService(); - } - - public async Task ExecuteAsync(HttpContext context) - { - Init(context); - - if (_result.IsError) + if (result.Result.IsError) { - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; } else { context.Response.SetNoCache(); - AddCspHeaders(context); + AddCspHeaders(result, context); - var html = GetHtml(); + var html = GetHtml(result); await context.Response.WriteHtmlAsync(html); } } - private void AddCspHeaders(HttpContext context) + private void AddCspHeaders(EndSessionCallbackResult result, HttpContext context) { if (_options.Authentication.RequireCspFrameSrcForSignout) { var sb = new StringBuilder(); - var origins = _result.FrontChannelLogoutUrls?.Select(x => x.GetOrigin()); + var origins = result.Result.FrontChannelLogoutUrls?.Select(x => x.GetOrigin()); if (origins != null) { foreach (var origin in origins.Distinct()) @@ -79,14 +82,14 @@ private void AddCspHeaders(HttpContext context) } } - private string GetHtml() + private string GetHtml(EndSessionCallbackResult result) { var sb = new StringBuilder(); sb.Append(""); - if (_result.FrontChannelLogoutUrls != null) + if (result.Result.FrontChannelLogoutUrls != null) { - foreach (var url in _result.FrontChannelLogoutUrls) + foreach (var url in result.Result.FrontChannelLogoutUrls) { sb.AppendFormat("", HtmlEncoder.Default.Encode(url)); sb.AppendLine(); diff --git a/src/IdentityServer/Endpoints/Results/EndSessionResult.cs b/src/IdentityServer/Endpoints/Results/EndSessionResult.cs index af01770fa..9458b762a 100644 --- a/src/IdentityServer/Endpoints/Results/EndSessionResult.cs +++ b/src/IdentityServer/Endpoints/Results/EndSessionResult.cs @@ -21,9 +21,12 @@ namespace Duende.IdentityServer.Endpoints.Results; /// Result for endsession /// /// -public class EndSessionResult : IEndpointResult +public class EndSessionResult : EndpointResult { - private readonly EndSessionValidationResult _result; + /// + /// The result + /// + public EndSessionValidationResult Result { get; } /// /// Initializes a new instance of the class. @@ -32,16 +35,18 @@ public class EndSessionResult : IEndpointResult /// result public EndSessionResult(EndSessionValidationResult result) { - _result = result ?? throw new ArgumentNullException(nameof(result)); + Result = result ?? throw new ArgumentNullException(nameof(result)); } +} + - internal EndSessionResult( - EndSessionValidationResult result, +class EndSessionResultGenerator : IEndpointResultGenerator +{ + public EndSessionResultGenerator( IdentityServerOptions options, ISystemClock clock, IServerUrls urls, IMessageStore logoutMessageStore) - : this(result) { _options = options; _clock = clock; @@ -54,24 +59,9 @@ internal EndSessionResult( private IServerUrls _urls; private IMessageStore _logoutMessageStore; - private void Init(HttpContext context) - { - _options = _options ?? context.RequestServices.GetRequiredService(); - _clock = _clock ?? context.RequestServices.GetRequiredService(); - _urls = _urls ?? context.RequestServices.GetRequiredService(); - _logoutMessageStore = _logoutMessageStore ?? context.RequestServices.GetRequiredService>(); - } - - /// - /// Executes the result. - /// - /// The HTTP context. - /// - public async Task ExecuteAsync(HttpContext context) + public async Task ExecuteAsync(EndSessionResult result, HttpContext context) { - Init(context); - - var validatedRequest = _result.IsError ? null : _result.ValidatedRequest; + var validatedRequest = result.Result.IsError ? null : result.Result.ValidatedRequest; string id = null; @@ -99,4 +89,4 @@ public async Task ExecuteAsync(HttpContext context) context.Response.Redirect(redirect); } -} \ No newline at end of file +} diff --git a/src/IdentityServer/Endpoints/Results/IntrospectionResult.cs b/src/IdentityServer/Endpoints/Results/IntrospectionResult.cs index 1d0ef9be0..bd2e74714 100644 --- a/src/IdentityServer/Endpoints/Results/IntrospectionResult.cs +++ b/src/IdentityServer/Endpoints/Results/IntrospectionResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Duende Software. All rights reserved. +// Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. @@ -15,7 +15,7 @@ namespace Duende.IdentityServer.Endpoints.Results; /// Result for introspection /// /// -public class IntrospectionResult : IEndpointResult +public class IntrospectionResult : EndpointResult { /// /// Gets the result. @@ -34,16 +34,15 @@ public IntrospectionResult(Dictionary entries) { Entries = entries ?? throw new ArgumentNullException(nameof(entries)); } +} - /// - /// Executes the result. - /// - /// The HTTP context. - /// - public Task ExecuteAsync(HttpContext context) + +class IntrospectionResultGenerator : IEndpointResultGenerator +{ + public Task ExecuteAsync(IntrospectionResult result, HttpContext context) { context.Response.SetNoCache(); - - return context.Response.WriteJsonAsync(Entries); + + return context.Response.WriteJsonAsync(result.Entries); } -} \ No newline at end of file +} diff --git a/src/IdentityServer/Endpoints/Results/JsonWebKeysResult.cs b/src/IdentityServer/Endpoints/Results/JsonWebKeysResult.cs index 298cab84c..094a57e00 100644 --- a/src/IdentityServer/Endpoints/Results/JsonWebKeysResult.cs +++ b/src/IdentityServer/Endpoints/Results/JsonWebKeysResult.cs @@ -16,7 +16,7 @@ namespace Duende.IdentityServer.Endpoints.Results; /// Result for the jwks document /// /// -public class JsonWebKeysResult : IEndpointResult +public class JsonWebKeysResult : EndpointResult { /// /// Gets the web keys. @@ -44,19 +44,17 @@ public JsonWebKeysResult(IEnumerable webKeys, int? maxAge) WebKeys = webKeys ?? throw new ArgumentNullException(nameof(webKeys)); MaxAge = maxAge; } +} - /// - /// Executes the result. - /// - /// The HTTP context. - /// - public Task ExecuteAsync(HttpContext context) +class JsonWebKeysResultGenerator : IEndpointResultGenerator +{ + public Task ExecuteAsync(JsonWebKeysResult result, HttpContext context) { - if (MaxAge.HasValue && MaxAge.Value >= 0) + if (result.MaxAge.HasValue && result.MaxAge.Value >= 0) { - context.Response.SetCache(MaxAge.Value, "Origin"); + context.Response.SetCache(result.MaxAge.Value, "Origin"); } - return context.Response.WriteJsonAsync(new { keys = WebKeys }, "application/json; charset=UTF-8"); + return context.Response.WriteJsonAsync(new { keys = result.WebKeys }, "application/json; charset=UTF-8"); } } \ No newline at end of file diff --git a/src/IdentityServer/Endpoints/Results/ProtectedResourceErrorResult.cs b/src/IdentityServer/Endpoints/Results/ProtectedResourceErrorResult.cs index 082a0ee8a..f2d9cc821 100644 --- a/src/IdentityServer/Endpoints/Results/ProtectedResourceErrorResult.cs +++ b/src/IdentityServer/Endpoints/Results/ProtectedResourceErrorResult.cs @@ -12,41 +12,61 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class ProtectedResourceErrorResult : IEndpointResult +/// +/// Models result of a protected resource +/// +public class ProtectedResourceErrorResult : EndpointResult { - public string Error; - public string ErrorDescription; + /// + /// The error + /// + public string Error { get; } + /// + /// The error description + /// + public string ErrorDescription { get; } + /// + /// Ctor + /// + /// + /// public ProtectedResourceErrorResult(string error, string errorDescription = null) { Error = error; ErrorDescription = errorDescription; } +} - public Task ExecuteAsync(HttpContext context) +internal class ProtectedResourceErrorResultGenerator : IEndpointResultGenerator +{ + public Task ExecuteAsync(ProtectedResourceErrorResult result, HttpContext context) { context.Response.StatusCode = 401; context.Response.SetNoCache(); - if (Constants.ProtectedResourceErrorStatusCodes.ContainsKey(Error)) + var error = result.Error; + var errorDescription = result.ErrorDescription; + + if (Constants.ProtectedResourceErrorStatusCodes.ContainsKey(error)) { - context.Response.StatusCode = Constants.ProtectedResourceErrorStatusCodes[Error]; + context.Response.StatusCode = Constants.ProtectedResourceErrorStatusCodes[error]; } - if (Error == OidcConstants.ProtectedResourceErrors.ExpiredToken) + if (error == OidcConstants.ProtectedResourceErrors.ExpiredToken) { - Error = OidcConstants.ProtectedResourceErrors.InvalidToken; - ErrorDescription = "The access token expired"; + error = OidcConstants.ProtectedResourceErrors.InvalidToken; + errorDescription = "The access token expired"; } - var errorString = string.Format($"error=\"{Error}\""); - if (ErrorDescription.IsMissing()) + var errorString = string.Format($"error=\"{error}\""); + if (errorDescription.IsMissing()) { context.Response.Headers.Append(HeaderNames.WWWAuthenticate, new StringValues(new[] { "Bearer realm=\"IdentityServer\"", errorString }).ToString()); } else { - var errorDescriptionString = string.Format($"error_description=\"{ErrorDescription}\""); + var errorDescriptionString = string.Format($"error_description=\"{errorDescription}\""); context.Response.Headers.Append(HeaderNames.WWWAuthenticate, new StringValues(new[] { "Bearer realm=\"IdentityServer\"", errorString, errorDescriptionString }).ToString()); } diff --git a/src/IdentityServer/Endpoints/Results/StatusCodeResult.cs b/src/IdentityServer/Endpoints/Results/StatusCodeResult.cs index 19718ee2e..69064b5af 100644 --- a/src/IdentityServer/Endpoints/Results/StatusCodeResult.cs +++ b/src/IdentityServer/Endpoints/Results/StatusCodeResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Duende Software. All rights reserved. +// Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. @@ -13,7 +13,7 @@ namespace Duende.IdentityServer.Endpoints.Results; /// Result for a raw HTTP status code /// /// -public class StatusCodeResult : IEndpointResult +public class StatusCodeResult : EndpointResult { /// /// Gets the status code. @@ -40,15 +40,13 @@ public StatusCodeResult(int statusCode) { StatusCode = statusCode; } +} - /// - /// Executes the result. - /// - /// The HTTP context. - /// - public Task ExecuteAsync(HttpContext context) +class StatusCodeResultGenerator : IEndpointResultGenerator +{ + public Task ExecuteAsync(StatusCodeResult result, HttpContext context) { - context.Response.StatusCode = StatusCode; + context.Response.StatusCode = result.StatusCode; return Task.CompletedTask; } diff --git a/src/IdentityServer/Endpoints/Results/TokenErrorResult.cs b/src/IdentityServer/Endpoints/Results/TokenErrorResult.cs index 1540da885..7f9243494 100644 --- a/src/IdentityServer/Endpoints/Results/TokenErrorResult.cs +++ b/src/IdentityServer/Endpoints/Results/TokenErrorResult.cs @@ -14,33 +14,47 @@ namespace Duende.IdentityServer.Endpoints.Results; -internal class TokenErrorResult : IEndpointResult +/// +/// Models a token error result +/// +public class TokenErrorResult : EndpointResult { + /// + /// The response + /// public TokenErrorResponse Response { get; } + /// + /// Ctor + /// + /// + /// public TokenErrorResult(TokenErrorResponse error) { if (error.Error.IsMissing()) throw new ArgumentNullException(nameof(error.Error), "Error must be set"); Response = error; } +} - public async Task ExecuteAsync(HttpContext context) +internal class TokenErrorResultGenerator : IEndpointResultGenerator +{ + public async Task ExecuteAsync(TokenErrorResult result, HttpContext context) { context.Response.StatusCode = 400; context.Response.SetNoCache(); - if (Response.DPoPNonce.IsPresent()) + if (result.Response.DPoPNonce.IsPresent()) { - context.Response.Headers[OidcConstants.HttpHeaders.DPoPNonce] = Response.DPoPNonce; + context.Response.Headers[OidcConstants.HttpHeaders.DPoPNonce] = result.Response.DPoPNonce; } var dto = new ResultDto { - error = Response.Error, - error_description = Response.ErrorDescription, - - custom = Response.Custom + error = result.Response.Error, + error_description = result.Response.ErrorDescription, + + custom = result.Response.Custom }; await context.Response.WriteJsonAsync(dto); @@ -53,5 +67,5 @@ internal class ResultDto [JsonExtensionData] public Dictionary custom { get; set; } - } + } } \ No newline at end of file diff --git a/src/IdentityServer/Endpoints/Results/TokenResult.cs b/src/IdentityServer/Endpoints/Results/TokenResult.cs index 85331e2de..eebd17102 100644 --- a/src/IdentityServer/Endpoints/Results/TokenResult.cs +++ b/src/IdentityServer/Endpoints/Results/TokenResult.cs @@ -9,39 +9,53 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using System.Threading.Tasks; -using Duende.IdentityServer.Hosting; using Duende.IdentityServer.ResponseHandling; +using Duende.IdentityServer.Hosting; namespace Duende.IdentityServer.Endpoints.Results; -internal class TokenResult : IEndpointResult +/// +/// Models a token result +/// +public class TokenResult : EndpointResult { - public TokenResponse Response { get; set; } + /// + /// The response + /// + public TokenResponse Response { get; } + /// + /// Ctor + /// + /// + /// public TokenResult(TokenResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); } +} - public async Task ExecuteAsync(HttpContext context) +internal class TokenResultGenerator : IEndpointResultGenerator +{ + public async Task ExecuteAsync(TokenResult result, HttpContext context) { context.Response.SetNoCache(); - if (Response.DPoPNonce.IsPresent()) + if (result.Response.DPoPNonce.IsPresent()) { - context.Response.Headers[OidcConstants.HttpHeaders.DPoPNonce] = Response.DPoPNonce; + context.Response.Headers[OidcConstants.HttpHeaders.DPoPNonce] = result.Response.DPoPNonce; } var dto = new ResultDto { - id_token = Response.IdentityToken, - access_token = Response.AccessToken, - refresh_token = Response.RefreshToken, - expires_in = Response.AccessTokenLifetime, - token_type = Response.AccessTokenType, - scope = Response.Scope, - - Custom = Response.Custom + id_token = result.Response.IdentityToken, + access_token = result.Response.AccessToken, + refresh_token = result.Response.RefreshToken, + expires_in = result.Response.AccessTokenLifetime, + token_type = result.Response.AccessTokenType, + scope = result.Response.Scope, + + Custom = result.Response.Custom }; await context.Response.WriteJsonAsync(dto); diff --git a/src/IdentityServer/Endpoints/Results/TokenRevocationErrorResult.cs b/src/IdentityServer/Endpoints/Results/TokenRevocationErrorResult.cs index 564445bbc..b0a2549cd 100644 --- a/src/IdentityServer/Endpoints/Results/TokenRevocationErrorResult.cs +++ b/src/IdentityServer/Endpoints/Results/TokenRevocationErrorResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Duende Software. All rights reserved. +// Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. @@ -7,6 +7,7 @@ using System.Net; using System.Threading.Tasks; using Duende.IdentityServer.Hosting; +using System; namespace Duende.IdentityServer.Endpoints.Results; @@ -14,7 +15,7 @@ namespace Duende.IdentityServer.Endpoints.Results; /// Result for revocation error /// /// -public class TokenRevocationErrorResult : IEndpointResult +public class TokenRevocationErrorResult : EndpointResult { /// /// Gets or sets the error. @@ -22,7 +23,7 @@ public class TokenRevocationErrorResult : IEndpointResult /// /// The error. /// - public string Error { get; set; } + public string Error { get; } /// /// Initializes a new instance of the class. @@ -30,17 +31,15 @@ public class TokenRevocationErrorResult : IEndpointResult /// The error. public TokenRevocationErrorResult(string error) { - Error = error; + Error = error ?? throw new ArgumentNullException(nameof(error)); } +} - /// - /// Executes the result. - /// - /// The HTTP context. - /// - public Task ExecuteAsync(HttpContext context) +class TokenRevocationErrorResultGenerator : IEndpointResultGenerator +{ + public Task ExecuteAsync(TokenRevocationErrorResult result, HttpContext context) { - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - return context.Response.WriteJsonAsync(new { error = Error }); + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + return context.Response.WriteJsonAsync(new { error = result.Error }); } } \ No newline at end of file diff --git a/src/IdentityServer/Endpoints/Results/UserInfoResult.cs b/src/IdentityServer/Endpoints/Results/UserInfoResult.cs index 3820372cb..a0b7a5b2b 100644 --- a/src/IdentityServer/Endpoints/Results/UserInfoResult.cs +++ b/src/IdentityServer/Endpoints/Results/UserInfoResult.cs @@ -1,4 +1,4 @@ -// Copyright (c) Duende Software. All rights reserved. +// Copyright (c) Duende Software. All rights reserved. // See LICENSE in the project root for license information. @@ -7,21 +7,35 @@ using Duende.IdentityServer.Hosting; using Duende.IdentityServer.Extensions; using Microsoft.AspNetCore.Http; +using System; namespace Duende.IdentityServer.Endpoints.Results; -internal class UserInfoResult : IEndpointResult +/// +/// The result of userinfo +/// +public class UserInfoResult : EndpointResult { - public Dictionary Claims; + /// + /// The claims + /// + public Dictionary Claims { get; } + /// + /// Ctor + /// + /// public UserInfoResult(Dictionary claims) { - Claims = claims; + Claims = claims ?? throw new ArgumentNullException(nameof(claims)); } +} - public async Task ExecuteAsync(HttpContext context) +internal class UserInfoResultGenerator : IEndpointResultGenerator +{ + public async Task ExecuteAsync(UserInfoResult result, HttpContext context) { context.Response.SetNoCache(); - await context.Response.WriteJsonAsync(Claims); + await context.Response.WriteJsonAsync(result.Claims); } } \ No newline at end of file diff --git a/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackResultTests.cs b/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackResultTests.cs index 673b34e63..44f91da22 100644 --- a/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackResultTests.cs +++ b/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackResultTests.cs @@ -19,7 +19,7 @@ public class EndSessionCallbackResultTests private readonly EndSessionCallbackValidationResult _validationResult; private readonly IdentityServerOptions _options; - private readonly EndSessionCallbackResult _subject; + private readonly EndSessionCallbackResultGenerator _subject; public EndSessionCallbackResultTests() { @@ -28,7 +28,7 @@ public EndSessionCallbackResultTests() IsError = false, }; _options = new IdentityServerOptions(); - _subject = new EndSessionCallbackResult(_validationResult, _options); + _subject = new EndSessionCallbackResultGenerator(_options); } [Fact] @@ -39,7 +39,7 @@ public async Task default_options_should_emit_frame_src_csp_headers() var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; - await _subject.ExecuteAsync(ctx); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_validationResult), ctx); ctx.Response.Headers["Content-Security-Policy"].First().Should().Contain("frame-src http://foo"); } @@ -53,7 +53,7 @@ public async Task relax_csp_options_should_prevent_frame_src_csp_headers() var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; - await _subject.ExecuteAsync(ctx); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_validationResult), ctx); ctx.Response.Headers["Content-Security-Policy"].FirstOrDefault().Should().BeNull(); } diff --git a/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs b/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs index bfb0d84a8..36d22a0b2 100644 --- a/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs +++ b/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs @@ -17,7 +17,7 @@ namespace UnitTests.Endpoints.Results; public class CheckSessionResultTests { - private CheckSessionResult _subject; + private CheckSessionResultGenerator _subject; private IdentityServerOptions _options = new IdentityServerOptions(); @@ -31,13 +31,13 @@ public CheckSessionResultTests() _options.Authentication.CheckSessionCookieName = "foobar"; - _subject = new CheckSessionResult(_options); + _subject = new CheckSessionResultGenerator(_options); } [Fact] public async Task should_pass_results_in_body() { - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new CheckSessionResult(), _context); _context.Response.StatusCode.Should().Be(200); _context.Response.ContentType.Should().StartWith("text/html"); @@ -58,7 +58,7 @@ public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1() { _options.Csp.Level = CspLevel.One; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new CheckSessionResult(), _context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain($"script-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.CheckSessionScript}'"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain($"script-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.CheckSessionScript}'"); @@ -69,7 +69,7 @@ public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_dis { _options.Csp.AddDeprecatedHeader = false; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new CheckSessionResult(), _context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain($"script-src '{IdentityServerConstants.ContentSecurityPolicyHashes.CheckSessionScript}'"); _context.Response.Headers["X-Content-Security-Policy"].Should().BeEmpty(); @@ -82,7 +82,7 @@ public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_dis public async Task can_change_cached_cookiename(string cookieName) { _options.Authentication.CheckSessionCookieName = cookieName; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new CheckSessionResult(), _context); _context.Response.Body.Seek(0, SeekOrigin.Begin); using (var rdr = new StreamReader(_context.Response.Body)) { diff --git a/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs b/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs index e949f52ff..5d9d22508 100644 --- a/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs +++ b/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs @@ -18,7 +18,7 @@ namespace UnitTests.Endpoints.Results; public class EndSessionCallbackResultTests { - private EndSessionCallbackResult _subject; + private EndSessionCallbackResultGenerator _subject; private EndSessionCallbackValidationResult _result = new EndSessionCallbackValidationResult(); private IdentityServerOptions _options = TestIdentityServerOptions.Create(); @@ -31,7 +31,7 @@ public EndSessionCallbackResultTests() _context.Request.Host = new HostString("server"); _context.Response.Body = new MemoryStream(); - _subject = new EndSessionCallbackResult(_result, _options); + _subject = new EndSessionCallbackResultGenerator(_options); } [Fact] @@ -39,7 +39,7 @@ public async Task error_should_return_400() { _result.IsError = true; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_result), _context); _context.Response.StatusCode.Should().Be(400); } @@ -50,7 +50,7 @@ public async Task success_should_render_html_and_iframes() _result.IsError = false; _result.FrontChannelLogoutUrls = new string[] { "http://foo.com", "http://bar.com" }; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_result), _context); _context.Response.ContentType.Should().StartWith("text/html"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-store"); @@ -78,7 +78,7 @@ public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1() _options.Csp.Level = CspLevel.One; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_result), _context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); @@ -91,7 +91,7 @@ public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_dis _options.Csp.AddDeprecatedHeader = false; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionCallbackResult(_result), _context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='"); _context.Response.Headers["X-Content-Security-Policy"].Should().BeEmpty(); diff --git a/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionResultTests.cs b/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionResultTests.cs index 81023ddaf..9d1cef490 100644 --- a/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionResultTests.cs +++ b/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionResultTests.cs @@ -20,7 +20,7 @@ namespace UnitTests.Endpoints.Results; public class EndSessionResultTests { - private EndSessionResult _subject; + private EndSessionResultGenerator _subject; private EndSessionValidationResult _result = new EndSessionValidationResult(); private IdentityServerOptions _options = new IdentityServerOptions(); @@ -39,7 +39,7 @@ public EndSessionResultTests() _options.UserInteraction.LogoutUrl = "~/logout"; _options.UserInteraction.LogoutIdParameter = "logoutId"; - _subject = new EndSessionResult(_result, _options, new StubClock(), _urls, _mockLogoutMessageStore); + _subject = new EndSessionResultGenerator(_options, new StubClock(), _urls, _mockLogoutMessageStore); } [Fact] @@ -55,7 +55,7 @@ public async Task validated_signout_should_pass_logout_message() PostLogOutUri = "http://client/post-logout-callback" }; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionResult(_result), _context); _mockLogoutMessageStore.Messages.Count.Should().Be(1); var location = _context.Response.Headers["Location"].Single(); @@ -70,7 +70,7 @@ public async Task unvalidated_signout_should_not_pass_logout_message() { _result.IsError = false; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionResult(_result), _context); _mockLogoutMessageStore.Messages.Count.Should().Be(0); var location = _context.Response.Headers["Location"].Single(); @@ -93,7 +93,7 @@ public async Task error_result_should_not_pass_logout_message() PostLogOutUri = "http://client/post-logout-callback" }; - await _subject.ExecuteAsync(_context); + await _subject.ExecuteAsync(new EndSessionResult(_result), _context); _mockLogoutMessageStore.Messages.Count.Should().Be(0); var location = _context.Response.Headers["Location"].Single();