🐛 Trying to fix connection issues
This commit is contained in:
parent
1a5d0bbfc0
commit
50d8f74a98
@ -2,7 +2,7 @@ using DysonNetwork.Sphere.Account;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Sphere.Auth.OpenId;
|
||||
using DysonNetwork.Sphere.Storage;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Sphere.Auth.OpenId;
|
||||
@ -14,9 +14,13 @@ public class ConnectionController(
|
||||
AppDatabase db,
|
||||
IEnumerable<OidcService> oidcServices,
|
||||
AccountService accounts,
|
||||
AuthService auth
|
||||
AuthService auth,
|
||||
ICacheService cacheService
|
||||
) : ControllerBase
|
||||
{
|
||||
private const string StateCachePrefix = "oidc-state:";
|
||||
private const string ReturnUrlCachePrefix = "oidc-returning:";
|
||||
private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15);
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<AccountConnection>>> GetConnections()
|
||||
{
|
||||
@ -142,10 +146,12 @@ public class ConnectionController(
|
||||
|
||||
var state = Guid.NewGuid().ToString("N");
|
||||
var nonce = Guid.NewGuid().ToString("N");
|
||||
HttpContext.Session.SetString($"oidc_state_{state}", $"{currentUser.Id}|{request.Provider}|{nonce}");
|
||||
|
||||
var stateValue = $"{currentUser.Id}|{request.Provider}|{nonce}";
|
||||
var finalReturnUrl = !string.IsNullOrEmpty(request.ReturnUrl) ? request.ReturnUrl : "/settings/connections";
|
||||
HttpContext.Session.SetString($"oidc_return_url_{state}", finalReturnUrl);
|
||||
|
||||
// Store state and return URL in cache
|
||||
await cacheService.SetAsync($"{StateCachePrefix}{state}", stateValue, StateExpiration);
|
||||
await cacheService.SetAsync($"{ReturnUrlCachePrefix}{state}", finalReturnUrl, StateExpiration);
|
||||
|
||||
var authUrl = oidcService.GetAuthorizationUrl(state, nonce);
|
||||
|
||||
@ -169,14 +175,22 @@ public class ConnectionController(
|
||||
if (callbackData.State == null)
|
||||
return BadRequest("State parameter is missing.");
|
||||
|
||||
var sessionState = HttpContext.Session.GetString($"oidc_state_{callbackData.State!}");
|
||||
HttpContext.Session.Remove($"oidc_state_{callbackData.State}");
|
||||
// Get and validate state from cache
|
||||
var stateKey = $"{StateCachePrefix}{callbackData.State}";
|
||||
var stateValue = await cacheService.GetAsync<string>(stateKey);
|
||||
if (string.IsNullOrEmpty(stateValue))
|
||||
{
|
||||
return BadRequest("Invalid or expired state parameter");
|
||||
}
|
||||
|
||||
// If sessionState is present, it's a manual connection flow for an existing user.
|
||||
if (sessionState == null) return await HandleLoginOrRegistration(provider, oidcService, callbackData);
|
||||
var stateParts = sessionState.Split('|');
|
||||
if (stateParts.Length != 3 || !stateParts[1].Equals(provider, StringComparison.OrdinalIgnoreCase))
|
||||
return BadRequest("State mismatch.");
|
||||
// Remove state from cache to prevent replay attacks
|
||||
await cacheService.RemoveAsync(stateKey);
|
||||
|
||||
var stateParts = stateValue.Split('|');
|
||||
if (stateParts.Length != 3)
|
||||
{
|
||||
return BadRequest("Invalid state format");
|
||||
}
|
||||
|
||||
var accountId = Guid.Parse(stateParts[0]);
|
||||
return await HandleManualConnection(provider, oidcService, callbackData, accountId);
|
||||
@ -259,9 +273,9 @@ public class ConnectionController(
|
||||
}
|
||||
|
||||
// Clean up and redirect
|
||||
var returnUrl = HttpContext.Session.GetString($"oidc_return_url_{callbackData.State}");
|
||||
HttpContext.Session.Remove($"oidc_return_url_{callbackData.State}");
|
||||
HttpContext.Session.Remove($"oidc_state_{callbackData.State}");
|
||||
var returnUrlKey = $"{ReturnUrlCachePrefix}{callbackData.State}";
|
||||
var returnUrl = await cacheService.GetAsync<string>(returnUrlKey);
|
||||
await cacheService.RemoveAsync(returnUrlKey);
|
||||
|
||||
return Redirect(string.IsNullOrEmpty(returnUrl) ? "/settings/connections" : returnUrl);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using DysonNetwork.Sphere.Account;
|
||||
using DysonNetwork.Sphere.Storage;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@ -12,12 +13,16 @@ public class OidcController(
|
||||
IServiceProvider serviceProvider,
|
||||
AppDatabase db,
|
||||
AccountService accounts,
|
||||
AuthService authService
|
||||
AuthService auth,
|
||||
ICacheService cache
|
||||
)
|
||||
: ControllerBase
|
||||
{
|
||||
private const string StateCachePrefix = "oidc-state:";
|
||||
private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15);
|
||||
|
||||
[HttpGet("{provider}")]
|
||||
public ActionResult SignIn([FromRoute] string provider, [FromQuery] string? returnUrl = "/")
|
||||
public async Task<ActionResult> SignIn([FromRoute] string provider, [FromQuery] string? returnUrl = "/")
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -29,10 +34,11 @@ public class OidcController(
|
||||
var state = Guid.NewGuid().ToString();
|
||||
var nonce = Guid.NewGuid().ToString();
|
||||
|
||||
// Store user's ID, provider, and nonce in session. The callback will use this.
|
||||
HttpContext.Session.SetString($"oidc_state_{state}", $"{currentUser.Id}|{provider}|{nonce}");
|
||||
// Store user's ID, provider, and nonce in cache. The callback will use this.
|
||||
var stateValue = $"{currentUser.Id}|{provider}|{nonce}";
|
||||
await cache.SetAsync($"{StateCachePrefix}{state}", stateValue, StateExpiration);
|
||||
|
||||
// The state parameter sent to the provider is the GUID key for the session state.
|
||||
// The state parameter sent to the provider is the GUID key for the cache.
|
||||
var authUrl = oidcService.GetAuthorizationUrl(state, nonce);
|
||||
return Redirect(authUrl);
|
||||
}
|
||||
@ -88,7 +94,7 @@ public class OidcController(
|
||||
);
|
||||
|
||||
// Generate token using existing auth service
|
||||
var token = authService.CreateToken(session);
|
||||
var token = auth.CreateToken(session);
|
||||
|
||||
return Ok(new AuthController.TokenExchangeResponse { Token = token });
|
||||
}
|
||||
@ -156,7 +162,7 @@ public class OidcController(
|
||||
Meta = userInfo.ToMetadata()
|
||||
};
|
||||
|
||||
db.AccountConnections.Add(connection);
|
||||
await db.AccountConnections.AddAsync(connection);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return existingAccount;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*! tailwindcss v4.1.7 | MIT License | https://tailwindcss.com */
|
||||
/*! tailwindcss v4.1.10 | MIT License | https://tailwindcss.com */
|
||||
@layer properties;
|
||||
@layer theme, base, components, utilities;
|
||||
@layer theme {
|
||||
@ -311,6 +311,9 @@
|
||||
.max-w-lg {
|
||||
max-width: var(--container-lg);
|
||||
}
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
||||
}
|
||||
@ -332,9 +335,23 @@
|
||||
.rounded-lg {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
.border {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.border-2 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 2px;
|
||||
}
|
||||
.border-gray-300 {
|
||||
border-color: var(--color-gray-300);
|
||||
}
|
||||
.bg-blue-500 {
|
||||
background-color: var(--color-blue-500);
|
||||
}
|
||||
.bg-gray-100 {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
.bg-green-100 {
|
||||
background-color: var(--color-green-100);
|
||||
}
|
||||
@ -430,6 +447,12 @@
|
||||
.text-yellow-800 {
|
||||
color: var(--color-yellow-800);
|
||||
}
|
||||
.lowercase {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
.opacity-80 {
|
||||
opacity: 80%;
|
||||
}
|
||||
@ -504,6 +527,11 @@
|
||||
line-height: var(--tw-leading, var(--text-6xl--line-height));
|
||||
}
|
||||
}
|
||||
.dark\:border-gray-600 {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
border-color: var(--color-gray-600);
|
||||
}
|
||||
}
|
||||
.dark\:bg-gray-800 {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--color-gray-800);
|
||||
@ -800,6 +828,11 @@
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@property --tw-leading {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
@ -953,6 +986,7 @@
|
||||
--tw-rotate-z: initial;
|
||||
--tw-skew-x: initial;
|
||||
--tw-skew-y: initial;
|
||||
--tw-border-style: solid;
|
||||
--tw-leading: initial;
|
||||
--tw-font-weight: initial;
|
||||
--tw-tracking: initial;
|
||||
|
Loading…
x
Reference in New Issue
Block a user