< Summary

Information
Class: Elsa.ExternalAuthentication.Endpoints.Broker.ContinueLogout
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Broker/Logout.cs
Line coverage
84%
Covered lines: 11
Uncovered lines: 2
Coverable lines: 13
Total lines: 83
Line coverage: 84.6%
Branch coverage
75%
Covered branches: 3
Total branches: 4
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Configure()100%11100%
HandleAsync()75%4477.77%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Endpoints/Broker/Logout.cs

#LineLine coverage
 1using Elsa.Abstractions;
 2using Elsa.ExternalAuthentication.Models;
 3using Elsa.ExternalAuthentication.Services;
 4using Elsa.Identity.Constants;
 5
 6namespace Elsa.ExternalAuthentication.Endpoints.Broker;
 7
 8internal sealed class Logout(IExternalAuthenticationBroker broker) : ElsaEndpoint<LogoutRequest>
 9{
 10    public override void Configure()
 11    {
 12        // Deliberately authenticated without a permission: the session id is read from the caller's
 13        // principal below, so an identity is required, but logging out is never permission-gated.
 14        Post("/external-authentication/logout");
 15
 16        // Authenticated without a permission: the session id is read from the caller's principal below,
 17        // so an identity is required, but logging out is never permission-gated.
 18        RequireAuthenticatedOnly();
 19    }
 20
 21    public override async Task HandleAsync(LogoutRequest request, CancellationToken cancellationToken)
 22    {
 23        var sessionId = HttpContext.User.FindFirst(CustomClaimTypes.ExternalAuthenticationSessionId)?.Value;
 24        if (string.IsNullOrWhiteSpace(sessionId) || !Uri.TryCreate(request.PostLogoutRedirectUri, UriKind.Absolute, out 
 25        {
 26            await BrokerEndpointSupport.SendErrorAsync(Send, BrokerErrorFactory.Create(BrokerErrorCategory.InvalidReques
 27            return;
 28        }
 29
 30        var result = await broker.LogoutAsync(new(request.ClientId ?? string.Empty, redirectUri, request.Mode ?? "local"
 31        if (result.Error is { } error)
 32        {
 33            await BrokerEndpointSupport.SendErrorAsync(Send, error, cancellationToken);
 34            return;
 35        }
 36
 37        await Send.OkAsync(new LogoutResponse(result.Completed, result.NavigationUri?.ToString(), result.RedirectUri?.To
 38    }
 39}
 40
 41internal sealed class LogoutRequest
 42{
 43    public string? ClientId { get; set; }
 44    public string? PostLogoutRedirectUri { get; set; }
 45    public string? Mode { get; set; }
 46}
 47
 48internal sealed record LogoutResponse(bool Completed, string? NavigationUrl, string? RedirectUri);
 49
 1150internal sealed class ContinueLogout(IExternalAuthenticationBroker broker) : ElsaEndpointWithoutRequest
 51{
 52    public override void Configure()
 53    {
 954        Get("/external-authentication/logout/continue/{handle}");
 55
 56        // Anonymous, like every other broker endpoint the browser is navigated to. The single-use
 57        // route handle carries the authority; the caller's Elsa session has already been revoked by
 58        // the time this runs, and a top-level browser navigation sends no Authorization header, so
 59        // this endpoint can never present authenticated credentials.
 960        AllowAnonymous();
 961    }
 62
 63    public override async Task HandleAsync(CancellationToken cancellationToken)
 64    {
 265        var result = await broker.ContinueLogoutAsync(Route<string>("handle")!, cancellationToken);
 266        if (result.Error is { } error)
 67        {
 168            await BrokerEndpointSupport.SendErrorAsync(Send, error, cancellationToken);
 169            return;
 70        }
 71
 172        if (result.NavigationUri is null)
 73        {
 074            await BrokerEndpointSupport.SendErrorAsync(Send, BrokerErrorFactory.Create(BrokerErrorCategory.InvalidReques
 075            return;
 76        }
 77
 78        // Responses must go through the Send API: writing to HttpContext.Response without starting it
 79        // lets the FastEndpoints auto-response overwrite the status with 204, which silently discarded
 80        // both the redirect and the error this endpoint used to produce.
 181        await Send.RedirectAsync(result.NavigationUri.ToString(), false, true);
 282    }
 83}