tgstation-server 6.19.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
18
20{
25 {
27 public abstract OAuthProvider Provider { get; }
28
30 public OAuthGatewayStatus GatewayStatus => OAuthConfiguration.Gateway ?? default;
31
35 protected ILogger<GenericOAuthValidator> Logger { get; }
36
41
45 protected abstract Uri TokenUrl { get; }
46
50 protected abstract Uri UserInformationUrl { get; }
51
55 readonly IHttpClientFactory httpClientFactory;
56
61 protected static JsonSerializerSettings SerializerSettings() => new()
62 {
63 ContractResolver = new DefaultContractResolver
64 {
65 NamingStrategy = new SnakeCaseNamingStrategy(),
66 },
67 };
68
76 IHttpClientFactory httpClientFactory,
77 ILogger<GenericOAuthValidator> logger,
78 OAuthConfiguration oAuthConfiguration)
79 {
80 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
81 Logger = logger ?? throw new ArgumentNullException(nameof(logger));
82 OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration));
83 }
84
86 public async ValueTask<(string? UserID, string AccessCode)?> ValidateResponseCode(string code, bool requireUserID, CancellationToken cancellationToken)
87 {
88 using var httpClient = CreateHttpClient();
89 string? tokenResponsePayload = null;
90 string? userInformationPayload = null;
91 try
92 {
93 Logger.LogTrace("Validating response code...");
94 using var tokenRequest = new HttpRequestMessage(HttpMethod.Post, TokenUrl);
95
96 var tokenRequestPayload = CreateTokenRequest(code);
97
98 // roundabout but it works
99 var tokenRequestJson = JsonConvert.SerializeObject(
100 tokenRequestPayload,
102
103 var tokenRequestDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(tokenRequestJson)!;
104 tokenRequest.Content = new FormUrlEncodedContent(tokenRequestDictionary);
105
106 using var tokenResponse = await httpClient.SendAsync(tokenRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
107 tokenResponse.EnsureSuccessStatusCode();
108 tokenResponsePayload = await tokenResponse.Content.ReadAsStringAsync(cancellationToken);
109 var tokenResponseJson = JObject.Parse(tokenResponsePayload);
110
111 var accessToken = DecodeTokenPayload(tokenResponseJson);
112 if (accessToken == null)
113 {
114 Logger.LogTrace("No token from DecodeTokenPayload!");
115 return null;
116 }
117
118 if (!requireUserID)
119 return (null, AccessCode: accessToken);
120
121 Logger.LogTrace("Getting user details...");
122
124 using var userInformationRequest = new HttpRequestMessage(HttpMethod.Get, userInfoUrl);
125 userInformationRequest.Headers.Authorization = new AuthenticationHeaderValue(
127 accessToken);
128
129 using var userInformationResponse = await httpClient.SendAsync(userInformationRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
130 userInformationResponse.EnsureSuccessStatusCode();
131 userInformationPayload = await userInformationResponse.Content.ReadAsStringAsync(cancellationToken);
132
133 var userInformationJson = JObject.Parse(userInformationPayload);
134
135 return (DecodeUserInformationPayload(userInformationJson), AccessCode: accessToken);
136 }
137 catch (Exception ex)
138 {
139 Logger.LogWarning(
140 ex,
141 "Error while completing OAuth handshake! Payload:{newLine}{responsePayload}",
142 Environment.NewLine,
143 userInformationPayload ?? tokenResponsePayload);
144 return null;
145 }
146 }
147
150 => new()
151 {
152 ClientId = OAuthConfiguration.ClientId,
153 RedirectUri = OAuthConfiguration.RedirectUrl,
154 ServerUrl = OAuthConfiguration.ServerUrl,
155 GatewayOnly = GatewayStatus.ToBoolean(),
156 };
157
163 protected abstract string DecodeTokenPayload(dynamic responseJson);
164
170 protected abstract string DecodeUserInformationPayload(dynamic responseJson);
171
177 protected abstract OAuthTokenRequest CreateTokenRequest(string code);
178
183 HttpClient CreateHttpClient()
184 {
185 var httpClient = httpClientFactory.CreateClient();
186 try
187 {
188 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
189 return httpClient;
190 }
191 catch
192 {
193 httpClient.Dispose();
194 throw;
195 }
196 }
197 }
198}
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.
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 .
GenericOAuthValidator(IHttpClientFactory httpClientFactory, ILogger< GenericOAuthValidator > logger, OAuthConfiguration oAuthConfiguration)
Initializes a new instance of the GenericOAuthValidator class.
readonly IHttpClientFactory httpClientFactory
The IHttpClientFactory for the GenericOAuthValidator.
HttpClient CreateHttpClient()
Create a new configured HttpClient.
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.
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.
Validates OAuth responses for a given Provider.
OAuthProvider
List of OAuth2.0 providers supported by TGS that do not support OIDC.
OAuthGatewayStatus
Status of the OAuth gateway for a provider.