tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
GenericOAuthValidator.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Net.Http;
4using System.Net.Http.Headers;
5using System.Net.Mime;
6using System.Threading;
7using System.Threading.Tasks;
8
9using Microsoft.Extensions.Logging;
10using Newtonsoft.Json;
11using Newtonsoft.Json.Linq;
12using Newtonsoft.Json.Serialization;
13
19
21{
26 {
28 public abstract OAuthProvider Provider { get; }
29
31 public OAuthGatewayStatus GatewayStatus => OAuthConfiguration.Gateway ?? default;
32
36 protected ILogger<GenericOAuthValidator> Logger { get; }
37
42
46 protected abstract Uri TokenUrl { get; }
47
51 protected abstract Uri UserInformationUrl { get; }
52
57
62 protected static JsonSerializerSettings SerializerSettings() => new()
63 {
64 ContractResolver = new DefaultContractResolver
65 {
66 NamingStrategy = new SnakeCaseNamingStrategy(),
67 },
68 };
69
78 ILogger<GenericOAuthValidator> logger,
79 OAuthConfiguration oAuthConfiguration)
80 {
81 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
82 Logger = logger ?? throw new ArgumentNullException(nameof(logger));
83 OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration));
84 }
85
87 public async ValueTask<(string? UserID, string AccessCode)?> ValidateResponseCode(string code, bool requireUserID, CancellationToken cancellationToken)
88 {
89 using var httpClient = CreateHttpClient();
90 string? tokenResponsePayload = null;
91 string? userInformationPayload = null;
92 try
93 {
94 Logger.LogTrace("Validating response code...");
95 using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, TokenUrl);
96
97 var tokenRequestPayload = CreateTokenRequest(code);
98
99 // roundabout but it works
100 var tokenRequestJson = JsonConvert.SerializeObject(
101 tokenRequestPayload,
103
104 var tokenRequestDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(tokenRequestJson)!;
105 tokenRequest.Content = new FormUrlEncodedContent(tokenRequestDictionary);
106
107 using var tokenResponse = await httpClient.SendAsync(tokenRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
108 tokenResponse.EnsureSuccessStatusCode();
109 tokenResponsePayload = await tokenResponse.Content.ReadAsStringAsync(cancellationToken);
110 var tokenResponseJson = JObject.Parse(tokenResponsePayload);
111
112 var accessToken = DecodeTokenPayload(tokenResponseJson);
113 if (accessToken == null)
114 {
115 Logger.LogTrace("No token from DecodeTokenPayload!");
116 return null;
117 }
118
119 if (!requireUserID)
120 return (null, AccessCode: accessToken);
121
122 Logger.LogTrace("Getting user details...");
123
125 using var userInformationRequest = new HttpRequestMessage(HttpMethod.Get, userInfoUrl);
126 userInformationRequest.Headers.Authorization = new AuthenticationHeaderValue(
128 accessToken);
129
130 using var userInformationResponse = await httpClient.SendAsync(userInformationRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
131 userInformationResponse.EnsureSuccessStatusCode();
132 userInformationPayload = await userInformationResponse.Content.ReadAsStringAsync(cancellationToken);
133
134 var userInformationJson = JObject.Parse(userInformationPayload);
135
136 return (DecodeUserInformationPayload(userInformationJson), AccessCode: accessToken);
137 }
138 catch (Exception ex)
139 {
140 Logger.LogWarning(
141 ex,
142 "Error while completing OAuth handshake! Payload:{newLine}{responsePayload}",
143 Environment.NewLine,
144 userInformationPayload ?? tokenResponsePayload);
145 return null;
146 }
147 }
148
151 => new()
152 {
153 ClientId = OAuthConfiguration.ClientId,
154 RedirectUri = OAuthConfiguration.RedirectUrl,
155 ServerUrl = OAuthConfiguration.ServerUrl,
156 GatewayOnly = GatewayStatus.ToBoolean(),
157 };
158
164 protected abstract string DecodeTokenPayload(dynamic responseJson);
165
171 protected abstract string DecodeUserInformationPayload(dynamic responseJson);
172
178 protected abstract OAuthTokenRequest CreateTokenRequest(string code);
179
185 {
186 var httpClient = httpClientFactory.CreateClient();
187 try
188 {
189 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
190 return httpClient;
191 }
192 catch
193 {
194 httpClient.Dispose();
195 throw;
196 }
197 }
198 }
199}
Represents the header that must be present for every server request.
Definition ApiHeaders.cs:25
const string BearerAuthenticationScheme
The JWT authentication header scheme.
Definition ApiHeaders.cs:44
Public information about a given OAuthProvider.
Uri? UserInformationUrlOverride
User information URL override. Not supported by the Api.Models.OAuthProvider.GitHub provider.
Uri? RedirectUrl
The authentication server URL. Not used by all providers.
Uri? ServerUrl
The client redirect URL. Not used by all providers.
GenericOAuthValidator(IAbstractHttpClientFactory httpClientFactory, ILogger< GenericOAuthValidator > logger, OAuthConfiguration oAuthConfiguration)
Initializes a new instance of the GenericOAuthValidator class.
OAuthProviderInfo GetProviderInfo()
Gets the OAuthProvider of validator.The client ID of the validator on success, null on failure.
string DecodeUserInformationPayload(dynamic responseJson)
Decode the user information payload responseJson .
ILogger< GenericOAuthValidator > Logger
The ILogger for the GenericOAuthValidator.
async ValueTask<(string? UserID, string AccessCode)?> ValidateResponseCode(string code, bool requireUserID, CancellationToken cancellationToken)
Validate a given OAuth response code .A ValueTask<TResult> resulting in null if authentication failed...
Uri UserInformationUrl
Uri to HttpMethod.Get the user information payload from.
string DecodeTokenPayload(dynamic responseJson)
Decode the token payload responseJson .
OAuthProvider Provider
The OAuthProvider this validator is for.
Uri TokenUrl
Uri to HttpMethod.Post to to get the access token.
readonly IAbstractHttpClientFactory httpClientFactory
The IHttpClientFactory for the GenericOAuthValidator.
IHttpClient CreateHttpClient()
Create a new configured IHttpClient.
static JsonSerializerSettings SerializerSettings()
Gets JsonSerializerSettings that should be used.
OAuthTokenRequest CreateTokenRequest(string code)
Create the OAuthTokenRequest for a given code .
OAuthGatewayStatus GatewayStatus
The OAuthGatewayStatus for the IOAuthValidator.
IHttpClient CreateClient()
Create a IHttpClient.
HttpRequestHeaders DefaultRequestHeaders
The HttpRequestHeaders used on every request.
Validates OAuth responses for a given Provider.
OAuthProvider
List of OAuth providers supported by TGS.
OAuthGatewayStatus
Status of the OAuth gateway for a provider.