:drunk: No idea what did AI did

This commit is contained in:
2025-07-06 19:46:59 +08:00
parent 14b79f16f4
commit 3391c08c04
40 changed files with 2484 additions and 112 deletions

View File

@ -0,0 +1,81 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using NodaTime;
namespace DysonNetwork.Common.Models;
/// <summary>
/// Represents a connection between an account and an authentication provider
/// </summary>
public class AccountConnection
{
/// <summary>
/// Unique identifier for the connection
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; } = null!;
/// <summary>
/// The account ID this connection is associated with
/// </summary>
public string? AccountId { get; set; }
/// <summary>
/// The authentication provider (e.g., "google", "github")
/// </summary>
[Required]
[MaxLength(50)]
public string Provider { get; set; } = null!;
/// <summary>
/// The unique identifier for the user from the provider
/// </summary>
[Required]
[MaxLength(256)]
public string ProvidedIdentifier { get; set; } = null!;
/// <summary>
/// Display name for the connection
/// </summary>
[MaxLength(100)]
public string? DisplayName { get; set; }
/// <summary>
/// OAuth access token from the provider
/// </summary>
public string? AccessToken { get; set; }
/// <summary>
/// OAuth refresh token from the provider (if available)
/// </summary>
public string? RefreshToken { get; set; }
/// <summary>
/// When the access token expires (if available)
/// </summary>
public Instant? ExpiresAt { get; set; }
/// <summary>
/// When the connection was first established
/// </summary>
public Instant CreatedAt { get; set; }
/// <summary>
/// When the connection was last used
/// </summary>
public Instant? LastUsedAt { get; set; }
/// <summary>
/// Additional metadata about the connection
/// </summary>
[Column(TypeName = "jsonb")]
public Dictionary<string, object>? Meta { get; set; }
/// <summary>
/// Navigation property for the associated account
/// </summary>
[ForeignKey(nameof(AccountId))]
public virtual Account? Account { get; set; }
}

View File

@ -65,4 +65,26 @@ public class AuthChallenge : ModelBase
if (StepRemain == 0 && BlacklistFactors.Count == 0) StepRemain = StepTotal;
return this;
}
}
public class AuthTokens
{
public string AccessToken { get; set; } = string.Empty;
public string RefreshToken { get; set; } = string.Empty;
public int ExpiresIn { get; set; }
public string TokenType { get; set; } = "Bearer";
public string? Scope { get; set; }
public string? IdToken { get; set; }
public static AuthTokens Create(string accessToken, string refreshToken, int expiresIn, string? scope = null, string? idToken = null)
{
return new AuthTokens
{
AccessToken = accessToken,
RefreshToken = refreshToken,
ExpiresIn = expiresIn,
Scope = scope,
IdToken = idToken
};
}
}

View File

@ -0,0 +1,74 @@
using System.Text.Json.Serialization;
namespace DysonNetwork.Common.Models.Auth;
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AuthChallengeType
{
// Authentication challenges
Password = 0,
EmailCode = 1,
PhoneCode = 2,
Totp = 3,
WebAuthn = 4,
RecoveryCode = 5,
// Authorization challenges
Consent = 10,
TwoFactor = 11,
// Account recovery challenges
ResetPassword = 20,
VerifyEmail = 21,
VerifyPhone = 22,
// Security challenges
Reauthentication = 30,
DeviceVerification = 31,
// Custom challenges
Custom = 100
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AuthChallengePlatform
{
Web = 0,
Ios = 1,
Android = 2,
Desktop = 3,
Api = 4,
Cli = 5,
Sdk = 6,
// Special platforms
System = 100,
Unknown = 999
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AuthFactorType
{
Password = 0,
EmailCode = 1,
PhoneCode = 2,
Totp = 3,
WebAuthn = 4,
RecoveryCode = 5,
// Social and federation
Google = 10,
Apple = 11,
Microsoft = 12,
Facebook = 13,
Twitter = 14,
Github = 15,
// Enterprise
Saml = 50,
Oidc = 51,
Ldap = 52,
// Custom factor types
Custom = 100
}

View File

@ -0,0 +1,10 @@
using NodaTime;
namespace DysonNetwork.Common.Models;
public class LastActiveInfo
{
public string SessionId { get; set; } = string.Empty;
public string AccountId { get; set; } = string.Empty;
public Instant SeenAt { get; set; }
}

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
namespace DysonNetwork.Common.Models;
/// <summary>
/// Represents user information from an OIDC provider
/// </summary>
public class OidcUserInfo
{
/// <summary>
/// The unique identifier for the user from the OIDC provider
/// </summary>
public string? UserId { get; set; }
/// <summary>
/// The user's email address
/// </summary>
public string? Email { get; set; }
/// <summary>
/// Whether the user's email has been verified by the OIDC provider
/// </summary>
public bool EmailVerified { get; set; }
/// <summary>
/// The user's given name (first name)
/// </summary>
public string? GivenName { get; set; }
/// <summary>
/// The user's family name (last name)
/// </summary>
public string? FamilyName { get; set; }
/// <summary>
/// The user's full name
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The user's preferred username
/// </summary>
public string? PreferredUsername { get; set; }
/// <summary>
/// URL to the user's profile picture
/// </summary>
public string? Picture { get; set; }
/// <summary>
/// The OIDC provider name (e.g., "google", "github")
/// </summary>
public string? Provider { get; set; }
/// <summary>
/// OAuth access token from the provider
/// </summary>
public string? AccessToken { get; set; }
/// <summary>
/// OAuth refresh token from the provider (if available)
/// </summary>
public string? RefreshToken { get; set; }
/// <summary>
/// When the access token expires (if available)
/// </summary>
public DateTimeOffset? ExpiresAt { get; set; }
/// <summary>
/// Additional claims from the ID token or user info endpoint
/// </summary>
public Dictionary<string, object>? Claims { get; set; }
/// <summary>
/// Converts the user info to a metadata dictionary for storage
/// </summary>
public Dictionary<string, object> ToMetadata()
{
var metadata = new Dictionary<string, object>();
if (!string.IsNullOrWhiteSpace(UserId))
metadata["user_id"] = UserId;
if (!string.IsNullOrWhiteSpace(Email))
metadata["email"] = Email;
metadata["email_verified"] = EmailVerified;
if (!string.IsNullOrWhiteSpace(GivenName))
metadata["given_name"] = GivenName;
if (!string.IsNullOrWhiteSpace(FamilyName))
metadata["family_name"] = FamilyName;
if (!string.IsNullOrWhiteSpace(Name))
metadata["name"] = Name;
if (!string.IsNullOrWhiteSpace(PreferredUsername))
metadata["preferred_username"] = PreferredUsername;
if (!string.IsNullOrWhiteSpace(Picture))
metadata["picture"] = Picture;
if (!string.IsNullOrWhiteSpace(Provider))
metadata["provider"] = Provider;
if (ExpiresAt.HasValue)
metadata["expires_at"] = ExpiresAt.Value;
return metadata;
}
}