< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Security.GoogleIdTokenVerifier
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Presentation/ArturRios.Heimdall.WebApi/Security/GoogleIdTokenVerifier.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 22
Coverable lines: 22
Total lines: 62
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 8
Branch coverage: 0%
Method coverage
0%
Covered methods: 0
Fully covered methods: 0
Total methods: 2
Method coverage: 0%
Full method coverage: 0%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
VerifyAsync()0%7280%

File(s)

/home/ci/agent/workspace/heimdall-api-docs_main/app/src/Presentation/ArturRios.Heimdall.WebApi/Security/GoogleIdTokenVerifier.cs

#LineLine coverage
 1using ArturRios.Heimdall.Command.Services;
 2using Google.Apis.Auth;
 3
 4namespace ArturRios.Heimdall.WebApi.Security;
 5
 6/// <summary>
 7///     Verifies UC-25's Google ID tokens against Google (FR-GO-11, NFR-13).
 8///     <see cref="GoogleJsonWebSignature.ValidateAsync(string, GoogleJsonWebSignature.ValidationSettings)" />
 9///     checks the signature against Google's
 10///     published keys, the issuer against <c>accounts.google.com</c>, and the expiry; the configured
 11///     client IDs constrain the audience, so a token minted for someone else's application is
 12///     refused even though Google signed it.
 13/// </summary>
 14/// <remarks>
 15///     Every failure becomes <c>null</c> rather than an exception, because AF-25a treats them alike
 16///     and the handler has no decision to make between "expired" and "forged". The reason is logged
 17///     at debug level so an operator can still tell them apart; it is deliberately not returned to
 18///     the caller.
 19/// </remarks>
 020public class GoogleIdTokenVerifier(
 021    GoogleSignInOptions options,
 022    ILogger<GoogleIdTokenVerifier> logger) : IGoogleIdTokenVerifier
 23{
 24    public async Task<GoogleIdTokenPayload?> VerifyAsync(string idToken)
 25    {
 026        if (string.IsNullOrWhiteSpace(idToken))
 27        {
 028            return null;
 29        }
 30
 31        try
 32        {
 33            // The settings are built by GoogleIdTokenValidationSettings so the audience constraint
 34            // is something a test can see — see its remarks for why that one is worth isolating.
 035            var payload = await GoogleJsonWebSignature.ValidateAsync(
 036                idToken, GoogleIdTokenValidationSettings.For(options));
 37
 38            // Reached only once ValidateAsync has returned, i.e. once signature, issuer, audience
 39            // and expiry are all verified: the claim is never read off an untrusted token. Why the
 40            // claim is re-read off the token at all, rather than taken from the typed payload, is
 41            // GoogleIdTokenClaims.EmailVerified's business.
 042            return new GoogleIdTokenPayload(
 043                payload.Subject,
 044                payload.Email,
 045                GoogleIdTokenClaims.EmailVerified(idToken, logger),
 046                payload.Name,
 047                payload.Picture,
 048                string.IsNullOrEmpty(payload.Nonce) ? null : payload.Nonce,
 049                string.IsNullOrEmpty(payload.JwtId) ? null : payload.JwtId,
 050                payload.ExpirationTimeSeconds is { } expiresAt
 051                    ? DateTimeOffset.FromUnixTimeSeconds(expiresAt).UtcDateTime
 052                    : null);
 53        }
 054        catch (InvalidJwtException exception)
 55        {
 056            logger.LogDebug(exception, "Google ID token rejected");
 57
 058            return null;
 59        }
 060    }
 61
 62}