| | | 1 | | using ArturRios.Heimdall.WebApi.Security; |
| | | 2 | | using ArturRios.Jwt; |
| | | 3 | | |
| | | 4 | | namespace ArturRios.Heimdall.WebApi; |
| | | 5 | | |
| | | 6 | | internal static class JwtConfigurationBuilder |
| | | 7 | | { |
| | | 8 | | private const string TokenAudienceEnvironmentVariable = "HEIMDALL_AUTH_TOKEN_AUDIENCE"; |
| | | 9 | | private const string TokenExpirationEnvironmentVariable = "HEIMDALL_AUTH_TOKEN_EXPIRATION_IN_SECONDS"; |
| | | 10 | | private const string TokenIssuerEnvironmentVariable = "HEIMDALL_AUTH_TOKEN_ISSUER"; |
| | | 11 | | private const double DefaultTokenExpirationInSeconds = 3600; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// Reads the token settings from the environment — lifetime, issuer and audience — and hands |
| | | 15 | | /// them to <paramref name="keys" />, which makes the configuration sign ES256 with the current |
| | | 16 | | /// key and accept the current and any previous one (<see cref="TokenSigningKeys.ApplyTo" />). |
| | | 17 | | /// It holds EC keys alone and no secret, so an HS256 token is refused whatever it is signed with. |
| | | 18 | | /// </summary> |
| | | 19 | | /// <remarks> |
| | | 20 | | /// <para> |
| | | 21 | | /// <c>HEIMDALL_AUTH_SIGNING_KEY_PREVIOUS</c> is how the signing key is replaced without |
| | | 22 | | /// signing everybody out (Threat Model TH-22). Its public half is published beside the |
| | | 23 | | /// current key's and verifies the tokens it signed, while new tokens are signed with the |
| | | 24 | | /// current key alone. Rotating is then: set the previous variable to the key in use, set the |
| | | 25 | | /// current variable to a new one, restart; and once the longest token lifetime has passed — |
| | | 26 | | /// a person token's or an application token's, whichever is longer — clear the previous |
| | | 27 | | /// variable and restart again. |
| | | 28 | | /// </para> |
| | | 29 | | /// <para> |
| | | 30 | | /// Every token carries a <c>kid</c>: the RFC 7638 thumbprint of the key that signed it |
| | | 31 | | /// (<see cref="JwtKey.Id" />). A thumbprint is derived from the public half, which the key |
| | | 32 | | /// set publishes anyway, so it discloses nothing, and it stays attached to its key wherever |
| | | 33 | | /// the key goes: a token signed before a rotation names the key that becomes the previous |
| | | 34 | | /// one, and is checked against that key alone. |
| | | 35 | | /// </para> |
| | | 36 | | /// </remarks> |
| | | 37 | | internal static JwtConfiguration BuildJwtConfiguration(TokenSigningKeys keys) |
| | | 38 | | { |
| | 966 | 39 | | var lifetime = TokenLifetimeSetting.Parse( |
| | 966 | 40 | | Environment.GetEnvironmentVariable(TokenExpirationEnvironmentVariable), |
| | 966 | 41 | | DefaultTokenExpirationInSeconds); |
| | | 42 | | |
| | 966 | 43 | | ConfigurationWarnings.WarnOnFallback(TokenExpirationEnvironmentVariable, lifetime.RejectedValue, DefaultTokenExp |
| | | 44 | | |
| | 966 | 45 | | return keys.ApplyTo(new JwtConfiguration( |
| | 966 | 46 | | lifetime.Seconds, |
| | 966 | 47 | | Environment.GetEnvironmentVariable(TokenIssuerEnvironmentVariable) ?? string.Empty, |
| | 966 | 48 | | Environment.GetEnvironmentVariable(TokenAudienceEnvironmentVariable) ?? string.Empty, |
| | 966 | 49 | | string.Empty, |
| | 966 | 50 | | [])); |
| | | 51 | | } |
| | | 52 | | } |