diff --git a/src/Tgstation.Server.Host/Security/ITokenFactory.cs b/src/Tgstation.Server.Host/Security/ITokenFactory.cs index bf9a70fab1..0dbb4a959d 100644 --- a/src/Tgstation.Server.Host/Security/ITokenFactory.cs +++ b/src/Tgstation.Server.Host/Security/ITokenFactory.cs @@ -1,4 +1,6 @@ -using Microsoft.IdentityModel.Tokens; +using System; + +using Microsoft.IdentityModel.Tokens; using Tgstation.Server.Api.Models.Response; @@ -9,6 +11,11 @@ namespace Tgstation.Server.Host.Security /// public interface ITokenFactory { + /// + /// Gets or sets the 's signing key. + /// + ReadOnlySpan SigningKey { get; set; } + /// /// The for the . /// diff --git a/src/Tgstation.Server.Host/Security/TokenFactory.cs b/src/Tgstation.Server.Host/Security/TokenFactory.cs index fcc5990c2f..b579c20faa 100644 --- a/src/Tgstation.Server.Host/Security/TokenFactory.cs +++ b/src/Tgstation.Server.Host/Security/TokenFactory.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IdentityModel.Tokens.Jwt; using System.Linq; @@ -19,7 +20,23 @@ namespace Tgstation.Server.Host.Security sealed class TokenFactory : ITokenFactory { /// - public TokenValidationParameters ValidationParameters { get; } + public TokenValidationParameters ValidationParameters { get; private set; } + + /// + public ReadOnlySpan SigningKey + { + get => signingKey; + set + { + signingKey = value.ToArray(); + SetValidationParameters(); + } + } + + /// + /// The for the . + /// + readonly IAssemblyInformationProvider assemblyInformationProvider; /// /// The for the . @@ -36,11 +53,16 @@ namespace Tgstation.Server.Host.Security /// readonly JwtSecurityTokenHandler tokenHandler; + /// + /// Backing field for . + /// + byte[] signingKey; + /// /// Initializes a new instance of the class. /// /// The used for generating the . - /// The used to generate the issuer name. + /// The value of . /// The containing the value of . public TokenFactory( ICryptographySuite cryptographySuite, @@ -48,32 +70,15 @@ namespace Tgstation.Server.Host.Security IOptions securityConfigurationOptions) { ArgumentNullException.ThrowIfNull(cryptographySuite); - ArgumentNullException.ThrowIfNull(assemblyInformationProvider); + this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); securityConfiguration = securityConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(securityConfigurationOptions)); - var signingKeyBytes = String.IsNullOrWhiteSpace(securityConfiguration.CustomTokenSigningKeyBase64) + signingKey = String.IsNullOrWhiteSpace(securityConfiguration.CustomTokenSigningKeyBase64) ? cryptographySuite.GetSecureBytes(securityConfiguration.TokenSigningKeyByteCount) : Convert.FromBase64String(securityConfiguration.CustomTokenSigningKeyBase64); - ValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes), - - ValidateIssuer = true, - ValidIssuer = assemblyInformationProvider.AssemblyName.Name, - - ValidateLifetime = true, - ValidateAudience = true, - ValidAudience = typeof(TokenResponse).Assembly.GetName().Name, - - ClockSkew = TimeSpan.FromMinutes(securityConfiguration.TokenClockSkewMinutes), - - RequireSignedTokens = true, - - RequireExpirationTime = true, - }; + SetValidationParameters(); tokenHeader = new JwtHeader( new SigningCredentials( @@ -128,5 +133,29 @@ namespace Tgstation.Server.Host.Security return tokenResponse; } + + /// + /// Initializes based on fields. + /// + [MemberNotNull(nameof(ValidationParameters))] + void SetValidationParameters() + => ValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(signingKey), + + ValidateIssuer = true, + ValidIssuer = assemblyInformationProvider.AssemblyName.Name, + + ValidateLifetime = true, + ValidateAudience = true, + ValidAudience = typeof(TokenResponse).Assembly.GetName().Name, + + ClockSkew = TimeSpan.FromMinutes(securityConfiguration.TokenClockSkewMinutes), + + RequireSignedTokens = true, + + RequireExpirationTime = true, + }; } }