< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.WebApi.Security.TokenOrigin
Assembly: ArturRios.Heimdall.WebApi
File(s): /home/ci/agent/workspace/heimdall-api-docs_main/app/src/Presentation/ArturRios.Heimdall.WebApi/Security/TokenOrigin.cs
Line coverage
100%
Covered lines: 14
Uncovered lines: 0
Coverable lines: 14
Total lines: 58
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/ci/agent/workspace/heimdall-api-docs_main/app/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> <c>ArturRios.Jwt</c> checks only the signature and lifetime, and a
 12///         signature does not say whose token it is. Heimdall's ES256 key signs every kind of token it
 13///         issues, so a valid signature alone would let one kind pass for another (Threat Model TB-5).
 14///         Each reader names the audiences it takes: the bearer-token validator the three person
 15///         audiences (<see cref="PersonTokenAudiences" />), the challenge reader the challenge audience
 16///         alone, and the application-token reader the application audience alone.
 17///     </para>
 18///     <para>
 19///         <b>Exact.</b> The issuer must equal the configured one, ordinally; a token without an
 20///         <c>iss</c> passes only when none is configured, which is how Heimdall then issues. A token
 21///         must carry exactly one audience, because Heimdall never issues one with several; no
 22///         <c>aud</c> at all counts as the empty audience, again how Heimdall issues when none is
 23///         configured. Run it only on a token whose signature has been checked: before that, the
 24///         claims are whatever the sender wrote.
 25///     </para>
 26/// </remarks>
 27public static class TokenOrigin
 28{
 29    /// <summary>
 30    ///     Whether <paramref name="token" /> was issued by <paramref name="issuer" /> for exactly one
 31    ///     of <paramref name="audiences" />. An unreadable token is not.
 32    /// </summary>
 33    public static bool IsIssuedFor(string token, string issuer, params IReadOnlyCollection<string> audiences)
 34    {
 35        try
 36        {
 110237            var jwt = new JsonWebToken(token);
 109938            var tokenAudiences = jwt.Audiences.ToList();
 39
 109940            var audience = tokenAudiences.Count switch
 109941            {
 142                0 => string.Empty,
 109543                1 => tokenAudiences[0],
 344                _ => null
 109945            };
 46
 109947            return audience is not null &&
 109948                   string.Equals(jwt.Issuer ?? string.Empty, issuer, StringComparison.Ordinal) &&
 109949                   audiences.Contains(audience, StringComparer.Ordinal);
 50        }
 351        catch (Exception)
 52        {
 53            // Total, like the claims reader: a token whose header, issuer or audience cannot be read
 54            // is refused as unauthenticated, never failed with a 500.
 355            return false;
 56        }
 110257    }
 58}