< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Handlers.GoogleSignOutCommandHandler
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Handlers/GoogleSignOutCommandHandler.cs
Line coverage
100%
Covered lines: 10
Uncovered lines: 0
Coverable lines: 10
Total lines: 66
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 2
Fully covered methods: 2
Total methods: 2
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
HandleAsync()100%22100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Handlers/GoogleSignOutCommandHandler.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Interfaces;
 2using ArturRios.Heimdall.Command.Input;
 3using ArturRios.Heimdall.Command.Output;
 4using ArturRios.Heimdall.Domain.Entities;
 5using ArturRios.Heimdall.Shared.Messages;
 6using ArturRios.Mediator.Command.Interfaces;
 7using ArturRios.Output;
 8using Microsoft.EntityFrameworkCore;
 9
 10namespace ArturRios.Heimdall.Command.Handlers;
 11
 12/// <summary>
 13///     Handles <see cref="GoogleSignOutCommand" /> (UC-26, FR-GO-18): confirms the caller's token
 14///     still names a Google User in good standing and acknowledges the sign-out; every other caller
 15///     is AF-26a. Nothing is written — see the remarks.
 16/// </summary>
 17/// <remarks>
 18///     <para>
 19///         UC-26 step 2 offers two ways to end a session — invalidate the token, or have the client
 20///         discard it — and defers to "the configured token strategy". This project's is the stateless
 21///         one UC-11 established: signed JWTs with an expiry (NFR-03), no refresh tokens and no
 22///         revocation list. So the sign-out has nothing to revoke, and the endpoint's job is to
 23///         confirm the caller is who UC-26 says may call it and answer the success that tells the
 24///         client to drop the token. Adding a denylist here would be a token strategy no document
 25///         chooses, and it would apply to every token the API issues, not just Google's.
 26///     </para>
 27///     <para>
 28///         That leaves the lookup as the whole of the endpoint's substance, and it is not ceremony:
 29///         authentication runs in <c>ClaimsOnly</c> mode — no database read per request — so a valid
 30///         bearer token outlives the Google User it names once UC-28 logically deletes or UC-29
 31///         removes them, exactly as UC-15's does for a person. A caller in that position is refused,
 32///         because UC-26's precondition is a live session issued by UC-25 and UC-25 itself would no
 33///         longer authenticate them (AF-25d).
 34///     </para>
 35///     <para>
 36///         Every refusal answers with <see cref="AuthMessages.GoogleAuthenticationFailed" />, the same
 37///         message UC-25 uses, so the endpoint cannot be used to tell a deleted Google User apart from
 38///         one that never existed.
 39///     </para>
 40/// </remarks>
 1041public class GoogleSignOutCommandHandler(IAsyncReadOnlyRepository<GoogleUser, long> googleUserReader)
 42    : ICommandHandlerAsync<GoogleSignOutCommand, GoogleSignOutCommandOutput>
 43{
 44    public async Task<DataOutput<GoogleSignOutCommandOutput?>> HandleAsync(GoogleSignOutCommand command, CancellationTok
 45    {
 1046        var output = DataOutput<GoogleSignOutCommandOutput?>.New;
 47
 48        // UC-26 step 1 (AF-26a). The filter is part of the check rather than a separate branch: a
 49        // Google User that is missing and one that is logically deleted are refused alike, so the
 50        // answer distinguishes neither. The command carries no id of its own, so a caller can only
 51        // ever name themselves.
 1052        var signedIn = await googleUserReader.Query()
 1053            .AnyAsync(x => x.PublicId == command.ActingPersonId && !x.IsDeleted);
 54
 1055        if (!signedIn)
 56        {
 457            return output.WithError(AuthMessages.GoogleAuthenticationFailed);
 58        }
 59
 60        // UC-26 steps 2 and 3: nothing to invalidate under the stateless strategy, so the success is
 61        // the instruction to discard the token.
 662        return output
 663            .WithData(new GoogleSignOutCommandOutput())
 664            .WithMessage(AuthMessages.GoogleSignOutSuccessful);
 1065    }
 66}