Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions UltimateAuth.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</Folder>
<Project Path="src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj" Id="bd87e254-0565-4fc5-950d-ee5bbb416079" />
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj" Id="40a23002-f885-42a8-bdd9-fd962ab28742" />
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj" Id="8572d1e8-db32-42a1-b61c-e8805e59c019" />
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj" Id="a1e6d007-bdc0-4574-b549-ec863757edd3" />
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj" Id="84b784d0-bb48-406a-a0d1-c600da667597" />
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj" Id="28b1d647-fb0b-4cc3-8503-2680c4a9b28f" />
Expand All @@ -26,13 +27,15 @@
<Project Path="src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj" Id="62ee7b1d-46ce-4f2e-985d-1e794f891b8b" />
<Project Path="src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj" Id="ca03a140-f3dc-4a21-9b7d-895a3b10808b" />
<Project Path="src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj" Id="2281c3b5-1d60-4542-a673-553f96eed25b" />
<Project Path="src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore.Abstractions/CodeBeam.UltimateAuth.EntityFrameworkCore.Abstractions.csproj" Id="8867767d-bd1b-4d51-ac3f-0979038165c9" />
<Project Path="src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj" Id="b37c337f-2446-4f54-8684-b72fa83ac444" />
<Project Path="src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj" Id="6abfb7a6-ea36-42db-a843-38054dd40fd8" />
<Project Path="src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj" Id="5b9a090d-1689-4a81-9dfa-3ba69f0bda38" />
<Project Path="src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj" Id="fc9bfef0-8a89-4639-81ee-3f84f6e33816" />
<Project Path="src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj" Id="6eb14b32-0b56-460f-a2b2-f95d28bad625" />
<Project Path="src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj" Id="8220884e-4958-4b49-8c69-56ce9d2b6c6f" />
<Project Path="src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj" Id="3a04f065-8f9d-46b3-9726-1febffe6d46f" />
<Project Path="src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj" Id="a8febfee-0cfe-4e8c-8dcb-8703c35dd77b" />
<Project Path="src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj" Id="7ce3df22-4773-4b9b-afd0-8ba506e0f9de" />
<Project Path="src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj" Id="601176dd-b760-4b6f-9cc7-c618134ae178" />
<Project Path="src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj" Id="30d5db36-6dc8-46f6-9139-8b6b3d6053d5" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
using CodeBeam.UltimateAuth.Core.Domain;
using CodeBeam.UltimateAuth.Core.MultiTenancy;

namespace CodeBeam.UltimateAuth.Core.Abstractions;

/// <summary>
/// Low-level persistence abstraction for refresh tokens.
/// NO validation logic. NO business rules.
/// </summary>
public interface IRefreshTokenStore
{
Task StoreAsync(TenantKey tenant, StoredRefreshToken token, CancellationToken ct = default);
Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken ct = default);

Task<StoredRefreshToken?> FindByHashAsync(TenantKey tenant, string tokenHash, CancellationToken ct = default);
Task<TResult> ExecuteAsync<TResult>(Func<CancellationToken, Task<TResult>> action, CancellationToken ct = default);

Task RevokeAsync(TenantKey tenant, string tokenHash, DateTimeOffset revokedAt, string? replacedByTokenHash = null, CancellationToken ct = default);
Task StoreAsync(RefreshToken token, CancellationToken ct = default);

Task RevokeBySessionAsync(TenantKey tenant, AuthSessionId sessionId, DateTimeOffset revokedAt, CancellationToken ct = default);
Task<RefreshToken?> FindByHashAsync(string tokenHash, CancellationToken ct = default);

Task RevokeByChainAsync(TenantKey tenant, SessionChainId chainId, DateTimeOffset revokedAt, CancellationToken ct = default);
Task RevokeAsync(string tokenHash, DateTimeOffset revokedAt, string? replacedByTokenHash = null, CancellationToken ct = default);

Task RevokeAllForUserAsync(TenantKey tenant, UserKey userKey, DateTimeOffset revokedAt, CancellationToken ct = default);
Task RevokeBySessionAsync(AuthSessionId sessionId, DateTimeOffset revokedAt, CancellationToken ct = default);

Task RevokeByChainAsync(SessionChainId chainId, DateTimeOffset revokedAt, CancellationToken ct = default);

Task RevokeAllForUserAsync(UserKey userKey, DateTimeOffset revokedAt, CancellationToken ct = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using CodeBeam.UltimateAuth.Core.MultiTenancy;

namespace CodeBeam.UltimateAuth.Core.Abstractions;

public interface IRefreshTokenStoreFactory
{
IRefreshTokenStore Create(TenantKey tenant);
}
4 changes: 4 additions & 0 deletions src/CodeBeam.UltimateAuth.Core/AssemblyVisibility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@

[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Server")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Users.EntityFrameworkCore")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore")]
[assembly: InternalsVisibleTo("CodeBeam.UltimateAuth.Tests.Unit")]
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeBeam.UltimateAuth.Core.Domain;
using CodeBeam.UltimateAuth.Core.Errors;
using CodeBeam.UltimateAuth.Core.MultiTenancy;
using System.Collections;

Expand Down Expand Up @@ -29,7 +30,7 @@ public sealed class AccessContext
public UserKey GetTargetUserKey()
{
if (TargetUserKey is not UserKey targetUserKey)
throw new InvalidOperationException("Target user is not found.");
throw new UAuthNotFoundException("Target user is not found.");

return targetUserKey;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public sealed record LoginResult
public LoginStatus Status { get; init; }
public AuthSessionId? SessionId { get; init; }
public AccessToken? AccessToken { get; init; }
public RefreshToken? RefreshToken { get; init; }
public RefreshTokenInfo? RefreshToken { get; init; }
public LoginContinuation? Continuation { get; init; }
public AuthFailureReason? FailureReason { get; init; }
public DateTimeOffset? LockoutUntilUtc { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public sealed class RefreshFlowResult

public AuthSessionId? SessionId { get; init; }
public AccessToken? AccessToken { get; init; }
public RefreshToken? RefreshToken { get; init; }
public RefreshTokenInfo? RefreshToken { get; init; }

public static RefreshFlowResult ReauthRequired()
{
Expand All @@ -24,7 +24,7 @@ public static RefreshFlowResult Success(
RefreshOutcome outcome,
AuthSessionId? sessionId = null,
AccessToken? accessToken = null,
RefreshToken? refreshToken = null)
RefreshTokenInfo? refreshToken = null)
{
return new RefreshFlowResult
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ public sealed record AuthTokens
/// </summary>
public AccessToken AccessToken { get; init; } = default!;

public RefreshToken? RefreshToken { get; init; }
public RefreshTokenInfo? RefreshToken { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// <summary>
/// Transport model for refresh token. Returned to client once upon creation.
/// </summary>
public sealed class RefreshToken
public sealed class RefreshTokenInfo
{
/// <summary>
/// Plain refresh token value (returned to client once).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ public sealed record RefreshTokenRotationResult

public AuthSessionId? SessionId { get; init; }
public AccessToken? AccessToken { get; init; }
public RefreshToken? RefreshToken { get; init; }
public RefreshTokenInfo? RefreshToken { get; init; }

private RefreshTokenRotationResult() { }

public static RefreshTokenRotationResult Failed() => new() { IsSuccess = false, ReauthRequired = true };

public static RefreshTokenRotationResult Success(
AccessToken accessToken,
RefreshToken refreshToken)
RefreshTokenInfo refreshToken)
=> new()
{
IsSuccess = true,
Expand Down
10 changes: 5 additions & 5 deletions src/CodeBeam.UltimateAuth.Core/Domain/Device/DeviceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ public static DeviceContext Anonymous()

public static DeviceContext Create(
DeviceId deviceId,
string? deviceType,
string? platform,
string? operatingSystem,
string? browser,
string? ipAddress)
string? deviceType = null,
string? platform = null,
string? operatingSystem = null,
string? browser = null,
string? ipAddress = null)
{
return new DeviceContext(
deviceId,
Expand Down
26 changes: 15 additions & 11 deletions src/CodeBeam.UltimateAuth.Core/Domain/Session/UAuthSession.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using CodeBeam.UltimateAuth.Core.Abstractions;
using CodeBeam.UltimateAuth.Core.Errors;
using CodeBeam.UltimateAuth.Core.MultiTenancy;

namespace CodeBeam.UltimateAuth.Core.Domain;

// TODO: Add ISoftDeleteable
public sealed class UAuthSession : IVersionedEntity
{
public AuthSessionId SessionId { get; }
Expand All @@ -12,23 +12,25 @@ public sealed class UAuthSession : IVersionedEntity
public SessionChainId ChainId { get; }
public DateTimeOffset CreatedAt { get; }
public DateTimeOffset ExpiresAt { get; }
public bool IsRevoked { get; }
public DateTimeOffset? RevokedAt { get; }
public long SecurityVersionAtCreation { get; }
public DeviceContext Device { get; } // For snapshot,main value is chain's device.
public ClaimsSnapshot Claims { get; }
public SessionMetadata Metadata { get; }
public long Version { get; set; }

public bool IsRevoked => RevokedAt != null;

private UAuthSession(
AuthSessionId sessionId,
TenantKey tenant,
UserKey userKey,
SessionChainId chainId,
DateTimeOffset createdAt,
DateTimeOffset expiresAt,
bool isRevoked,
DateTimeOffset? revokedAt,
long securityVersionAtCreation,
DeviceContext device,
ClaimsSnapshot claims,
SessionMetadata metadata,
long version)
Expand All @@ -39,9 +41,9 @@ private UAuthSession(
ChainId = chainId;
CreatedAt = createdAt;
ExpiresAt = expiresAt;
IsRevoked = isRevoked;
RevokedAt = revokedAt;
SecurityVersionAtCreation = securityVersionAtCreation;
Device = device;
Claims = claims;
Metadata = metadata;
Version = version;
Expand All @@ -55,6 +57,7 @@ public static UAuthSession Create(
DateTimeOffset now,
DateTimeOffset expiresAt,
long securityVersion,
DeviceContext device,
ClaimsSnapshot? claims,
SessionMetadata metadata)
{
Expand All @@ -65,9 +68,9 @@ public static UAuthSession Create(
chainId,
createdAt: now,
expiresAt: expiresAt,
isRevoked: false,
revokedAt: null,
securityVersionAtCreation: securityVersion,
device: device,
claims: claims ?? ClaimsSnapshot.Empty,
metadata: metadata,
version: 0
Expand All @@ -76,7 +79,8 @@ public static UAuthSession Create(

public UAuthSession Revoke(DateTimeOffset at)
{
if (IsRevoked) return this;
if (IsRevoked)
return this;

return new UAuthSession(
SessionId,
Expand All @@ -85,9 +89,9 @@ public UAuthSession Revoke(DateTimeOffset at)
ChainId,
CreatedAt,
ExpiresAt,
true,
at,
SecurityVersionAtCreation,
Device,
Claims,
Metadata,
Version + 1
Expand All @@ -101,9 +105,9 @@ internal static UAuthSession FromProjection(
SessionChainId chainId,
DateTimeOffset createdAt,
DateTimeOffset expiresAt,
bool isRevoked,
DateTimeOffset? revokedAt,
long securityVersionAtCreation,
DeviceContext device,
ClaimsSnapshot claims,
SessionMetadata metadata,
long version)
Expand All @@ -115,9 +119,9 @@ internal static UAuthSession FromProjection(
chainId,
createdAt,
expiresAt,
isRevoked,
revokedAt,
securityVersionAtCreation,
device,
claims,
metadata,
version
Expand All @@ -138,7 +142,7 @@ public SessionState GetState(DateTimeOffset at)
public UAuthSession WithChain(SessionChainId chainId)
{
if (!ChainId.IsUnassigned)
throw new InvalidOperationException("Chain already assigned.");
throw new UAuthConflictException("Chain already assigned.");

return new UAuthSession(
sessionId: SessionId,
Expand All @@ -147,9 +151,9 @@ public UAuthSession WithChain(SessionChainId chainId)
chainId: chainId,
createdAt: CreatedAt,
expiresAt: ExpiresAt,
isRevoked: IsRevoked,
revokedAt: RevokedAt,
securityVersionAtCreation: SecurityVersionAtCreation,
device: Device,
claims: Claims,
metadata: Metadata,
version: Version + 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using CodeBeam.UltimateAuth.Core.Abstractions;
using CodeBeam.UltimateAuth.Core.MultiTenancy;
using static CodeBeam.UltimateAuth.Core.Defaults.UAuthActions;

namespace CodeBeam.UltimateAuth.Core.Domain;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@ public sealed class UAuthSessionRoot : IVersionedEntity

public DateTimeOffset CreatedAt { get; }
public DateTimeOffset? UpdatedAt { get; }

public bool IsRevoked { get; }
public DateTimeOffset? RevokedAt { get; }

public long SecurityVersion { get; }
public long Version { get; set; }

public bool IsRevoked => RevokedAt != null;

private UAuthSessionRoot(
SessionRootId rootId,
TenantKey tenant,
UserKey userKey,
DateTimeOffset createdAt,
DateTimeOffset? updatedAt,
bool isRevoked,
DateTimeOffset? revokedAt,
long securityVersion,
long version)
Expand All @@ -34,7 +33,6 @@ private UAuthSessionRoot(
UserKey = userKey;
CreatedAt = createdAt;
UpdatedAt = updatedAt;
IsRevoked = isRevoked;
RevokedAt = revokedAt;
SecurityVersion = securityVersion;
Version = version;
Expand All @@ -51,7 +49,6 @@ public static UAuthSessionRoot Create(
userKey,
at,
null,
false,
null,
0,
0
Expand All @@ -66,7 +63,6 @@ public UAuthSessionRoot IncreaseSecurityVersion(DateTimeOffset at)
UserKey,
CreatedAt,
at,
IsRevoked,
RevokedAt,
SecurityVersion + 1,
Version + 1
Expand All @@ -84,7 +80,6 @@ public UAuthSessionRoot Revoke(DateTimeOffset at)
UserKey,
CreatedAt,
at,
true,
at,
SecurityVersion + 1,
Version + 1
Expand All @@ -97,7 +92,6 @@ internal static UAuthSessionRoot FromProjection(
UserKey userKey,
DateTimeOffset createdAt,
DateTimeOffset? updatedAt,
bool isRevoked,
DateTimeOffset? revokedAt,
long securityVersion,
long version)
Expand All @@ -108,7 +102,6 @@ internal static UAuthSessionRoot FromProjection(
userKey,
createdAt,
updatedAt,
isRevoked,
revokedAt,
securityVersion,
version
Expand Down
Loading
Loading