mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-27 15:07:03 +01:00
Merge remote-tracking branch 'origin/OAuthUpdate' into dev
This commit is contained in:
+1
-1
@@ -146,7 +146,7 @@ You will be granted a bearer token as in basic auth. This will have an extended
|
||||
|
||||
- GitHub: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps
|
||||
- Discord: https://discord.com/developers/docs/topics/oauth2
|
||||
- TGForums: https://tgstation13.org/phpBB/viewtopic.php?f=45&t=9922
|
||||
- TGForums: https://tgstation13.org/phpBB/viewtopic.php?f=45&t=30155
|
||||
- Keycloak: https://plugins.miniorange.com/keycloak-single-sign-on-wordpress-sso-oauth-openid-connect
|
||||
|
||||
@section api_perms Permissions
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Base <see langword="class"/> for <see cref="IOAuthValidator"/>s.
|
||||
/// </summary>
|
||||
abstract class BaseOAuthValidator : IOAuthValidator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public abstract OAuthProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="BaseOAuthValidator"/>.
|
||||
/// </summary>
|
||||
protected ILogger<BaseOAuthValidator> Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthConfiguration"/> for the <see cref="BaseOAuthValidator"/>.
|
||||
/// </summary>
|
||||
protected OAuthConfiguration OAuthConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IHttpClientFactory"/> for the <see cref="BaseOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IHttpClientFactory httpClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAssemblyInformationProvider"/> for the <see cref="BaseOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="JsonSerializerSettings"/> that should be used.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="JsonSerializerSettings"/> <see cref="object"/>.</returns>
|
||||
protected static JsonSerializerSettings SerializerSettings() => new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new SnakeCaseNamingStrategy(),
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseOAuthValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClientFactory">The value of <see cref="httpClientFactory"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="Logger"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The value of <see cref="OAuthConfiguration"/>.</param>
|
||||
public BaseOAuthValidator(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
ILogger<BaseOAuthValidator> logger,
|
||||
OAuthConfiguration oAuthConfiguration)
|
||||
{
|
||||
this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Task<OAuthProviderInfo> GetProviderInfo(CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Task<string> ValidateResponseCode(string code, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new configured <see cref="HttpClient"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new configured <see cref="HttpClient"/>.</returns>
|
||||
protected HttpClient CreateHttpClient()
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
try
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue);
|
||||
return httpClient;
|
||||
}
|
||||
catch
|
||||
{
|
||||
httpClient.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,15 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// </summary>
|
||||
sealed class DiscordOAuthValidator : GenericOAuthValidator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override OAuthProvider Provider => OAuthProvider.Discord;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Uri TokenUrl => new Uri("https://discord.com/api/oauth2/token");
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Uri UserInformationUrl => new Uri("https://discord.com/api/users/@me");
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DiscordOAuthValidator"/> class.
|
||||
/// </summary>
|
||||
@@ -30,15 +39,6 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override OAuthProvider Provider => OAuthProvider.Discord;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Uri TokenUrl => new Uri("https://discord.com/api/oauth2/token");
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Uri UserInformationUrl => new Uri("https://discord.com/api/users/@me");
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "identify");
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -19,8 +21,21 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// <summary>
|
||||
/// <see cref="IOAuthValidator"/> for generic OAuth2 endpoints.
|
||||
/// </summary>
|
||||
abstract class GenericOAuthValidator : BaseOAuthValidator
|
||||
abstract class GenericOAuthValidator : IOAuthValidator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public abstract OAuthProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="GenericOAuthValidator"/>.
|
||||
/// </summary>
|
||||
protected ILogger<GenericOAuthValidator> Logger { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthConfiguration"/> for the <see cref="GenericOAuthValidator"/>.
|
||||
/// </summary>
|
||||
protected OAuthConfiguration OAuthConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Uri"/> to <see cref="HttpMethod.Post"/> to to get the access token.
|
||||
/// </summary>
|
||||
@@ -31,28 +46,49 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// </summary>
|
||||
protected abstract Uri UserInformationUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IHttpClientFactory"/> for the <see cref="GenericOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IHttpClientFactory httpClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAssemblyInformationProvider"/> for the <see cref="GenericOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="JsonSerializerSettings"/> that should be used.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="JsonSerializerSettings"/> <see cref="object"/>.</returns>
|
||||
protected static JsonSerializerSettings SerializerSettings() => new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new SnakeCaseNamingStrategy(),
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GenericOAuthValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The <see cref="OAuthConfiguration"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="httpClientFactory">The value of <see cref="httpClientFactory"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="Logger"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The value of <see cref="OAuthConfiguration"/>.</param>
|
||||
public GenericOAuthValidator(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
ILogger<GenericOAuthValidator> logger,
|
||||
OAuthConfiguration oAuthConfiguration)
|
||||
: base(
|
||||
httpClientFactory,
|
||||
assemblyInformationProvider,
|
||||
logger,
|
||||
oAuthConfiguration)
|
||||
{
|
||||
this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<string> ValidateResponseCode(string code, CancellationToken cancellationToken)
|
||||
public async Task<string> ValidateResponseCode(string code, CancellationToken cancellationToken)
|
||||
{
|
||||
using var httpClient = CreateHttpClient();
|
||||
string tokenResponsePayload = null;
|
||||
@@ -110,7 +146,7 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<OAuthProviderInfo> GetProviderInfo(CancellationToken cancellationToken) => Task.FromResult(
|
||||
public Task<OAuthProviderInfo> GetProviderInfo(CancellationToken cancellationToken) => Task.FromResult(
|
||||
new OAuthProviderInfo
|
||||
{
|
||||
ClientId = OAuthConfiguration.ClientId,
|
||||
@@ -138,5 +174,25 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// <param name="code">The OAuth code from the browser.</param>
|
||||
/// <returns>The <see cref="OAuthTokenRequest"/> to send to <see cref="TokenUrl"/>.</returns>
|
||||
protected abstract OAuthTokenRequest CreateTokenRequest(string code);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new configured <see cref="HttpClient"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new configured <see cref="HttpClient"/>.</returns>
|
||||
HttpClient CreateHttpClient()
|
||||
{
|
||||
var httpClient = httpClientFactory.CreateClient();
|
||||
try
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
|
||||
httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue);
|
||||
return httpClient;
|
||||
}
|
||||
catch
|
||||
{
|
||||
httpClient.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Base <see langword="class"/> for tgstation forum responses.
|
||||
/// </summary>
|
||||
abstract class TGBaseResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Expected value of <see cref="Status"/>.
|
||||
/// </summary>
|
||||
public const string OkStatus = "OK";
|
||||
|
||||
/// <summary>
|
||||
/// The response status.
|
||||
/// </summary>
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The response error, if any.
|
||||
/// </summary>
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Response when creating a tgstation forums session.
|
||||
/// </summary>
|
||||
sealed class TGCreateSessionResponse : TGBaseResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The session's private token. Similar to OAuth authorization response code.
|
||||
/// </summary>
|
||||
public string SessionPrivateToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The session's public token. Barely similar to OAuth client ID.
|
||||
/// </summary>
|
||||
public string SessionPublicToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
@@ -19,28 +12,24 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
/// <summary>
|
||||
/// <see cref="IOAuthValidator"/> for /tg/ forums.
|
||||
/// </summary>
|
||||
sealed class TGForumsOAuthValidator : BaseOAuthValidator
|
||||
sealed class TGForumsOAuthValidator : GenericOAuthValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Amount of minutes until unused sessions that were created are forgotten.
|
||||
/// </summary>
|
||||
const uint SessionRetentionMinutes = 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override OAuthProvider Provider => OAuthProvider.TGForums;
|
||||
|
||||
/// <summary>
|
||||
/// The active session.
|
||||
/// </summary>
|
||||
readonly List<Tuple<TGCreateSessionResponse, DateTimeOffset>> sessions;
|
||||
/// <inheritdoc />
|
||||
protected override Uri TokenUrl => new Uri("https://tgstation13.org/phpBB/app.php/tgapi/oauth/token");
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Uri UserInformationUrl => new Uri("https://tgstation13.org/phpBB/app.php/tgapi/user/me");
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TGForumsOAuthValidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The <see cref="OAuthConfiguration"/> for the <see cref="BaseOAuthValidator"/>.</param>
|
||||
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for the <see cref="GenericOAuthValidator"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The <see cref="IAssemblyInformationProvider"/> for the <see cref="GenericOAuthValidator"/>.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> for the <see cref="GenericOAuthValidator"/>.</param>
|
||||
/// <param name="oAuthConfiguration">The <see cref="OAuthConfiguration"/> for the <see cref="GenericOAuthValidator"/>.</param>
|
||||
public TGForumsOAuthValidator(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
@@ -52,98 +41,15 @@ namespace Tgstation.Server.Host.Security.OAuth
|
||||
logger,
|
||||
oAuthConfiguration)
|
||||
{
|
||||
sessions = new List<Tuple<TGCreateSessionResponse, DateTimeOffset>>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<OAuthProviderInfo> GetProviderInfo(CancellationToken cancellationToken)
|
||||
{
|
||||
var expiredSessions = sessions.RemoveAll(x => x.Item2.AddMinutes(SessionRetentionMinutes) < DateTimeOffset.UtcNow);
|
||||
if (expiredSessions > 0)
|
||||
Logger.LogTrace("Expired {0} sessions", expiredSessions);
|
||||
|
||||
Logger.LogTrace("Creating new session...");
|
||||
try
|
||||
{
|
||||
UriBuilder builder = new UriBuilder("https://tgstation13.org/phpBB/oauth_create_session.php")
|
||||
{
|
||||
Query = $"site_private_token={HttpUtility.UrlEncode(Convert.ToBase64String(Encoding.UTF8.GetBytes(OAuthConfiguration.ClientSecret)))}&return_uri={HttpUtility.UrlEncode(OAuthConfiguration.RedirectUrl.ToString())}",
|
||||
};
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
|
||||
using var httpClient = CreateHttpClient();
|
||||
|
||||
using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
var newSession = JsonConvert.DeserializeObject<TGCreateSessionResponse>(json, SerializerSettings());
|
||||
|
||||
if (newSession.Status != TGBaseResponse.OkStatus)
|
||||
{
|
||||
Logger.LogWarning("Invalid status from /tg/ API! Status: {0}, Error: {1}", newSession.Status, newSession.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
sessions.Add(
|
||||
Tuple.Create(
|
||||
newSession,
|
||||
DateTimeOffset.UtcNow));
|
||||
return new OAuthProviderInfo
|
||||
{
|
||||
ClientId = newSession.SessionPublicToken,
|
||||
RedirectUri = OAuthConfiguration.RedirectUrl,
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to create TG Forums session!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<string> ValidateResponseCode(string code, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sessionTuple = sessions.FirstOrDefault(x => x.Item1.SessionPublicToken == code);
|
||||
if (sessionTuple == null)
|
||||
{
|
||||
Logger.LogWarning("No known session with this code active!");
|
||||
return null;
|
||||
}
|
||||
protected override string DecodeUserInformationPayload(dynamic responseJson) => responseJson.phpbb_username;
|
||||
|
||||
Logger.LogTrace("Validating session...");
|
||||
|
||||
UriBuilder builder = new UriBuilder("https://tgstation13.org/phpBB/oauth_get_session_info.php")
|
||||
{
|
||||
Query = $"site_private_token={HttpUtility.UrlEncode(Convert.ToBase64String(Encoding.UTF8.GetBytes(OAuthConfiguration.ClientSecret)))}&session_private_token={HttpUtility.UrlEncode(sessionTuple.Item1.SessionPrivateToken)}",
|
||||
};
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
|
||||
using var httpClient = CreateHttpClient();
|
||||
|
||||
using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
var sessionInfo = JsonConvert.DeserializeObject<TGGetSessionInfoResponse>(json, SerializerSettings());
|
||||
|
||||
if (sessionInfo.Status != TGBaseResponse.OkStatus)
|
||||
{
|
||||
Logger.LogWarning("Invalid status from /tg/ API! Status: {0}, Error: {1}", sessionInfo.Status, sessionInfo.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
sessions.Remove(sessionTuple);
|
||||
return sessionInfo.PhpbbUsername;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to create TG Forums session!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "user");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Response when getting tgstation forum user's info.
|
||||
/// </summary>
|
||||
sealed class TGGetSessionInfoResponse : TGBaseResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The user's forum account name.
|
||||
/// </summary>
|
||||
public string PhpbbUsername { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user