mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-27 16:08:47 -07:00
Fix token cookie propagation
This commit is contained in:
@@ -3,6 +3,7 @@ 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 System.Text.Json;
|
||||||
using Open.IdentityServer;
|
using Open.IdentityServer;
|
||||||
using Open.IdentityServer.Models;
|
using Open.IdentityServer.Models;
|
||||||
using Open.IdentityServer.Services;
|
using Open.IdentityServer.Services;
|
||||||
@@ -273,7 +274,7 @@ internal static class RecRoomIdentityServerConfig
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class IdentityServerGameTokenRequestMiddleware(RequestDelegate next)
|
public sealed class IdentityServerGameTokenRequestMiddleware(RequestDelegate next, DomainConfig domain)
|
||||||
{
|
{
|
||||||
public async Task InvokeAsync(HttpContext context)
|
public async Task InvokeAsync(HttpContext context)
|
||||||
{
|
{
|
||||||
@@ -324,7 +325,110 @@ public sealed class IdentityServerGameTokenRequestMiddleware(RequestDelegate nex
|
|||||||
if (changed)
|
if (changed)
|
||||||
context.Features.Set<IFormFeature>(new FormFeature(new FormCollection(values)));
|
context.Features.Set<IFormFeature>(new FormFeature(new FormCollection(values)));
|
||||||
|
|
||||||
await next(context);
|
await InvokeAndPersistAccessTokenCookieAsync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InvokeAndPersistAccessTokenCookieAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var originalBody = context.Response.Body;
|
||||||
|
await using var bufferedBody = new MemoryStream();
|
||||||
|
context.Response.Body = bufferedBody;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await next(context);
|
||||||
|
|
||||||
|
bufferedBody.Position = 0;
|
||||||
|
if (IsSuccessfulJsonResponse(context.Response))
|
||||||
|
TryPersistAccessTokenCookie(context, bufferedBody);
|
||||||
|
|
||||||
|
bufferedBody.Position = 0;
|
||||||
|
await bufferedBody.CopyToAsync(originalBody);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.Response.Body = originalBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSuccessfulJsonResponse(HttpResponse response) =>
|
||||||
|
response.StatusCode is >= 200 and < 300
|
||||||
|
&& response.ContentType?.Contains("application/json", StringComparison.OrdinalIgnoreCase) == true;
|
||||||
|
|
||||||
|
private void TryPersistAccessTokenCookie(HttpContext context, Stream responseBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var document = JsonDocument.Parse(responseBody);
|
||||||
|
if (!document.RootElement.TryGetProperty("access_token", out var accessTokenElement))
|
||||||
|
return;
|
||||||
|
var accessToken = accessTokenElement.GetString();
|
||||||
|
if (string.IsNullOrWhiteSpace(accessToken))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var expires = DateTimeOffset.UtcNow.AddHours(12);
|
||||||
|
if (document.RootElement.TryGetProperty("expires_in", out var expiresInElement) &&
|
||||||
|
expiresInElement.TryGetInt32(out var expiresIn) &&
|
||||||
|
expiresIn > 0)
|
||||||
|
{
|
||||||
|
expires = DateTimeOffset.UtcNow.AddSeconds(expiresIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new CookieOptions
|
||||||
|
{
|
||||||
|
HttpOnly = true,
|
||||||
|
Secure = context.Request.IsHttps || domain.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase),
|
||||||
|
SameSite = SameSiteMode.Lax,
|
||||||
|
Path = "/",
|
||||||
|
Expires = expires,
|
||||||
|
};
|
||||||
|
|
||||||
|
var cookieDomain = ResolveCookieDomain(context.Request.Host.Host);
|
||||||
|
if (cookieDomain is not null)
|
||||||
|
options.Domain = cookieDomain;
|
||||||
|
|
||||||
|
context.Response.Cookies.Append(AuthService.AccessCookieName, accessToken, options);
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
// Leave the token response untouched if the upstream payload is not JSON.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? ResolveCookieDomain(string host)
|
||||||
|
{
|
||||||
|
var apex = domain.Apex.Trim().TrimStart('.');
|
||||||
|
if (string.IsNullOrWhiteSpace(apex) ||
|
||||||
|
apex.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
apex.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
apex.Equals("::1", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domain.UsesHyphenSubdomains && host.EndsWith("-" + apex, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var parentDomain = ResolveParentDomain(apex);
|
||||||
|
return parentDomain is null ? null : "." + parentDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.Equals(apex, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
host.EndsWith("." + apex, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "." + apex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ResolveParentDomain(string host)
|
||||||
|
{
|
||||||
|
var firstDot = host.IndexOf('.');
|
||||||
|
if (firstDot <= 0 || firstDot == host.Length - 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parent = host[(firstDot + 1)..];
|
||||||
|
return parent.Contains('.') ? parent : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsTokenRequest(HttpContext context) =>
|
private static bool IsTokenRequest(HttpContext context) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user