diff --git a/README.md b/README.md
index 26a84e81dd..841fdb4e12 100644
--- a/README.md
+++ b/README.md
@@ -143,10 +143,11 @@ The following providers use the `RedirectUrl` setting:
- GitHub
- TGForums
+- Keycloak
The following providers use the `ServerUrl` setting:
-- None so far
+- Keycloak
### Database Configuration
diff --git a/docs/API.dox b/docs/API.dox
index 5507fa7f83..927d3cd0c5 100644
--- a/docs/API.dox
+++ b/docs/API.dox
@@ -147,6 +147,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
+- Keycloak: https://plugins.miniorange.com/keycloak-single-sign-on-wordpress-sso-oauth-openid-connect
@section api_perms Permissions
diff --git a/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs
new file mode 100644
index 0000000000..61e08a56ab
--- /dev/null
+++ b/src/Tgstation.Server.Host/Security/OAuth/KeycloakOAuthValidator.cs
@@ -0,0 +1,54 @@
+using Microsoft.Extensions.Logging;
+using System;
+using System.Net.Http;
+using Tgstation.Server.Api.Models;
+using Tgstation.Server.Host.Configuration;
+using Tgstation.Server.Host.System;
+
+namespace Tgstation.Server.Host.Security.OAuth
+{
+ ///
+ /// OAuth validator for Keycloak.
+ ///
+ sealed class KeycloakOAuthValidator : GenericOAuthValidator
+ {
+ ///
+ public override OAuthProvider Provider => OAuthProvider.Keycloak;
+
+ ///
+ protected override Uri TokenUrl => new Uri($"{BaseProtocolPath}/token");
+
+ ///
+ protected override Uri UserInformationUrl => new Uri($"{BaseProtocolPath}/userinfo");
+
+ ///
+ /// Base path to the server's OAuth endpoint.
+ ///
+ string BaseProtocolPath => $"{OAuthConfiguration.ServerUrl}/protocol/openid-connect";
+
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// The for the .
+ /// The for the .
+ /// The for the .
+ /// The for the .
+ public KeycloakOAuthValidator(
+ IHttpClientFactory httpClientFactory,
+ IAssemblyInformationProvider assemblyInformationProvider,
+ ILogger logger,
+ OAuthConfiguration oAuthConfiguration)
+ : base(httpClientFactory, assemblyInformationProvider, logger, oAuthConfiguration)
+ {
+ }
+
+ ///
+ protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "openid");
+
+ ///
+ protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token;
+
+ ///
+ protected override string DecodeUserInformationPayload(dynamic responseJson) => responseJson.sub;
+ }
+}
diff --git a/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs b/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs
index 9087aad25b..e8133706b2 100644
--- a/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs
+++ b/src/Tgstation.Server.Host/Security/OAuth/OAuthProviders.cs
@@ -66,6 +66,14 @@ namespace Tgstation.Server.Host.Security.OAuth
loggerFactory.CreateLogger(),
tgConfig));
+ if (securityConfiguration.OAuth.TryGetValue(OAuthProvider.Keycloak, out var keyCloakConfig))
+ validatorsBuilder.Add(
+ new KeycloakOAuthValidator(
+ httpClientFactory,
+ assemblyInformationProvider,
+ loggerFactory.CreateLogger(),
+ keyCloakConfig));
+
validators = validatorsBuilder;
}
diff --git a/src/Tgstation.Server.Host/appsettings.json b/src/Tgstation.Server.Host/appsettings.json
index 00506b5762..b0df570838 100644
--- a/src/Tgstation.Server.Host/appsettings.json
+++ b/src/Tgstation.Server.Host/appsettings.json
@@ -58,7 +58,8 @@
"OAuth": {
"GitHub": null,
"Discord": null,
- "TGForums": null
+ "TGForums": null,
+ "Keycloak": null
}
}
}