diff --git a/src/Tgstation.Server.Host/Authority/ILoginAuthority.cs b/src/Tgstation.Server.Host/Authority/ILoginAuthority.cs
index 527cf41b85..c769ea6a4a 100644
--- a/src/Tgstation.Server.Host/Authority/ILoginAuthority.cs
+++ b/src/Tgstation.Server.Host/Authority/ILoginAuthority.cs
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Tgstation.Server.Host.Authority.Core;
using Tgstation.Server.Host.GraphQL.Mutations.Payloads;
+using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.Authority
{
@@ -15,8 +16,8 @@ namespace Tgstation.Server.Host.Authority
/// Attempt to login to the server with the current Basic or OAuth credentials.
///
/// The for the operation.
- /// A resulting in an authenticated token .
- RequirementsGated> AttemptLogin(CancellationToken cancellationToken);
+ /// A resulting in a .
+ RequirementsGated> AttemptLogin(CancellationToken cancellationToken);
///
/// Attempt to login to an OAuth service with the current OAuth credentials.
diff --git a/src/Tgstation.Server.Host/Authority/LoginAuthority.cs b/src/Tgstation.Server.Host/Authority/LoginAuthority.cs
index f540ce6a4b..8f150845d4 100644
--- a/src/Tgstation.Server.Host/Authority/LoginAuthority.cs
+++ b/src/Tgstation.Server.Host/Authority/LoginAuthority.cs
@@ -139,7 +139,7 @@ namespace Tgstation.Server.Host.Authority
}
///
- public RequirementsGated> AttemptLogin(CancellationToken cancellationToken)
+ public RequirementsGated> AttemptLogin(CancellationToken cancellationToken)
=> new(
() => null,
() => AttemptLoginImpl(cancellationToken),
@@ -176,19 +176,19 @@ namespace Tgstation.Server.Host.Authority
/// Login process.
///
/// The for the operation.
- /// A resulting in the containing the authenticated bearer token.
- private async ValueTask> AttemptLoginImpl(CancellationToken cancellationToken)
+ /// A resulting in the containing the .
+ private async ValueTask> AttemptLoginImpl(CancellationToken cancellationToken)
{
// password and oauth logins disabled
if (securityConfigurationOptions.Value.OidcStrictMode)
- return Unauthorized();
+ return Unauthorized();
var headers = apiHeadersProvider.ApiHeaders;
if (headers == null)
- return GenerateHeadersExceptionResponse(apiHeadersProvider.HeadersException!);
+ return GenerateHeadersExceptionResponse(apiHeadersProvider.HeadersException!);
if (headers.IsTokenAuthentication)
- return BadRequest(ErrorCode.TokenWithToken);
+ return BadRequest(ErrorCode.TokenWithToken);
var oAuthLogin = headers.OAuthProvider.HasValue;
@@ -211,7 +211,7 @@ namespace Tgstation.Server.Host.Authority
if (oAuthLogin)
{
var oAuthProvider = headers.OAuthProvider!.Value;
- var (errorResponse, oauthResult) = await TryOAuthenticate(headers, oAuthProvider, true, cancellationToken);
+ var (errorResponse, oauthResult) = await TryOAuthenticate(headers, oAuthProvider, true, cancellationToken);
if (errorResponse != null)
return errorResponse;
@@ -224,7 +224,7 @@ namespace Tgstation.Server.Host.Authority
{
var canonicalUserName = User.CanonicalizeName(headers.Username!);
if (canonicalUserName == User.CanonicalizeName(User.TgsSystemUserName))
- return Unauthorized();
+ return Unauthorized();
if (systemIdentity == null)
query = query.Where(x => x.CanonicalName == canonicalUserName);
@@ -236,7 +236,7 @@ namespace Tgstation.Server.Host.Authority
// No user? You're not allowed
if (user == null)
- return Unauthorized();
+ return Unauthorized();
// A system user may have had their name AND password changed to one in our DB...
// Or a DB user was created that had the same user/pass as a system user
@@ -251,7 +251,7 @@ namespace Tgstation.Server.Host.Authority
{
// DB User password check and update
if (!isLikelyDbUser || !cryptographySuite.CheckUserPassword(user, headers.Password!))
- return Unauthorized();
+ return Unauthorized();
if (user.PasswordHash != originalHash)
{
Logger.LogDebug("User ID {userId}'s password hash needs a refresh, updating database.", user.Id);
@@ -294,17 +294,17 @@ namespace Tgstation.Server.Host.Authority
if (!user.Enabled!.Value)
{
Logger.LogTrace("Not logging in disabled user {userId}.", user.Id);
- return Forbid();
+ return Forbid();
}
- var (token, expiresAt) = tokenFactory.CreateToken(user, oAuthLogin);
+ var token = tokenFactory.CreateToken(user, oAuthLogin);
if (usingSystemIdentity)
- await CacheSystemIdentity(systemIdentity!, user, expiresAt);
+ await CacheSystemIdentity(systemIdentity!, user, token.Expiry);
Logger.LogDebug("Successfully logged in user {userId}!", user.Id);
- return new AuthorityResponse(token);
+ return new AuthorityResponse(token);
}
}
diff --git a/src/Tgstation.Server.Host/Controllers/ApiRootController.cs b/src/Tgstation.Server.Host/Controllers/ApiRootController.cs
index 0163af7bc3..97d171d7a6 100644
--- a/src/Tgstation.Server.Host/Controllers/ApiRootController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ApiRootController.cs
@@ -195,7 +195,7 @@ namespace Tgstation.Server.Host.Controllers
return ValueTask.FromResult(HeadersIssue(ApiHeadersProvider.HeadersException!));
}
- return loginAuthority.InvokeTransformable(this, authority => authority.AttemptLogin(cancellationToken));
+ return loginAuthority.InvokeTransformable(this, authority => authority.AttemptLogin(cancellationToken));
}
///
diff --git a/src/Tgstation.Server.Host/Controllers/Transformers/TokenResponseTransformer.cs b/src/Tgstation.Server.Host/Controllers/Transformers/TokenResponseTransformer.cs
index 7b7851100f..91ccf19147 100644
--- a/src/Tgstation.Server.Host/Controllers/Transformers/TokenResponseTransformer.cs
+++ b/src/Tgstation.Server.Host/Controllers/Transformers/TokenResponseTransformer.cs
@@ -1,12 +1,13 @@
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Models;
+using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.Controllers.Transformers
{
///
/// for s.
///
- sealed class TokenResponseTransformer : TransformerBase
+ sealed class TokenResponseTransformer : TransformerBase
{
///
/// Initializes a new instance of the class.
@@ -15,7 +16,7 @@ namespace Tgstation.Server.Host.Controllers.Transformers
: base(
token => new TokenResponse
{
- Bearer = token,
+ Bearer = token.Token,
})
{
}
diff --git a/src/Tgstation.Server.Host/GraphQL/Mutation.cs b/src/Tgstation.Server.Host/GraphQL/Mutation.cs
index cee0635398..613456ca02 100644
--- a/src/Tgstation.Server.Host/GraphQL/Mutation.cs
+++ b/src/Tgstation.Server.Host/GraphQL/Mutation.cs
@@ -9,6 +9,7 @@ using HotChocolate.Types;
using Tgstation.Server.Host.Authority;
using Tgstation.Server.Host.GraphQL.Mutations.Payloads;
using Tgstation.Server.Host.GraphQL.Transformers;
+using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.GraphQL
{
@@ -39,7 +40,7 @@ namespace Tgstation.Server.Host.GraphQL
{
ArgumentNullException.ThrowIfNull(loginAuthority);
- return loginAuthority.InvokeTransformable(
+ return loginAuthority.InvokeTransformable(
authority => authority.AttemptLogin(cancellationToken));
}
diff --git a/src/Tgstation.Server.Host/GraphQL/Transformers/LoginResultTransformer.cs b/src/Tgstation.Server.Host/GraphQL/Transformers/LoginResultTransformer.cs
index 0c22ae2a63..4976d6696a 100644
--- a/src/Tgstation.Server.Host/GraphQL/Transformers/LoginResultTransformer.cs
+++ b/src/Tgstation.Server.Host/GraphQL/Transformers/LoginResultTransformer.cs
@@ -1,12 +1,13 @@
using Tgstation.Server.Host.GraphQL.Mutations.Payloads;
using Tgstation.Server.Host.Models;
+using Tgstation.Server.Host.Security;
namespace Tgstation.Server.Host.GraphQL.Transformers
{
///
/// for s.
///
- sealed class LoginResultTransformer : TransformerBase
+ sealed class LoginResultTransformer : TransformerBase
{
///
/// Initializes a new instance of the class.
@@ -15,7 +16,7 @@ namespace Tgstation.Server.Host.GraphQL.Transformers
: base(
token => new LoginResult
{
- Bearer = token,
+ Bearer = token.Token,
})
{
}
diff --git a/src/Tgstation.Server.Host/Security/GeneratedToken.cs b/src/Tgstation.Server.Host/Security/GeneratedToken.cs
new file mode 100644
index 0000000000..1d2cd08bcf
--- /dev/null
+++ b/src/Tgstation.Server.Host/Security/GeneratedToken.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Tgstation.Server.Host.Security
+{
+ ///
+ /// A securely generated token.
+ ///
+ public record struct GeneratedToken
+ {
+ ///
+ /// The token string.
+ ///
+ public required string Token { get; init; }
+
+ ///
+ /// When the token expires.
+ ///
+ public required DateTimeOffset Expiry { get; init; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Security/ITokenFactory.cs b/src/Tgstation.Server.Host/Security/ITokenFactory.cs
index 1baa29e6cc..70993bcf81 100644
--- a/src/Tgstation.Server.Host/Security/ITokenFactory.cs
+++ b/src/Tgstation.Server.Host/Security/ITokenFactory.cs
@@ -26,7 +26,7 @@ namespace Tgstation.Server.Host.Security
///
/// The to create the token for. Must have the field available.
/// Whether or not this is an external service login.
- /// A new token and the that it expires.
- (string Token, DateTimeOffset Expiry) CreateToken(Models.User user, bool serviceLogin);
+ /// A new .
+ GeneratedToken CreateToken(Models.User user, bool serviceLogin);
}
}
diff --git a/src/Tgstation.Server.Host/Security/TokenFactory.cs b/src/Tgstation.Server.Host/Security/TokenFactory.cs
index d41803ef4d..e1d526e109 100644
--- a/src/Tgstation.Server.Host/Security/TokenFactory.cs
+++ b/src/Tgstation.Server.Host/Security/TokenFactory.cs
@@ -101,7 +101,7 @@ namespace Tgstation.Server.Host.Security
}
///
- public (string Token, DateTimeOffset Expiry) CreateToken(User user, bool serviceLogin)
+ public GeneratedToken CreateToken(User user, bool serviceLogin)
{
ArgumentNullException.ThrowIfNull(user);
@@ -141,7 +141,11 @@ namespace Tgstation.Server.Host.Security
var tokenResponse = tokenHandler.WriteToken(securityToken);
- return (Token: tokenResponse, Expiry: expiry);
+ return new GeneratedToken
+ {
+ Token = tokenResponse,
+ Expiry = expiry,
+ };
}
}
}
diff --git a/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs b/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs
index 60e465c342..6c7fd5f3d4 100644
--- a/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs
+++ b/tests/Tgstation.Server.Host.Tests/Swarm/TestableSwarmNode.cs
@@ -89,7 +89,7 @@ namespace Tgstation.Server.Host.Swarm.Tests
public TokenValidationParameters ValidationParameters => throw new NotSupportedException();
- public (string, DateTimeOffset) CreateToken(User user, bool serviceLogin)
+ public GeneratedToken CreateToken(User user, bool serviceLogin)
{
throw new NotSupportedException();
}