♻️ Finish centerlizing the data models
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -14,7 +15,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetKeys([FromQuery] int offset = 0, [FromQuery] int take = 20)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var query = db.ApiKeys
|
||||
.Where(e => e.AccountId == currentUser.Id)
|
||||
@@ -34,7 +35,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetKey(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var key = await db.ApiKeys
|
||||
.Where(e => e.AccountId == currentUser.Id)
|
||||
@@ -56,7 +57,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Label))
|
||||
return BadRequest("Label is required");
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var key = await auth.CreateApiKey(currentUser.Id, request.Label, request.ExpiredAt);
|
||||
key.Key = await auth.IssueApiKeyToken(key);
|
||||
@@ -67,7 +68,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
public async Task<IActionResult> RotateKey(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var key = await auth.GetApiKey(id, currentUser.Id);
|
||||
if(key is null) return NotFound();
|
||||
@@ -80,7 +81,7 @@ public class ApiKeyController(AppDatabase db, AuthService auth) : ControllerBase
|
||||
[Authorize]
|
||||
public async Task<IActionResult> DeleteKey(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||
|
||||
var key = await auth.GetApiKey(id, currentUser.Id);
|
||||
if(key is null) return NotFound();
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth;
|
||||
|
||||
public static class AuthCacheConstants
|
||||
|
@@ -2,13 +2,10 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NodaTime;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Localization;
|
||||
using DysonNetwork.Shared.Data;
|
||||
using DysonNetwork.Shared.GeoIp;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using AccountAuthFactor = DysonNetwork.Pass.Account.AccountAuthFactor;
|
||||
using AccountService = DysonNetwork.Pass.Account.AccountService;
|
||||
using ActionLogService = DysonNetwork.Pass.Account.ActionLogService;
|
||||
using DysonNetwork.Shared.Models;
|
||||
@@ -41,7 +38,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[HttpPost("challenge")]
|
||||
public async Task<ActionResult<AuthChallenge>> CreateChallenge([FromBody] ChallengeRequest request)
|
||||
public async Task<ActionResult<SnAuthChallenge>> CreateChallenge([FromBody] ChallengeRequest request)
|
||||
{
|
||||
var account = await accounts.LookupAccount(request.Account);
|
||||
if (account is null) return NotFound("Account was not found.");
|
||||
@@ -73,7 +70,7 @@ public class AuthController(
|
||||
.Where(e => e.UserAgent == userAgent)
|
||||
.Where(e => e.StepRemain > 0)
|
||||
.Where(e => e.ExpiredAt != null && now < e.ExpiredAt)
|
||||
.Where(e => e.Type == ChallengeType.Login)
|
||||
.Where(e => e.Type == Shared.Models.ChallengeType.Login)
|
||||
.Where(e => e.ClientId == device.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
if (existingChallenge is not null)
|
||||
@@ -83,7 +80,7 @@ public class AuthController(
|
||||
if (existingSession is null) return existingChallenge;
|
||||
}
|
||||
|
||||
var challenge = new AuthChallenge
|
||||
var challenge = new SnAuthChallenge
|
||||
{
|
||||
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddHours(1)),
|
||||
StepTotal = await auth.DetectChallengeRisk(Request, account),
|
||||
@@ -107,7 +104,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[HttpGet("challenge/{id:guid}")]
|
||||
public async Task<ActionResult<AuthChallenge>> GetChallenge([FromRoute] Guid id)
|
||||
public async Task<ActionResult<SnAuthChallenge>> GetChallenge([FromRoute] Guid id)
|
||||
{
|
||||
var challenge = await db.AuthChallenges
|
||||
.Include(e => e.Account)
|
||||
@@ -120,7 +117,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[HttpGet("challenge/{id:guid}/factors")]
|
||||
public async Task<ActionResult<List<AccountAuthFactor>>> GetChallengeFactors([FromRoute] Guid id)
|
||||
public async Task<ActionResult<List<SnAccountAuthFactor>>> GetChallengeFactors([FromRoute] Guid id)
|
||||
{
|
||||
var challenge = await db.AuthChallenges
|
||||
.Include(e => e.Account)
|
||||
@@ -166,7 +163,7 @@ public class AuthController(
|
||||
}
|
||||
|
||||
[HttpPatch("challenge/{id:guid}")]
|
||||
public async Task<ActionResult<AuthChallenge>> DoChallenge(
|
||||
public async Task<ActionResult<SnAuthChallenge>> DoChallenge(
|
||||
[FromRoute] Guid id,
|
||||
[FromBody] PerformChallengeRequest request
|
||||
)
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -14,8 +13,7 @@ public class AuthService(
|
||||
IConfiguration config,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ICacheService cache,
|
||||
ILogger<AuthService> logger
|
||||
ICacheService cache
|
||||
)
|
||||
{
|
||||
private HttpContext HttpContext => httpContextAccessor.HttpContext!;
|
||||
@@ -28,7 +26,7 @@ public class AuthService(
|
||||
/// <param name="request">The request context</param>
|
||||
/// <param name="account">The account to login</param>
|
||||
/// <returns>The required steps to login</returns>
|
||||
public async Task<int> DetectChallengeRisk(HttpRequest request, Account.Account account)
|
||||
public async Task<int> DetectChallengeRisk(HttpRequest request, SnAccount account)
|
||||
{
|
||||
// 1) Find out how many authentication factors the account has enabled.
|
||||
var maxSteps = await db.AccountAuthFactors
|
||||
@@ -77,7 +75,7 @@ public class AuthService(
|
||||
return totalRequiredSteps;
|
||||
}
|
||||
|
||||
public async Task<SnAuthSession> CreateSessionForOidcAsync(Account.Account account, Instant time,
|
||||
public async Task<SnAuthSession> CreateSessionForOidcAsync(SnAccount account, Instant time,
|
||||
Guid? customAppId = null)
|
||||
{
|
||||
var challenge = new SnAuthChallenge
|
||||
|
@@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Web;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Pass.Auth.OidcProvider.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@@ -131,7 +130,7 @@ public class OidcProviderController(
|
||||
[FromForm(Name = "code_challenge_method")]
|
||||
string? codeChallengeMethod = null)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account account)
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount account)
|
||||
return Unauthorized();
|
||||
|
||||
// Find the client
|
||||
@@ -303,7 +302,7 @@ public class OidcProviderController(
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetUserInfo()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser ||
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser ||
|
||||
HttpContext.Items["CurrentSession"] is not SnAuthSession currentSession) return Unauthorized();
|
||||
|
||||
// Get requested scopes from the token
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NodaTime;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Models;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
|
||||
|
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NodaTime;
|
||||
using AccountContactType = DysonNetwork.Pass.Account.AccountContactType;
|
||||
using AccountContactType = DysonNetwork.Shared.Models.AccountContactType;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OidcProvider.Services;
|
||||
|
||||
@@ -39,7 +39,7 @@ public class OidcProviderService(
|
||||
return resp.App ?? null;
|
||||
}
|
||||
|
||||
public async Task<AuthSession?> FindValidSessionAsync(Guid accountId, Guid clientId, bool withAccount = false)
|
||||
public async Task<SnAuthSession?> FindValidSessionAsync(Guid accountId, Guid clientId, bool withAccount = false)
|
||||
{
|
||||
var now = SystemClock.Instance.GetCurrentInstant();
|
||||
|
||||
@@ -58,7 +58,7 @@ public class OidcProviderService(
|
||||
s.AppId == clientId &&
|
||||
(s.ExpiredAt == null || s.ExpiredAt > now) &&
|
||||
s.Challenge != null &&
|
||||
s.Challenge.Type == ChallengeType.OAuth)
|
||||
s.Challenge.Type == Shared.Models.ChallengeType.OAuth)
|
||||
.OrderByDescending(s => s.CreatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class OidcProviderService(
|
||||
|
||||
|
||||
var client = await FindClientByIdAsync(clientId);
|
||||
if (client?.Status != CustomAppStatus.Production)
|
||||
if (client?.Status != Shared.Proto.CustomAppStatus.Production)
|
||||
return true;
|
||||
|
||||
if (client?.OauthConfig?.RedirectUris == null)
|
||||
@@ -146,7 +146,7 @@ public class OidcProviderService(
|
||||
|
||||
private string GenerateIdToken(
|
||||
CustomApp client,
|
||||
AuthSession session,
|
||||
SnAuthSession session,
|
||||
string? nonce = null,
|
||||
IEnumerable<string>? scopes = null
|
||||
)
|
||||
@@ -225,11 +225,9 @@ public class OidcProviderService(
|
||||
Guid? sessionId = null
|
||||
)
|
||||
{
|
||||
var client = await FindClientByIdAsync(clientId);
|
||||
if (client == null)
|
||||
throw new InvalidOperationException("Client not found");
|
||||
var client = await FindClientByIdAsync(clientId) ?? throw new InvalidOperationException("Client not found");
|
||||
|
||||
AuthSession session;
|
||||
SnAuthSession session;
|
||||
var clock = SystemClock.Instance;
|
||||
var now = clock.GetCurrentInstant();
|
||||
string? nonce = null;
|
||||
@@ -300,7 +298,7 @@ public class OidcProviderService(
|
||||
|
||||
private string GenerateJwtToken(
|
||||
CustomApp client,
|
||||
AuthSession session,
|
||||
SnAuthSession session,
|
||||
Instant expiresAt,
|
||||
IEnumerable<string>? scopes = null
|
||||
)
|
||||
@@ -372,7 +370,7 @@ public class OidcProviderService(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<AuthSession?> FindSessionByIdAsync(Guid sessionId)
|
||||
public async Task<SnAuthSession?> FindSessionByIdAsync(Guid sessionId)
|
||||
{
|
||||
return await db.AuthSessions
|
||||
.Include(s => s.Account)
|
||||
@@ -380,7 +378,7 @@ public class OidcProviderService(
|
||||
.FirstOrDefaultAsync(s => s.Id == sessionId);
|
||||
}
|
||||
|
||||
private static string GenerateRefreshToken(AuthSession session)
|
||||
private static string GenerateRefreshToken(SnAuthSession session)
|
||||
{
|
||||
return Convert.ToBase64String(session.Id.ToByteArray());
|
||||
}
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,6 +1,5 @@
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
||||
|
@@ -3,7 +3,6 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using NodaTime;
|
||||
using DysonNetwork.Shared.Models;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
||||
@@ -23,9 +24,9 @@ public class ConnectionController(
|
||||
private static readonly TimeSpan StateExpiration = TimeSpan.FromMinutes(15);
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<AccountConnection>>> GetConnections()
|
||||
public async Task<ActionResult<List<SnAccountConnection>>> GetConnections()
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var connections = await db.AccountConnections
|
||||
@@ -48,7 +49,7 @@ public class ConnectionController(
|
||||
[HttpDelete("{id:guid}")]
|
||||
public async Task<ActionResult> RemoveConnection(Guid id)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var connection = await db.AccountConnections
|
||||
@@ -66,7 +67,7 @@ public class ConnectionController(
|
||||
[HttpPost("/api/auth/connect/apple/mobile")]
|
||||
public async Task<ActionResult> ConnectAppleMobile([FromBody] AppleMobileConnectRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser)
|
||||
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
if (GetOidcService("apple") is not AppleOidcService appleService)
|
||||
@@ -99,7 +100,7 @@ public class ConnectionController(
|
||||
$"This Apple account is already linked to {(existingConnection.AccountId == currentUser.Id ? "your account" : "another user")}.");
|
||||
}
|
||||
|
||||
db.AccountConnections.Add(new AccountConnection
|
||||
db.AccountConnections.Add(new SnAccountConnection
|
||||
{
|
||||
AccountId = currentUser.Id,
|
||||
Provider = "apple",
|
||||
@@ -250,7 +251,7 @@ public class ConnectionController(
|
||||
else
|
||||
{
|
||||
// Create new connection
|
||||
db.AccountConnections.Add(new AccountConnection
|
||||
db.AccountConnections.Add(new SnAccountConnection
|
||||
{
|
||||
AccountId = accountId,
|
||||
Provider = provider,
|
||||
@@ -324,7 +325,7 @@ public class ConnectionController(
|
||||
var account = await accounts.LookupAccount(userInfo.Email) ?? await accounts.CreateAccount(userInfo);
|
||||
|
||||
// Create connection for new or existing user
|
||||
var newConnection = new AccountConnection
|
||||
var newConnection = new SnAccountConnection
|
||||
{
|
||||
Account = account,
|
||||
Provider = provider,
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,6 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
|
||||
namespace DysonNetwork.Pass.Auth.OpenId;
|
||||
|
@@ -1,8 +1,4 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DysonNetwork.Pass;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
@@ -33,7 +33,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 Account.Account currentUser)
|
||||
if (HttpContext.Items["CurrentUser"] is SnAccount currentUser)
|
||||
{
|
||||
var state = Guid.NewGuid().ToString();
|
||||
var nonce = Guid.NewGuid().ToString();
|
||||
@@ -128,7 +128,7 @@ public class OidcController(
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<Account.Account> FindOrCreateAccount(OidcUserInfo userInfo, string provider)
|
||||
private async Task<SnAccount> FindOrCreateAccount(OidcUserInfo userInfo, string provider)
|
||||
{
|
||||
if (string.IsNullOrEmpty(userInfo.Email))
|
||||
throw new ArgumentException("Email is required for account creation");
|
||||
@@ -157,7 +157,7 @@ public class OidcController(
|
||||
return existingAccount;
|
||||
}
|
||||
|
||||
var connection = new AccountConnection
|
||||
var connection = new SnAccountConnection
|
||||
{
|
||||
AccountId = existingAccount.Id,
|
||||
Provider = provider,
|
||||
@@ -178,7 +178,7 @@ public class OidcController(
|
||||
var newAccount = await accounts.CreateAccount(userInfo);
|
||||
|
||||
// Create the provider connection
|
||||
var newConnection = new AccountConnection
|
||||
var newConnection = new SnAccountConnection
|
||||
{
|
||||
AccountId = newAccount.Id,
|
||||
Provider = provider,
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text.Json.Serialization;
|
||||
using DysonNetwork.Pass.Account;
|
||||
using DysonNetwork.Shared.Cache;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -190,7 +189,7 @@ public abstract class OidcService(
|
||||
/// </summary>
|
||||
public async Task<SnAuthChallenge> CreateChallengeForUserAsync(
|
||||
OidcUserInfo userInfo,
|
||||
Account.Account account,
|
||||
SnAccount account,
|
||||
HttpContext request,
|
||||
string deviceId,
|
||||
string? deviceName = null
|
||||
@@ -205,7 +204,7 @@ public abstract class OidcService(
|
||||
|
||||
if (connection is null)
|
||||
{
|
||||
connection = new AccountConnection
|
||||
connection = new SnAccountConnection
|
||||
{
|
||||
Provider = ProviderName,
|
||||
ProvidedIdentifier = userInfo.UserId ?? "",
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using DysonNetwork.Pass.Wallet;
|
||||
|
Reference in New Issue
Block a user