mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Migrate auth to Open.IdentityServer
This commit is contained in:
+4
-4
@@ -1,14 +1,14 @@
|
|||||||
# DorkNet.Server container image — Coolify deploy target.
|
# DorkNet.Server container image — Coolify deploy target.
|
||||||
# ────────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────────
|
||||||
# Multi-stage: SDK image builds + publishes, runtime image is the
|
# Multi-stage: SDK image builds + publishes, runtime image is the
|
||||||
# slimmer aspnet:9.0 with only the published output.
|
# slimmer aspnet:10.0 with only the published output.
|
||||||
#
|
#
|
||||||
# Build context: repo root (so Tools/ and DorkNet.Models/ are visible
|
# Build context: repo root (so Tools/ and DorkNet.Models/ are visible
|
||||||
# to ProjectReference + dotnet restore). The .dockerignore at the
|
# to ProjectReference + dotnet restore). The .dockerignore at the
|
||||||
# repo root keeps the bin/, obj/, and other dev junk out of the
|
# repo root keeps the bin/, obj/, and other dev junk out of the
|
||||||
# context.
|
# context.
|
||||||
|
|
||||||
ARG DOTNET_VERSION=9.0
|
ARG DOTNET_VERSION=10.0
|
||||||
ARG NODE_VERSION=20
|
ARG NODE_VERSION=20
|
||||||
|
|
||||||
# ── admin SPA build stage ──────────────────────────────────────────
|
# ── admin SPA build stage ──────────────────────────────────────────
|
||||||
@@ -76,13 +76,13 @@ WORKDIR /app
|
|||||||
|
|
||||||
# curl + wget for healthcheck probes — Coolify v4 defaults to wget for
|
# curl + wget for healthcheck probes — Coolify v4 defaults to wget for
|
||||||
# its own probe regardless of what the image's HEALTHCHECK uses, so
|
# its own probe regardless of what the image's HEALTHCHECK uses, so
|
||||||
# both have to be present. The aspnet:9.0 base image strips both by
|
# both have to be present. The aspnet:10.0 base image strips both by
|
||||||
# default to keep the surface area small.
|
# default to keep the surface area small.
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends curl wget \
|
&& apt-get install -y --no-install-recommends curl wget \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Run as a non-root user — aspnet:9.0 ships with the `app` UID 1654.
|
# Run as a non-root user — aspnet:10.0 ships with the `app` UID 1654.
|
||||||
# Coolify mounts persistent volumes with write permission for this UID.
|
# Coolify mounts persistent volumes with write permission for this UID.
|
||||||
USER app
|
USER app
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ using System.Security.Claims;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Duende.IdentityServer;
|
using Open.IdentityServer;
|
||||||
using Duende.IdentityServer.Models;
|
using Open.IdentityServer.Models;
|
||||||
using Duende.IdentityServer.Services;
|
using Open.IdentityServer.Services;
|
||||||
using Duende.IdentityServer.Validation;
|
using Open.IdentityServer.Validation;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
@@ -206,12 +206,6 @@ public static class RecRoomIdentityServerRegistration
|
|||||||
.AddIdentityServer(options =>
|
.AddIdentityServer(options =>
|
||||||
{
|
{
|
||||||
options.IssuerUri = domain.AuthIssuer.TrimEnd('/');
|
options.IssuerUri = domain.AuthIssuer.TrimEnd('/');
|
||||||
options.KeyManagement.Enabled = false;
|
|
||||||
var licenseKey =
|
|
||||||
Environment.GetEnvironmentVariable("DUENDE_IDENTITYSERVER_LICENSE_KEY")
|
|
||||||
?? config["IdentityServer:LicenseKey"];
|
|
||||||
if (!string.IsNullOrWhiteSpace(licenseKey))
|
|
||||||
options.LicenseKey = licenseKey;
|
|
||||||
})
|
})
|
||||||
.AddInMemoryIdentityResources(RecRoomIdentityServerConfig.IdentityResources)
|
.AddInMemoryIdentityResources(RecRoomIdentityServerConfig.IdentityResources)
|
||||||
.AddInMemoryApiScopes(RecRoomIdentityServerConfig.ApiScopes)
|
.AddInMemoryApiScopes(RecRoomIdentityServerConfig.ApiScopes)
|
||||||
@@ -256,7 +250,7 @@ internal static class RecRoomIdentityServerConfig
|
|||||||
ClientName = "Rec Room game client",
|
ClientName = "Rec Room game client",
|
||||||
RequireClientSecret = true,
|
RequireClientSecret = true,
|
||||||
ClientSecrets = { new Secret(ClientSecret.Sha256()) },
|
ClientSecrets = { new Secret(ClientSecret.Sha256()) },
|
||||||
AllowedGrantTypes = { GrantType.ResourceOwnerPassword, GrantType.RefreshToken, CachedLoginGrantType },
|
AllowedGrantTypes = { GrantType.ResourceOwnerPassword, "refresh_token", CachedLoginGrantType },
|
||||||
AllowedScopes = RecRoomScopes(),
|
AllowedScopes = RecRoomScopes(),
|
||||||
AllowOfflineAccess = true,
|
AllowOfflineAccess = true,
|
||||||
AccessTokenLifetime = 60 * 60 * 12,
|
AccessTokenLifetime = 60 * 60 * 12,
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public class AdminController(
|
|||||||
SignupCodeService signupCodes,
|
SignupCodeService signupCodes,
|
||||||
IObjectStorage storage,
|
IObjectStorage storage,
|
||||||
DomainConfig domain,
|
DomainConfig domain,
|
||||||
IConfiguration config,
|
|
||||||
IdentityServerSigningKeyProvider signingKeys,
|
IdentityServerSigningKeyProvider signingKeys,
|
||||||
ILogger<AdminController> adminLogger) : ControllerBase
|
ILogger<AdminController> adminLogger) : ControllerBase
|
||||||
{
|
{
|
||||||
@@ -1170,9 +1169,6 @@ public class AdminController(
|
|||||||
var certPath = signingKeys.CertificatePath;
|
var certPath = signingKeys.CertificatePath;
|
||||||
var fullCertPath = Path.GetFullPath(certPath);
|
var fullCertPath = Path.GetFullPath(certPath);
|
||||||
var certificateExists = signingKeys.LoadedFromBase64 || System.IO.File.Exists(fullCertPath);
|
var certificateExists = signingKeys.LoadedFromBase64 || System.IO.File.Exists(fullCertPath);
|
||||||
var licenseConfigured =
|
|
||||||
!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("DUENDE_IDENTITYSERVER_LICENSE_KEY"))
|
|
||||||
|| !string.IsNullOrWhiteSpace(config["IdentityServer:LicenseKey"]);
|
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
@@ -1204,10 +1200,6 @@ public class AdminController(
|
|||||||
NotAfterUtc = signingKeys.Certificate.NotAfter.ToUniversalTime(),
|
NotAfterUtc = signingKeys.Certificate.NotAfter.ToUniversalTime(),
|
||||||
Algorithm = SecurityAlgorithms.RsaSha256,
|
Algorithm = SecurityAlgorithms.RsaSha256,
|
||||||
},
|
},
|
||||||
License = new
|
|
||||||
{
|
|
||||||
Configured = licenseConfigured,
|
|
||||||
},
|
|
||||||
PersistedGrants = new
|
PersistedGrants = new
|
||||||
{
|
{
|
||||||
Store = "in-memory",
|
Store = "in-memory",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using DorkNet.Server.Services;
|
|||||||
namespace DorkNet.Server.Controllers.Auth;
|
namespace DorkNet.Server.Controllers.Auth;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// auth.rec.net game-specific auth helpers. OAuth/OIDC endpoints are owned by Duende IdentityServer.
|
/// auth.rec.net game-specific auth helpers. OAuth/OIDC endpoints are owned by IdentityServer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class AuthController(
|
public class AuthController(
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Duende.IdentityServer" Version="7.4.7" />
|
<PackageReference Include="Open.IdentityServer" Version="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
|
<PackageReference Include="MessagePack" Version="3.1.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.15" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||||
<!-- RenderedCompactJsonFormatter for stdout JSON logs in production
|
<!-- RenderedCompactJsonFormatter for stdout JSON logs in production
|
||||||
(Coolify-friendly). The host pipeline reads stdout line-by-line,
|
(Coolify-friendly). The host pipeline reads stdout line-by-line,
|
||||||
and structured JSON lets a future Loki/Datadog ingest parse fields
|
and structured JSON lets a future Loki/Datadog ingest parse fields
|
||||||
@@ -26,8 +27,8 @@
|
|||||||
scale: SignalR group fanout, OrphanAccountTracker (sticky-session
|
scale: SignalR group fanout, OrphanAccountTracker (sticky-session
|
||||||
account-creation flow), and PlayerPresenceService heartbeats all
|
account-creation flow), and PlayerPresenceService heartbeats all
|
||||||
need shared state across replicas. -->
|
need shared state across replicas. -->
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
<PackageReference Include="StackExchange.Redis" Version="2.13.17" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.4" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="10.0.9" />
|
||||||
<!-- S3-compatible object storage. Production target: Garage in
|
<!-- S3-compatible object storage. Production target: Garage in
|
||||||
Coolify (one-click). Dev fallback: local MinIO for parity
|
Coolify (one-click). Dev fallback: local MinIO for parity
|
||||||
testing. The SDK speaks plain S3, so swapping endpoints
|
testing. The SDK speaks plain S3, so swapping endpoints
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
cost+salt in the output string so we only need one column. -->
|
cost+salt in the output string so we only need one column. -->
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
|
||||||
<!-- Server-side crop/resize for img.* CDN transforms (?cropSquare=1&width=N
|
<!-- Server-side crop/resize for img.* CDN transforms (?cropSquare=1&width=N
|
||||||
on profile thumbnails). The 2020 client treats this as part of the
|
on profile thumbnails). The 2020 client treats this as part of the
|
||||||
image CDN contract — it uploads a full-frame capture and expects the
|
image CDN contract — it uploads a full-frame capture and expects the
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ interface IdentityServerSettings {
|
|||||||
notAfterUtc: string;
|
notAfterUtc: string;
|
||||||
algorithm: string;
|
algorithm: string;
|
||||||
};
|
};
|
||||||
license: {
|
|
||||||
configured: boolean;
|
|
||||||
};
|
|
||||||
persistedGrants: {
|
persistedGrants: {
|
||||||
store: string;
|
store: string;
|
||||||
warning: string;
|
warning: string;
|
||||||
@@ -86,7 +83,6 @@ export function IdentityServer() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2 text-xs">
|
<div className="flex flex-wrap gap-2 text-xs">
|
||||||
<StatusBadge ok={settings.signing.certificateExists} on="Signing key" off="Missing key" />
|
<StatusBadge ok={settings.signing.certificateExists} on="Signing key" off="Missing key" />
|
||||||
<StatusBadge ok={settings.license.configured} on="License set" off="No license key" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ discovery and JWT validation agree on `https://auth.<domain>`.
|
|||||||
dotnet run --project DorkNet.Server
|
dotnet run --project DorkNet.Server
|
||||||
```
|
```
|
||||||
|
|
||||||
First boot creates `bin/Debug/net9.0/data/dorknet.db`, applies all EF
|
First boot creates `bin/Debug/net10.0/data/dorknet.db`, applies all EF
|
||||||
migrations under `DorkNet.Server/Migrations/`, seeds canonical rooms +
|
migrations under `DorkNet.Server/Migrations/`, seeds canonical rooms +
|
||||||
store catalog, and starts listening. You should see `Application
|
store catalog, and starts listening. You should see `Application
|
||||||
started.` in the log; `curl http://localhost:8080/healthz` returns 200.
|
started.` in the log; `curl http://localhost:8080/healthz` returns 200.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user