< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Security.GoogleIdTokenVerifier
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Presentation/ArturRios.Heimdall.WebApi/Security/GoogleIdTokenVerifier.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 18
Coverable lines: 18
Total lines: 58
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
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%2040%

File(s)

/home/runner/work/heimdall-api/heimdall-api/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);
 49        }
 050        catch (InvalidJwtException exception)
 51        {
 052            logger.LogDebug(exception, "Google ID token rejected");
 53
 054            return null;
 55        }
 056    }
 57
 58}