< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Security.TokenOrigin
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Presentation/ArturRios.Heimdall.WebApi/Security/TokenOrigin.cs
Line coverage
100%
Covered lines: 14
Uncovered lines: 0
Coverable lines: 14
Total lines: 59
Line coverage: 100%
Branch coverage
90%
Covered branches: 9
Total branches: 10
Branch coverage: 90%
Method coverage
100%
Covered methods: 1
Fully covered methods: 1
Total methods: 1
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
IsIssuedFor(...)90%1010100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Presentation/ArturRios.Heimdall.WebApi/Security/TokenOrigin.cs

#LineLine coverage
 1using Microsoft.IdentityModel.JsonWebTokens;
 2
 3namespace ArturRios.Heimdall.WebApi.Security;
 4
 5/// <summary>
 6///     Whether a token names Heimdall as its issuer and one of the audiences Heimdall issues it
 7///     under — the check every reader of a Heimdall token makes beside the signature and lifetime.
 8/// </summary>
 9/// <remarks>
 10///     <para>
 11///         <b>Why it exists.</b> The signing key is shared with other services today (Threat Model
 12///         TB-5), and <c>ArturRios.Jwt</c> checks only the signature and lifetime. Without this, a
 13///         token signed with that key for another service's audience, or under another issuer, would
 14///         be read here as one of Heimdall's own. Each reader names the audiences it takes: the
 15///         bearer-token validator the three person audiences (<see cref="PersonTokenAudiences" />),
 16///         the challenge reader the challenge audience alone, and the application-token reader the
 17///         application audience alone.
 18///     </para>
 19///     <para>
 20///         <b>Exact.</b> The issuer must equal the configured one, ordinally; a token without an
 21///         <c>iss</c> passes only when none is configured, which is how Heimdall then issues. A token
 22///         must carry exactly one audience, because Heimdall never issues one with several; no
 23///         <c>aud</c> at all counts as the empty audience, again how Heimdall issues when none is
 24///         configured. Run it only on a token whose signature has been checked: before that, the
 25///         claims are whatever the sender wrote.
 26///     </para>
 27/// </remarks>
 28public static class TokenOrigin
 29{
 30    /// <summary>
 31    ///     Whether <paramref name="token" /> was issued by <paramref name="issuer" /> for exactly one
 32    ///     of <paramref name="audiences" />. An unreadable token is not.
 33    /// </summary>
 34    public static bool IsIssuedFor(string token, string issuer, params IReadOnlyCollection<string> audiences)
 35    {
 36        try
 37        {
 106738            var jwt = new JsonWebToken(token);
 106439            var tokenAudiences = jwt.Audiences.ToList();
 40
 106441            var audience = tokenAudiences.Count switch
 106442            {
 143                0 => string.Empty,
 106044                1 => tokenAudiences[0],
 345                _ => null
 106446            };
 47
 106448            return audience is not null &&
 106449                   string.Equals(jwt.Issuer ?? string.Empty, issuer, StringComparison.Ordinal) &&
 106450                   audiences.Contains(audience, StringComparer.Ordinal);
 51        }
 352        catch (Exception)
 53        {
 54            // Total, like the claims reader: a token whose header, issuer or audience cannot be read
 55            // is refused as unauthenticated, never failed with a 500.
 356            return false;
 57        }
 106758    }
 59}