♻️ Extract the Storage service to DysonNetwork.Drive microservice

This commit is contained in:
2025-07-06 17:29:26 +08:00
parent 6a3d04af3d
commit 14b79f16f4
71 changed files with 2629 additions and 346 deletions

View File

@ -1,7 +1,7 @@
using System.Linq.Expressions;
using System.Reflection;
using DysonNetwork.Common.Models;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Sphere.Permission;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using NodaTime;

View File

@ -21,8 +21,8 @@
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.14.0" />
<PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.14.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
@ -44,6 +44,8 @@
<ItemGroup>
<ProjectReference Include="..\DysonNetwork.Common\DysonNetwork.Common.csproj" />
<ProjectReference Include="..\DysonNetwork.Drive\DysonNetwork.Drive.csproj" />
<ProjectReference Include="..\DysonNetwork.Sphere\DysonNetwork.Sphere.csproj" />
</ItemGroup>
</Project>

View File

@ -46,7 +46,7 @@ public class AccountCurrentController(
[HttpPatch]
public async Task<ActionResult<Account>> UpdateBasicInfo([FromBody] BasicInfoRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var account = await db.Accounts.FirstAsync(a => a.Id == currentUser.Id);
@ -77,7 +77,7 @@ public class AccountCurrentController(
[HttpPatch("profile")]
public async Task<ActionResult<Profile>> UpdateProfile([FromBody] ProfileRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var userId = currentUser.Id;
var profile = await db.AccountProfiles
@ -162,7 +162,7 @@ public class AccountCurrentController(
[HttpDelete]
public async Task<ActionResult> RequestDeleteAccount()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
try
{
@ -179,7 +179,7 @@ public class AccountCurrentController(
[HttpGet("statuses")]
public async Task<ActionResult<Status>> GetCurrentStatus()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var status = await events.GetStatus(currentUser.Id);
return Ok(status);
}
@ -188,7 +188,7 @@ public class AccountCurrentController(
[RequiredPermission("global", "accounts.statuses.update")]
public async Task<ActionResult<Status>> UpdateStatus([FromBody] AccountController.StatusRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var now = SystemClock.Instance.GetCurrentInstant();
var status = await db.AccountStatuses
@ -212,10 +212,10 @@ public class AccountCurrentController(
}
[HttpPost("statuses")]
[RequiredPermission("global", "accounts.statuses.create")]
[DysonNetwork.Sphere.Permission.RequiredPermission("global", "accounts.statuses.create")]
public async Task<ActionResult<Status>> CreateStatus([FromBody] AccountController.StatusRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var status = new Status
{
@ -233,7 +233,7 @@ public class AccountCurrentController(
[HttpDelete("me/statuses")]
public async Task<ActionResult> DeleteStatus()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var now = SystemClock.Instance.GetCurrentInstant();
var status = await db.AccountStatuses
@ -250,7 +250,7 @@ public class AccountCurrentController(
[HttpGet("check-in")]
public async Task<ActionResult<CheckInResult>> GetCheckInResult()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var userId = currentUser.Id;
var now = SystemClock.Instance.GetCurrentInstant();
@ -270,7 +270,7 @@ public class AccountCurrentController(
[HttpPost("check-in")]
public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var isAvailable = await events.CheckInDailyIsAvailable(currentUser);
if (!isAvailable)
@ -297,7 +297,7 @@ public class AccountCurrentController(
public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month,
[FromQuery] int? year)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var currentDate = SystemClock.Instance.GetCurrentInstant().InUtc().Date;
month ??= currentDate.Month;
@ -318,7 +318,7 @@ public class AccountCurrentController(
[FromQuery] int offset = 0
)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var query = db.ActionLogs
.Where(log => log.AccountId == currentUser.Id)
@ -338,7 +338,7 @@ public class AccountCurrentController(
[HttpGet("factors")]
public async Task<ActionResult<List<AccountAuthFactor>>> GetAuthFactors()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var factors = await db.AccountAuthFactors
.Include(f => f.Account)
@ -358,7 +358,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountAuthFactor>> CreateAuthFactor([FromBody] AuthFactorRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
if (await accounts.CheckAuthFactorExists(currentUser, request.Type))
return BadRequest($"Auth factor with type {request.Type} is already exists.");
@ -370,7 +370,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountAuthFactor>> EnableAuthFactor(Guid id, [FromBody] string? code)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
@ -392,7 +392,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountAuthFactor>> DisableAuthFactor(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
@ -414,7 +414,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountAuthFactor>> DeleteAuthFactor(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var factor = await db.AccountAuthFactors
.Where(f => f.AccountId == currentUser.Id && f.Id == id)
@ -445,7 +445,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<List<AuthorizedDevice>>> GetDevices()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser ||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
Response.Headers.Append("X-Auth-Session", currentSession.Id.ToString());
@ -475,13 +475,13 @@ public class AccountCurrentController(
[HttpGet("sessions")]
[Authorize]
public async Task<ActionResult<List<Session>>> GetSessions(
public async Task<ActionResult<List<AuthSession>>> GetSessions(
[FromQuery] int take = 20,
[FromQuery] int offset = 0
)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser ||
HttpContext.Items["CurrentSession"] is not AuthSession currentSession) return Unauthorized();
var query = db.AuthSessions
.Include(session => session.Account)
@ -505,7 +505,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<Session>> DeleteSession(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
try
{
@ -522,7 +522,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<Session>> DeleteCurrentSession()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser ||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
try
@ -537,9 +537,9 @@ public class AccountCurrentController(
}
[HttpPatch("sessions/{id:guid}/label")]
public async Task<ActionResult<Session>> UpdateSessionLabel(Guid id, [FromBody] string label)
public async Task<ActionResult<AuthSession>> UpdateSessionLabel(Guid id, [FromBody] string label)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
try
{
@ -553,9 +553,9 @@ public class AccountCurrentController(
}
[HttpPatch("sessions/current/label")]
public async Task<ActionResult<Session>> UpdateCurrentSessionLabel([FromBody] string label)
public async Task<ActionResult<AuthSession>> UpdateCurrentSessionLabel([FromBody] string label)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser ||
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser ||
HttpContext.Items["CurrentSession"] is not Session currentSession) return Unauthorized();
try
@ -573,7 +573,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<List<AccountContact>>> GetContacts()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var contacts = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id)
@ -592,7 +592,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountContact>> CreateContact([FromBody] AccountContactRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
try
{
@ -609,7 +609,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountContact>> VerifyContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
@ -631,7 +631,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountContact>> SetPrimaryContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
@ -653,7 +653,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<AccountContact>> DeleteContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
@ -676,7 +676,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<List<Badge>>> GetBadges()
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
var badges = await db.Badges
.Where(b => b.AccountId == currentUser.Id)
@ -688,7 +688,7 @@ public class AccountCurrentController(
[Authorize]
public async Task<ActionResult<Badge>> ActivateBadge(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
if (HttpContext.Items["CurrentUser"] is not Common.Models.Account currentUser) return Unauthorized();
try
{

View File

@ -140,7 +140,7 @@ public class NotificationController(PassDatabase db, NotificationService nty) :
[HttpPost("send")]
[Authorize]
[RequiredPermission("global", "notifications.send")]
[DysonNetwork.Sphere.Permission.RequiredPermission("global", "notifications.send")]
public async Task<ActionResult> SendNotification(
[FromBody] NotificationWithAimRequest request,
[FromQuery] bool save = false

View File

@ -3,7 +3,7 @@ using DysonNetwork.Pass.Features.Auth;
using DysonNetwork.Pass.Features.Auth.OpenId;
using DysonNetwork.Pass.Email;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Sphere.Permission;
using DysonNetwork.Pass.Storage;
using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore;

View File

@ -3,7 +3,7 @@ using DysonNetwork.Pass.Storage;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using DysonNetwork.Common.Models;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Sphere.Permission;
namespace DysonNetwork.Pass.Features.Account;

View File

@ -218,7 +218,7 @@ public class AuthController(
if (session is not null)
return BadRequest("Session already exists for this challenge.");
session = new Session
var session = new AuthSession
{
LastGrantedAt = Instant.FromDateTimeUtc(DateTime.UtcNow),
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddDays(30)),

View File

@ -3,11 +3,14 @@ using System.Security.Cryptography;
using System.Text.Encodings.Web;
using DysonNetwork.Pass.Features.Account;
using DysonNetwork.Pass.Features.Auth.OidcProvider.Services;
using DysonNetwork.Pass.Storage;
using DysonNetwork.Pass.Storage.Handlers;
using DysonNetwork.Common.Services;
using DysonNetwork.Drive.Handlers;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using SystemClock = NodaTime.SystemClock;
using NodaTime;
using DysonNetwork.Pass.Data;
using DysonNetwork.Common.Models;
using DysonNetwork.Drive;
namespace DysonNetwork.Pass.Features.Auth.Services;
@ -57,14 +60,14 @@ public class DysonTokenAuthHandler(
try
{
var now = SystemClock.Instance.GetCurrentInstant();
var now = NodaTime.SystemClock.Instance.GetCurrentInstant();
// Validate token and extract session ID
if (!ValidateToken(tokenInfo.Token, out var sessionId))
return AuthenticateResult.Fail("Invalid token.");
// Try to get session from cache first
var session = await cache.GetAsync<Session>($"{AuthCachePrefix}{sessionId}");
var session = await cache.GetAsync<AuthSession>($"{AuthCachePrefix}{sessionId}");
// If not in cache, load from database
if (session is null)
@ -126,7 +129,7 @@ public class DysonTokenAuthHandler(
{
Account = session.Account,
Session = session,
SeenAt = SystemClock.Instance.GetCurrentInstant(),
SeenAt = NodaTime.SystemClock.Instance.GetCurrentInstant(),
};
fbs.Enqueue(lastInfo);

View File

@ -5,6 +5,7 @@ using DysonNetwork.Pass.Storage;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using DysonNetwork.Pass.Data;
using DysonNetwork.Common.Models;
namespace DysonNetwork.Pass.Features.Auth;

View File

@ -1,4 +1,5 @@
using System.Security.Cryptography;
using DysonNetwork.Common.Models;
namespace DysonNetwork.Pass.Features.Auth;
@ -7,7 +8,7 @@ public class CompactTokenService(IConfiguration config)
private readonly string _privateKeyPath = config["AuthToken:PrivateKeyPath"]
?? throw new InvalidOperationException("AuthToken:PrivateKeyPath configuration is missing");
public string CreateToken(Session session)
public string CreateToken(AuthSession session)
{
// Load the private key for signing
var privateKeyPem = File.ReadAllText(_privateKeyPath);

View File

@ -5,6 +5,8 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Common.Services;
using Microsoft.IdentityModel.Tokens;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
@ -14,11 +16,12 @@ namespace DysonNetwork.Pass.Features.Auth.OpenId;
public class AppleOidcService(
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AuthService auth,
ICacheService cache
)
: OidcService(configuration, httpClientFactory, db, auth, cache)
: OidcService(configuration, httpClientFactory, passDb, sphereDb, auth, cache)
{
private readonly IConfiguration _configuration = configuration;
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;

View File

@ -1,17 +1,20 @@
using System.Net.Http.Json;
using System.Text.Json;
using DysonNetwork.Pass.Storage;
using DysonNetwork.Common.Services;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
public class DiscordOidcService(
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AuthService auth,
ICacheService cache
)
: OidcService(configuration, httpClientFactory, db, auth, cache)
: OidcService(configuration, httpClientFactory, passDb, sphereDb, auth, cache)
{
public override string ProviderName => "Discord";
protected override string DiscoveryEndpoint => ""; // Discord doesn't have a standard OIDC discovery endpoint

View File

@ -1,17 +1,20 @@
using System.Net.Http.Json;
using System.Text.Json;
using DysonNetwork.Pass.Storage;
using DysonNetwork.Common.Services;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
public class GitHubOidcService(
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AuthService auth,
ICacheService cache
)
: OidcService(configuration, httpClientFactory, db, auth, cache)
: OidcService(configuration, httpClientFactory, passDb, sphereDb, auth, cache)
{
public override string ProviderName => "GitHub";
protected override string DiscoveryEndpoint => ""; // GitHub doesn't have a standard OIDC discovery endpoint
@ -77,7 +80,7 @@ public class GitHubOidcService(
var client = HttpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/user");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("User-Agent", "DysonNetwork.Sphere");
request.Headers.Add("User-Agent", "DysonNetwork.Drive");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
@ -109,7 +112,7 @@ public class GitHubOidcService(
var client = HttpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/user/emails");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("User-Agent", "DysonNetwork.Sphere");
request.Headers.Add("User-Agent", "DysonNetwork.Drive");
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) return null;

View File

@ -4,17 +4,20 @@ using System.Security.Cryptography;
using System.Text;
using DysonNetwork.Common.Services;
using Microsoft.IdentityModel.Tokens;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
public class GoogleOidcService(
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AuthService auth,
ICacheService cache
)
: OidcService(configuration, httpClientFactory, db, auth, cache)
: OidcService(configuration, httpClientFactory, passDb, sphereDb, auth, cache)
{
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;

View File

@ -1,17 +1,20 @@
using System.Net.Http.Json;
using System.Text.Json;
using DysonNetwork.Pass.Storage;
using DysonNetwork.Common.Services;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
public class MicrosoftOidcService(
IConfiguration configuration,
IHttpClientFactory httpClientFactory,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AuthService auth,
ICacheService cache
)
: OidcService(configuration, httpClientFactory, db, auth, cache)
: OidcService(configuration, httpClientFactory, passDb, sphereDb, auth, cache)
{
public override string ProviderName => "Microsoft";

View File

@ -1,8 +1,10 @@
using DysonNetwork.Common.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using NodaTime;
using DysonNetwork.Common.Models;
using DysonNetwork.Pass.Data;
using DysonNetwork.Sphere;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
@ -10,7 +12,8 @@ namespace DysonNetwork.Pass.Features.Auth.OpenId;
[Route("/auth/login")]
public class OidcController(
IServiceProvider serviceProvider,
PassDatabase db,
PassDatabase passDb,
AppDatabase sphereDb,
AccountService accounts,
ICacheService cache
)
@ -31,7 +34,7 @@ public class OidcController(
var oidcService = GetOidcService(provider);
// If the user is already authenticated, treat as an account connection request
if (HttpContext.Items["CurrentUser"] is Models.Account currentUser)
if (HttpContext.Items["CurrentUser"] is Account currentUser)
{
var state = Guid.NewGuid().ToString();
var nonce = Guid.NewGuid().ToString();
@ -67,7 +70,7 @@ public class OidcController(
/// Handles Apple authentication directly from mobile apps
/// </summary>
[HttpPost("apple/mobile")]
public async Task<ActionResult<Challenge>> AppleMobileLogin(
public async Task<ActionResult<AuthChallenge>> AppleMobileSignIn(
[FromBody] AppleMobileSignInRequest request)
{
try
@ -124,7 +127,7 @@ public class OidcController(
};
}
private async Task<Models.Account> FindOrCreateAccount(OidcUserInfo userInfo, string provider)
private async Task<Account> FindOrCreateAccount(OidcUserInfo userInfo, string provider)
{
if (string.IsNullOrEmpty(userInfo.Email))
throw new ArgumentException("Email is required for account creation");
@ -134,15 +137,16 @@ public class OidcController(
if (existingAccount != null)
{
// Check if this provider connection already exists
var existingConnection = await db.AccountConnections
.FirstOrDefaultAsync(c => c.AccountId == existingAccount.Id &&
c.Provider == provider &&
c.ProvidedIdentifier == userInfo.UserId);
var existingConnection = await passDb.AccountConnections
.FirstOrDefaultAsync(c => c.Provider == provider &&
c.ProvidedIdentifier == userInfo.UserId &&
c.AccountId == existingAccount.Id
);
// If no connection exists, create one
if (existingConnection != null)
{
await db.AccountConnections
await passDb.AccountConnections
.Where(c => c.AccountId == existingAccount.Id &&
c.Provider == provider &&
c.ProvidedIdentifier == userInfo.UserId)
@ -164,8 +168,8 @@ public class OidcController(
Meta = userInfo.ToMetadata()
};
await db.AccountConnections.AddAsync(connection);
await db.SaveChangesAsync();
await passDb.AccountConnections.AddAsync(connection);
await passDb.SaveChangesAsync();
return existingAccount;
}
@ -185,8 +189,8 @@ public class OidcController(
Meta = userInfo.ToMetadata()
};
db.AccountConnections.Add(newConnection);
await db.SaveChangesAsync();
await passDb.AccountConnections.Add(newConnection);
await passDb.SaveChangesAsync();
return newAccount;
}

View File

@ -5,6 +5,8 @@ using DysonNetwork.Common.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using NodaTime;
using DysonNetwork.Common.Models;
using DysonNetwork.Pass.Data;
namespace DysonNetwork.Pass.Features.Auth.OpenId;
@ -21,7 +23,7 @@ public abstract class OidcService(
{
protected readonly IConfiguration Configuration = configuration;
protected readonly IHttpClientFactory HttpClientFactory = httpClientFactory;
protected readonly AppDatabase Db = db;
protected readonly PassDatabase Db = db;
/// <summary>
/// Gets the unique identifier for this provider

View File

@ -14,7 +14,7 @@ using NodaTime.Serialization.SystemTextJson;
using System.Text;
using DysonNetwork.Pass.Email;
using DysonNetwork.Pass.Developer;
using DysonNetwork.Pass.Features.Account.DysonNetwork.Pass.Features.Account;
using DysonNetwork.Pass.Features.Account;
using DysonNetwork.Pass.Features.Account.Services;
using DysonNetwork.Pass.Permission;
using Quartz;