diff --git a/README.md b/README.md
index a229c9a697..cd2a08d053 100644
--- a/README.md
+++ b/README.md
@@ -357,14 +357,15 @@ Gateway auth simply allows the users to authenticate with the service using the
```yml
Security:
OpenIDConnect:
- Example:
+ Example: # Scheme key: Name of this OIDC scheme (public)
Authority: "..." # This is the root of the URL containing the "/.well-known/openid-configuration" endpoint
ClientId: "..."
ClientSecret: "..."
- FriendlyName: "Example Provider" # Friendly name is how this provider is displayed in UIs
- ThemeColour: "#ff0000" # (Optional) Hex color code indicating the color used to theme UI elements relating to this provider
- ThemeIconUrl: "..." # (Optional) Public URL of an image that can be used to theme UI elements relating to this provider
- UsernameClaim: "preferred_username" # (Optional) Name of claim used to set TGS usernames upon registration. By default "preferred_username" is used. Only applies when using OidcStrictMode.
+ FriendlyName: "Example Provider" # Friendly name is how this provider is displayed in UIs (public)
+ ThemeColour: "#ff0000" # (Optional) Hex color code indicating the color used to theme UI elements relating to this provider (public)
+ ThemeIconUrl: "..." # (Optional) Public URL of an image that can be used to theme UI elements relating to this provider (public)
+ UsernameClaim: "preferred_username" # (Optional) Name of claim used to set TGS usernames upon registration. By default "preferred_username" is used. Only applies when using OidcStrictMode.
+ GroupIdClaim: "tgstation-server-group-id" # (Options) Name of claim used to set TGS group ID upon logging in. By default "tgstation-server-group-id" is used. Only applies when using OidcStrictMode.
```
- `Security:OidcStrictMode`: Boolean flag that, when `true`, disables password and OAuth logins, password changes, individual permission set assignment, and enables user registration using OpenID Connect providers. The claim name `tgstation-server-group-id` is used to dictate what TGS group users are registered to.
diff --git a/build/Version.props b/build/Version.props
index acc1797a58..86560c36ad 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -4,7 +4,7 @@
6.15.2
- 5.6.0
+ 5.7.0
10.13.0
0.6.0
7.0.0
diff --git a/src/Tgstation.Server.Host/Configuration/OidcConfiguration.cs b/src/Tgstation.Server.Host/Configuration/OidcConfiguration.cs
index 3a7062bb44..67380f7e5e 100644
--- a/src/Tgstation.Server.Host/Configuration/OidcConfiguration.cs
+++ b/src/Tgstation.Server.Host/Configuration/OidcConfiguration.cs
@@ -9,6 +9,11 @@ namespace Tgstation.Server.Host.Configuration
///
public sealed class OidcConfiguration
{
+ ///
+ /// The default value of .
+ ///
+ private const string DefaultGroupClaimName = "tgstation-server-group-id";
+
///
/// The containing the .well-known endpoint for the provider.
///
@@ -48,5 +53,10 @@ namespace Tgstation.Server.Host.Configuration
/// The name of the claim used to set the user's name.
///
public string UsernameClaim { get; set; } = JwtRegisteredClaimNames.PreferredUsername;
+
+ ///
+ /// Claim name used to set user groups in OIDC strict mode.
+ ///
+ public string GroupIdClaim { get; set; } = DefaultGroupClaimName;
}
}
diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs
index e06c0ff949..c4958767ee 100644
--- a/src/Tgstation.Server.Host/Core/Application.cs
+++ b/src/Tgstation.Server.Host/Core/Application.cs
@@ -916,6 +916,7 @@ namespace Tgstation.Server.Host.Core
.ValidateOidcToken(
context,
configName,
+ config.GroupIdClaim,
context
.HttpContext
.RequestAborted);
@@ -923,7 +924,7 @@ namespace Tgstation.Server.Host.Core
if (securityConfiguration.OidcStrictMode)
{
options.GetClaimsFromUserInfoEndpoint = true;
- options.ClaimActions.MapUniqueJsonKey(AuthenticationContextFactory.TgsGroupIdClaimName, AuthenticationContextFactory.TgsGroupIdClaimName);
+ options.ClaimActions.MapUniqueJsonKey(config.GroupIdClaim, config.GroupIdClaim);
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = config.UsernameClaim,
diff --git a/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs
index bfa742b840..e4b9e71424 100644
--- a/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs
+++ b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs
@@ -32,11 +32,6 @@ namespace Tgstation.Server.Host.Security
///
public const string OpenIDConnectAuthenticationSchemePrefix = $"{OpenIdConnectDefaults.AuthenticationScheme}.";
- ///
- /// Claim name used to set user groups in OIDC strict mode.
- ///
- public const string TgsGroupIdClaimName = "tgstation-server-group-id";
-
///
/// The the created.
///
@@ -233,7 +228,7 @@ namespace Tgstation.Server.Host.Security
}
///
- public async Task ValidateOidcToken(RemoteAuthenticationContext tokenValidatedContext, string schemeKey, CancellationToken cancellationToken)
+ public async Task ValidateOidcToken(RemoteAuthenticationContext tokenValidatedContext, string schemeKey, string groupIdClaimName, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(tokenValidatedContext);
@@ -270,7 +265,7 @@ namespace Tgstation.Server.Host.Security
}
else
{
- var groupClaim = principal.FindFirst(TgsGroupIdClaimName);
+ var groupClaim = principal.FindFirst(groupIdClaimName);
long? groupId;
if (groupClaim == default)
groupId = null;
@@ -278,7 +273,7 @@ namespace Tgstation.Server.Host.Security
groupId = groupIdParsed;
else
{
- tokenValidatedContext.Fail($"User has non-numeric '{TgsGroupIdClaimName}' claim!");
+ tokenValidatedContext.Fail($"User has non-numeric '{groupIdClaimName}' claim!");
return;
}
@@ -291,7 +286,7 @@ namespace Tgstation.Server.Host.Security
.FirstOrDefaultAsync(cancellationToken)
: null;
- var missingClaimError = $"User missing '{TgsGroupIdClaimName}' claim!";
+ var missingClaimError = $"User missing '{groupIdClaimName}' claim!";
if (connection == default)
{
var username = principal.Identity?.Name;
@@ -311,11 +306,13 @@ namespace Tgstation.Server.Host.Security
{
tokenValidatedContext.Fail(
groupId.HasValue
- ? $"'{TgsGroupIdClaimName}' does not point to a valid group!"
+ ? $"'{groupIdClaimName}' does not point to a valid group!"
: missingClaimError);
return;
}
+ logger.LogInformation("Registering new user '{name}' via OIDC scheme '{scheme}'", username, schemeKey);
+
var tgsUser = await databaseContext
.Users
.GetTgsUser(
@@ -353,6 +350,7 @@ namespace Tgstation.Server.Host.Security
// group update
if (group == null)
{
+ logger.LogDebug("User {id} attempted to login via OIDC scheme '{scheme}' but had no group ID claim ('{groupClaimName}') and will be disabled", user.Id, schemeKey, groupIdClaimName);
user.PermissionSet = new PermissionSet
{
AdministrationRights = AdministrationRights.None,
@@ -365,6 +363,7 @@ namespace Tgstation.Server.Host.Security
return;
}
+ logger.LogDebug("User {id} mapped to group {groupId} via OIDC login on scheme '{scheme}'", user.Id, groupId, schemeKey);
user.Group = group;
if (user.PermissionSet != null)
databaseContext.PermissionSets.Remove(user.PermissionSet);
diff --git a/src/Tgstation.Server.Host/Security/ITokenValidator.cs b/src/Tgstation.Server.Host/Security/ITokenValidator.cs
index e7d17f14aa..d53acceaa4 100644
--- a/src/Tgstation.Server.Host/Security/ITokenValidator.cs
+++ b/src/Tgstation.Server.Host/Security/ITokenValidator.cs
@@ -24,8 +24,9 @@ namespace Tgstation.Server.Host.Security
///
/// The for .
/// The scheme key being used to login.
+ /// The name of the used to set the user's group ID.
/// The for the operation.
/// A representing the running operation.
- Task ValidateOidcToken(RemoteAuthenticationContext tokenValidatedContext, string schemeKey, CancellationToken cancellationToken);
+ Task ValidateOidcToken(RemoteAuthenticationContext tokenValidatedContext, string schemeKey, string groupIdClaimName, CancellationToken cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/appsettings.yml b/src/Tgstation.Server.Host/appsettings.yml
index add5c212bf..7d952c73d1 100644
--- a/src/Tgstation.Server.Host/appsettings.yml
+++ b/src/Tgstation.Server.Host/appsettings.yml
@@ -74,6 +74,16 @@ Security:
TGForums: # https://tgstation13.org OAuth configuration
Keycloak: # Keycloak OAuth configuration.
InvisionCommunity: # Invision Community OAuth configuration.
+# OpenIDConnect:
+# Example: # Scheme key: Name of this OIDC scheme (public)
+# Authority: "..." # This is the root of the URL containing the "/.well-known/openid-configuration" endpoint
+# ClientId: "..."
+# ClientSecret: "..."
+# FriendlyName: "Example Provider" # Friendly name is how this provider is displayed in UIs (public)
+# ThemeColour: "#ff0000" # (Optional) Hex color code indicating the color used to theme UI elements relating to this provider (public)
+# ThemeIconUrl: "..." # (Optional) Public URL of an image that can be used to theme UI elements relating to this provider (public)
+# UsernameClaim: "preferred_username" # (Optional) Name of claim used to set TGS usernames upon registration. By default "preferred_username" is used. Only applies when using OidcStrictMode.
+# GroupIdClaim: "tgstation-server-group-id" # (Options) Name of claim used to set TGS group ID upon logging in. By default "tgstation-server-group-id" is used. Only applies when using OidcStrictMode.
Swarm: # Should be left empty if using swarm mode is not desired
# Identifier: # Required: The string identifier of the swarm node
# PrivateKey: # Required: The shared API key used for inter-server communication in the swarm. Must be the same secure string on all nodes