tgstation-server 6.19.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
TokenFactory.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Diagnostics.CodeAnalysis;
4using System.Globalization;
5using System.IdentityModel.Tokens.Jwt;
6using System.Linq;
7using System.Security.Claims;
8
9using Microsoft.Extensions.Options;
10using Microsoft.IdentityModel.Tokens;
11
16
18{
21 {
23 public TokenValidationParameters ValidationParameters { get; }
24
26 public ReadOnlySpan<byte> SigningKeyBytes
27 {
28 get => signingKey.Key;
29 [MemberNotNull(nameof(signingKey))]
30 [MemberNotNull(nameof(tokenHeader))]
31 set
32 {
33 signingKey = new SymmetricSecurityKey(value.ToArray());
34 tokenHeader = new JwtHeader(
35 new SigningCredentials(
37 SecurityAlgorithms.HmacSha256));
38 }
39 }
40
44 readonly IOptions<SecurityConfiguration> securityConfigurationOptions;
45
49 readonly JwtSecurityTokenHandler tokenHandler;
50
54 SymmetricSecurityKey signingKey;
55
59 JwtHeader tokenHeader;
60
68 ICryptographySuite cryptographySuite,
69 IAssemblyInformationProvider assemblyInformationProvider,
70 IOptions<SecurityConfiguration> securityConfigurationOptions)
71 {
72 ArgumentNullException.ThrowIfNull(cryptographySuite);
73 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
74
75 this.securityConfigurationOptions = securityConfigurationOptions ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
76
77 SigningKeyBytes = string.IsNullOrWhiteSpace(securityConfigurationOptions.Value.CustomTokenSigningKeyBase64)
78 ? cryptographySuite.GetSecureBytes(securityConfigurationOptions.Value.TokenSigningKeyByteCount)
79 : Convert.FromBase64String(securityConfigurationOptions.Value.CustomTokenSigningKeyBase64);
80
81 ValidationParameters = new TokenValidationParameters
82 {
83 ValidateIssuerSigningKey = true,
84 IssuerSigningKeyResolver = (_, _, _, _) => Enumerable.Repeat(signingKey, 1),
85
86 ValidateIssuer = true,
87 ValidIssuer = assemblyInformationProvider.AssemblyName.Name,
88
89 ValidateLifetime = true,
90 ValidateAudience = true,
91 ValidAudience = typeof(TokenResponse).Assembly.GetName().Name,
92
93 ClockSkew = TimeSpan.FromMinutes(securityConfigurationOptions.Value.TokenClockSkewMinutes),
94
95 RequireSignedTokens = true,
96
97 RequireExpirationTime = true,
98 };
99
100 tokenHandler = new JwtSecurityTokenHandler();
101 }
102
104 public string CreateToken(User user, bool serviceLogin)
105 {
106 ArgumentNullException.ThrowIfNull(user);
107
108 var uid = user.Require(x => x.Id);
109 var now = DateTimeOffset.UtcNow;
110 var nowUnix = now.ToUnixTimeSeconds();
111
112 // this prevents validation conflicts down the line
113 // tldr we can (theoretically) receive a token the same second after we generate it
114 // since unix time rounds down, it looks like it came from before the user changed their password
115 // this happens occasionally in unit tests
116 // just delay a second so we can force a round up
117 var userLastPassworUpdateUnix = user.LastPasswordUpdate?.ToUnixTimeSeconds();
118 DateTimeOffset notBefore;
119 if (nowUnix == userLastPassworUpdateUnix)
120 notBefore = now.AddSeconds(1);
121 else
122 notBefore = now;
123
124 var expiry = now.AddMinutes(serviceLogin
125 ? securityConfigurationOptions.Value.OAuthTokenExpiryMinutes
126 : securityConfigurationOptions.Value.TokenExpiryMinutes);
127
128 var securityToken = new JwtSecurityToken(
130 new JwtPayload(
131 ValidationParameters.ValidIssuer,
132 ValidationParameters.ValidAudience,
133 Enumerable.Empty<Claim>(),
134 new Dictionary<string, object>
135 {
136 { JwtRegisteredClaimNames.Sub, uid.ToString(CultureInfo.InvariantCulture) },
137 },
138 notBefore.UtcDateTime,
139 expiry.UtcDateTime,
140 now.UtcDateTime));
141
142 var tokenResponse = tokenHandler.WriteToken(securityToken);
143
144 return tokenResponse;
145 }
146 }
147}
Represents a JWT returned by the API.
DateTimeOffset? LastPasswordUpdate
When PasswordHash was last changed.
Definition User.cs:63
SymmetricSecurityKey signingKey
Backing field for SigningKeyBytes.
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
ReadOnlySpan< byte > SigningKeyBytes
Gets or sets the ITokenFactory's signing key bytes.
readonly JwtSecurityTokenHandler tokenHandler
The JwtSecurityTokenHandler used to generate TokenResponse.Bearer strings.
string CreateToken(User user, bool serviceLogin)
Create a TokenResponse for a given user .A new token string.
TokenFactory(ICryptographySuite cryptographySuite, IAssemblyInformationProvider assemblyInformationProvider, IOptions< SecurityConfiguration > securityConfigurationOptions)
Initializes a new instance of the TokenFactory class.
readonly IOptions< SecurityConfiguration > securityConfigurationOptions
The IOptions<TOptions> of SecurityConfiguration for the TokenFactory.
JwtHeader tokenHeader
The JwtHeader for generating tokens.
Contains various cryptographic functions.
byte[] GetSecureBytes(uint amount)
Generates a secure set of bytes.
AssemblyName AssemblyName
Gets the global::System.Reflection.AssemblyName.