@rineex/auth-core 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/eslint.config.mjs +1 -0
- package/package.json +7 -2
- package/src/application/mfa/events/challenge-issue-observability.event.ts +18 -0
- package/src/application/mfa/events/session-started-observability.event.ts +18 -0
- package/src/application/mfa/events/verification-failed-observability.event.ts +14 -0
- package/src/application/mfa/events/verification-succeeded-observibility.event.ts +12 -0
- package/src/application/mfa/issue-mfa-challenge.application-service.ts +75 -0
- package/src/application/mfa/start-mfa-session.application-service.ts +90 -0
- package/src/application/mfa/verify-mfa.application-service.ts +61 -0
- package/src/application/services/auth-orchestrator.service.ts +77 -0
- package/src/application/services/oauth-authorize.service.ts +12 -0
- package/src/domain/{aggregates → identity/aggregates}/authentication-attempt.aggregate.ts +43 -26
- package/src/domain/identity/aggregates/index.ts +1 -0
- package/src/domain/identity/entities/identity.entity.ts +126 -0
- package/src/domain/identity/entities/index.ts +1 -0
- package/src/domain/identity/events/index.ts +3 -0
- package/src/domain/identity/index.ts +4 -0
- package/src/domain/identity/value-objects/__tests__/auth-attempt-id.vo.spec.ts +42 -0
- package/src/domain/identity/value-objects/__tests__/auth-factor.vo.spec.ts +39 -0
- package/src/domain/identity/value-objects/__tests__/auth-method.vo.spec.ts +0 -0
- package/src/domain/{value-objects → identity/value-objects}/auth-attempt-id.vo.ts +4 -0
- package/src/domain/identity/value-objects/auth-factor.vo.ts +17 -0
- package/src/domain/identity/value-objects/auth-method.vo.ts +34 -0
- package/src/domain/identity/value-objects/auth-policy.vo.ts +19 -0
- package/src/domain/{value-objects → identity/value-objects}/identity-id.vo.ts +4 -0
- package/src/domain/identity/value-objects/identity-provider.vo.ts +13 -0
- package/src/domain/identity/value-objects/index.ts +8 -0
- package/src/domain/identity/value-objects/risk-signal.vo.ts +17 -0
- package/src/domain/index.ts +5 -0
- package/src/domain/mfa/aggregates/mfa-session.aggregate.ts +84 -0
- package/src/domain/mfa/entities/mfa-challenge.entity.ts +70 -0
- package/src/domain/mfa/types/mfa-challenge-registry.ts +21 -0
- package/src/domain/mfa/value-objects/mfa-challenge-id.vo.ts +19 -0
- package/src/domain/mfa/value-objects/mfa-challenge-status.vo.ts +31 -0
- package/src/domain/mfa/value-objects/mfa-session-id.vo.ts +19 -0
- package/src/domain/mfa/violations/mfa-active-challenge-exists.violation.ts +10 -0
- package/src/domain/mfa/violations/mfa-already-verified.violation.ts +10 -0
- package/src/domain/mfa/violations/mfa-attempts-exceeded.violation.ts +17 -0
- package/src/domain/mfa/violations/mfa-expired.violation.ts +10 -0
- package/src/domain/oauth/aggregates/oauth-authorization.aggregate.ts +106 -0
- package/src/domain/oauth/aggregates/oauth-authorize.service.ts +0 -0
- package/src/domain/oauth/entities/oauth-authorization.entity.ts +50 -0
- package/src/domain/oauth/value-objects/authorization-code-id.vo.ts +9 -0
- package/src/domain/oauth/value-objects/authorization-code.vo.ts +18 -0
- package/src/domain/oauth/value-objects/client-id.vo.ts +9 -0
- package/src/domain/oauth/value-objects/code-challenge-method.vo.ts +15 -0
- package/src/domain/oauth/value-objects/code-challenge.vo.ts +24 -0
- package/src/domain/oauth/value-objects/oauth-authorization-id.vo.ts +19 -0
- package/src/domain/oauth/value-objects/oauth-provider.vo.ts +15 -0
- package/src/domain/oauth/value-objects/pkce.vo.ts +29 -0
- package/src/domain/oauth/value-objects/redirect-uri.vo.ts +19 -0
- package/src/domain/oauth/value-objects/scope-set.vo.ts +37 -0
- package/src/domain/oauth/violations/authorization-already-used.violation.ts +10 -0
- package/src/domain/oauth/violations/authorization-expired.violation.ts +10 -0
- package/src/domain/oauth/violations/consent-required.violation.ts +10 -0
- package/src/domain/oauth/violations/invalid-authorization-code.violation.ts +12 -0
- package/src/domain/oauth/violations/invalid-oauth-provider.violation.ts +13 -0
- package/src/domain/oauth/violations/invalid-pkce.violation.ts +12 -0
- package/src/domain/oauth/violations/invalid-redirect-uri.violation.ts +10 -0
- package/src/domain/policy/contracts/auth-policy-context.ts +27 -0
- package/src/domain/policy/contracts/auth-policy-decision.ts +7 -0
- package/src/domain/policy/contracts/auth-policy.ts +17 -0
- package/src/domain/policy/contracts/index.ts +3 -0
- package/src/domain/policy/engine/auth-policy-engine.ts +41 -0
- package/src/domain/policy/index.ts +2 -0
- package/src/domain/session/entities/session.entity.ts +82 -0
- package/src/domain/session/value-objects/session-id.vo.ts +10 -0
- package/src/domain/token/aggregates/token.aggregate.ts +34 -0
- package/src/domain/token/value-objects/auth-token.vo.ts +29 -0
- package/src/domain/token/value-objects/session-token.vo.ts +14 -0
- package/src/domain/violations/auth-domain.violation.ts +9 -0
- package/src/domain/violations/invalid-auth-token.violation.ts +13 -0
- package/src/domain/violations/invalid-scope.violation.ts +10 -0
- package/src/domain/violations/invalid-session.violation.ts +13 -0
- package/src/index.ts +3 -1
- package/src/ports/inbound/auth-method.port.ts +1 -1
- package/src/ports/inbound/index.ts +2 -0
- package/src/ports/inbound/start-auth.command.ts +28 -0
- package/src/ports/index.ts +2 -0
- package/src/ports/log/log.port.ts +25 -0
- package/src/ports/mfa/mfa-clock.port.ts +11 -0
- package/src/ports/mfa/mfa-session-id-generator.port.ts +15 -0
- package/src/ports/mfa/mfa-session-repository.port.ts +31 -0
- package/src/ports/observability/observability-event.port.ts +16 -0
- package/src/ports/outbound/authentication-attempt.repository.port.ts +1 -1
- package/src/ports/outbound/domain-event-publisher.port.ts +13 -0
- package/src/ports/outbound/index.ts +2 -0
- package/src/ports/outbound/session.repository.port.ts +9 -0
- package/src/ports/repositories/oauth-authorization.repository.ts +21 -0
- package/src/ports/repositories/token.repository.ts +11 -0
- package/src/types/auth-factor.type.ts +10 -0
- package/src/types/auth-method.type.ts +20 -0
- package/src/types/auth-policy.type.ts +16 -0
- package/src/types/identity-provider.type.ts +8 -0
- package/src/types/index.ts +6 -0
- package/src/types/observability-event.ts +33 -0
- package/src/types/risk-signal.type.ts +11 -0
- package/src/utils/default-if-blank.util.ts +46 -0
- package/tsconfig.json +4 -1
- package/src/domain/entities/identity.entity.ts +0 -13
- package/src/domain/value-objects/auth-method.vo.ts +0 -21
- /package/src/domain/{events → identity/events}/authentication-failed.event.ts +0 -0
- /package/src/domain/{events → identity/events}/authentication-started.event.ts +0 -0
- /package/src/domain/{events → identity/events}/authentication-succeeded.event.ts +0 -0
- /package/src/domain/{value-objects → identity/value-objects}/auth-status.vo.ts +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AggregateRoot, CreateEntityProps } from '@rineex/ddd';
|
|
2
|
+
import { IdentityId } from '@/domain/identity';
|
|
3
|
+
|
|
4
|
+
interface TokenProps extends CreateEntityProps<IdentityId> {
|
|
5
|
+
readonly identityId: IdentityId;
|
|
6
|
+
readonly expiresAt: Date;
|
|
7
|
+
revokedAt?: Date;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class Token extends AggregateRoot<IdentityId> {
|
|
11
|
+
protected constructor(private props: TokenProps) {
|
|
12
|
+
super(props);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public revoke(at: Date): void {
|
|
16
|
+
if (this.props.revokedAt) return;
|
|
17
|
+
|
|
18
|
+
this.mutate(draft => {
|
|
19
|
+
draft.revokedAt = at;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public isActive(now = new Date()): boolean {
|
|
24
|
+
return (
|
|
25
|
+
!this.props.revokedAt && this.props.expiresAt.getTime() > now.getTime()
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
validate(): void {
|
|
30
|
+
if (this.props.expiresAt.getTime() <= Date.now()) {
|
|
31
|
+
throw new Error('Token already expired');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { InvalidAuthTokenViolation } from '@/domain/violations/invalid-auth-token.violation';
|
|
2
|
+
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a cryptographic authentication token.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT:
|
|
8
|
+
* - This is NOT a JWT
|
|
9
|
+
* - This is NOT a bearer string
|
|
10
|
+
* - This is a domain-level proof of authentication
|
|
11
|
+
*
|
|
12
|
+
* Concrete formats live in adapters.
|
|
13
|
+
*/
|
|
14
|
+
export abstract class AuthToken extends PrimitiveValueObject<string> {
|
|
15
|
+
/**
|
|
16
|
+
* Token type identifier.
|
|
17
|
+
* Used for routing to correct adapters.
|
|
18
|
+
*/
|
|
19
|
+
abstract readonly type: string;
|
|
20
|
+
|
|
21
|
+
protected validate(value: string): void {
|
|
22
|
+
if (value.length < 32) {
|
|
23
|
+
throw InvalidAuthTokenViolation.create({
|
|
24
|
+
actualLength: value.length,
|
|
25
|
+
minLength: 32,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AuthToken } from './auth-token.vo';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Token bound to a session lifecycle.
|
|
5
|
+
*/
|
|
6
|
+
export class SessionToken extends AuthToken {
|
|
7
|
+
get type() {
|
|
8
|
+
return 'session';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public static create(value: string): SessionToken {
|
|
12
|
+
return new SessionToken(value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for all authentication domain violations.
|
|
5
|
+
*
|
|
6
|
+
* Auth domain MUST only throw DomainViolation objects.
|
|
7
|
+
* Mapping to native Error happens at application or adapter layer.
|
|
8
|
+
*/
|
|
9
|
+
export abstract class AuthDomainViolation extends DomainViolation {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AuthDomainViolation } from './auth-domain.violation';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Raised when an authentication token violates domain invariants.
|
|
5
|
+
*/
|
|
6
|
+
export class InvalidAuthTokenViolation extends AuthDomainViolation {
|
|
7
|
+
readonly code = 'AUTH_TOKEN_INVALID';
|
|
8
|
+
readonly message = 'Authentication token is invalid';
|
|
9
|
+
|
|
10
|
+
public static create(details: { actualLength: number; minLength: number }) {
|
|
11
|
+
return new InvalidAuthTokenViolation(details);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
+
|
|
3
|
+
export class InvalidScopeViolation extends DomainViolation {
|
|
4
|
+
public readonly code = 'auth.scope.invalid';
|
|
5
|
+
public readonly message = 'Scope format is invalid';
|
|
6
|
+
|
|
7
|
+
public static create(): InvalidScopeViolation {
|
|
8
|
+
return new InvalidScopeViolation();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AuthDomainViolation } from './auth-domain.violation';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Raised when a session invariant is violated.
|
|
5
|
+
*/
|
|
6
|
+
export class InvalidSessionViolation extends AuthDomainViolation {
|
|
7
|
+
readonly code = 'session.invalid';
|
|
8
|
+
readonly message = 'Session state is invalid';
|
|
9
|
+
|
|
10
|
+
public static create(): InvalidSessionViolation {
|
|
11
|
+
return new InvalidSessionViolation();
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AuthMethodName } from '@/types/auth-method.type';
|
|
2
|
+
import { AuthContext } from '@/types/auth-context.type';
|
|
3
|
+
import { IdentityId } from '@/domain';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Command representing a request to start authentication.
|
|
7
|
+
*
|
|
8
|
+
* This is a pure use-case input.
|
|
9
|
+
* It is NOT tied to HTTP, RPC, GraphQL, or CLI.
|
|
10
|
+
*/
|
|
11
|
+
export type StartAuthenticationCommand = {
|
|
12
|
+
/**
|
|
13
|
+
* Authentication method requested.
|
|
14
|
+
* Strongly typed and autocomplete-safe.
|
|
15
|
+
*/
|
|
16
|
+
readonly method: AuthMethodName;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Optional identity hint.
|
|
20
|
+
* Used for passwordless, SSO, or known users.
|
|
21
|
+
*/
|
|
22
|
+
readonly identityId?: IdentityId;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Execution context for auditing, correlation, and risk.
|
|
26
|
+
*/
|
|
27
|
+
readonly context: AuthContext;
|
|
28
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application-level logger port.
|
|
3
|
+
*
|
|
4
|
+
* Must be:
|
|
5
|
+
* - side-effect only
|
|
6
|
+
* - non-throwing
|
|
7
|
+
* - fast
|
|
8
|
+
*
|
|
9
|
+
* Domain must never depend on this.
|
|
10
|
+
*/
|
|
11
|
+
export type LoggerPort = {
|
|
12
|
+
info: (message: string, metadata?: Readonly<Record<string, unknown>>) => void;
|
|
13
|
+
|
|
14
|
+
debug: (
|
|
15
|
+
message: string,
|
|
16
|
+
metadata?: Readonly<Record<string, unknown>>,
|
|
17
|
+
) => void;
|
|
18
|
+
|
|
19
|
+
warn: (message: string, metadata?: Readonly<Record<string, unknown>>) => void;
|
|
20
|
+
|
|
21
|
+
error: (
|
|
22
|
+
message: string,
|
|
23
|
+
metadata?: Readonly<Record<string, unknown>>,
|
|
24
|
+
) => void;
|
|
25
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MfaSessionId } from '@/domain/mfa/value-objects/mfa-session-id.vo';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates MFA session identifiers.
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* - ULID
|
|
8
|
+
* - UUIDv7
|
|
9
|
+
* - KSUID
|
|
10
|
+
*
|
|
11
|
+
* Domain does NOT care.
|
|
12
|
+
*/
|
|
13
|
+
export type MfaSessionIdGenerator = {
|
|
14
|
+
generate: () => MfaSessionId;
|
|
15
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MfaSessionId } from '@/domain/mfa/value-objects/mfa-session-id.vo';
|
|
2
|
+
import { MFASession } from '@/domain/mfa/aggregates/mfa-session.aggregate';
|
|
3
|
+
import { IdentityId } from '@/domain';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Persistence port for MFA sessions.
|
|
7
|
+
*
|
|
8
|
+
* Implementations may use:
|
|
9
|
+
* - SQL
|
|
10
|
+
* - NoSQL
|
|
11
|
+
* - Redis
|
|
12
|
+
* - In-memory
|
|
13
|
+
*
|
|
14
|
+
* Domain MUST NOT know which.
|
|
15
|
+
*/
|
|
16
|
+
export type MfaSessionRepository = {
|
|
17
|
+
/**
|
|
18
|
+
* Find MFA session by aggregate id.
|
|
19
|
+
*/
|
|
20
|
+
findById: (id: MfaSessionId) => Promise<MFASession | null>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Find active MFA session for an identity.
|
|
24
|
+
*/
|
|
25
|
+
findActiveByIdentity: (identityId: IdentityId) => Promise<MFASession | null>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Persist MFA session state.
|
|
29
|
+
*/
|
|
30
|
+
save: (session: MFASession) => Promise<void>;
|
|
31
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ObservabilityEvent } from '@/types/observability-event';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Observability event emitter.
|
|
5
|
+
*
|
|
6
|
+
* Used for:
|
|
7
|
+
* - audit logs
|
|
8
|
+
* - security monitoring
|
|
9
|
+
* - metrics
|
|
10
|
+
* - tracing
|
|
11
|
+
*
|
|
12
|
+
* Must be non-blocking and non-throwing.
|
|
13
|
+
*/
|
|
14
|
+
export type ObservabilityEventPort = {
|
|
15
|
+
emit: (event: ObservabilityEvent) => void;
|
|
16
|
+
};
|
|
@@ -6,6 +6,6 @@ import { AuthenticationAttempt } from '@/domain/aggregates/authentication-attemp
|
|
|
6
6
|
* Implementation is infrastructure-specific.
|
|
7
7
|
*/
|
|
8
8
|
export type AuthenticationAttemptRepositoryPort = {
|
|
9
|
-
save: (attempt:
|
|
9
|
+
save: (attempt: AuthenticationAttempt) => Promise<void>;
|
|
10
10
|
findById: (id: string) => Promise<AuthenticationAttempt | null>;
|
|
11
11
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DomainEvent } from '@rineex/ddd';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Publishes domain events emitted by aggregates.
|
|
5
|
+
*
|
|
6
|
+
* Implementations may:
|
|
7
|
+
* - dispatch to message bus
|
|
8
|
+
* - log events
|
|
9
|
+
* - forward to observability stack
|
|
10
|
+
*/
|
|
11
|
+
export type DomainEventPublisherPort = {
|
|
12
|
+
publish: (events: readonly DomainEvent[]) => Promise<void>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SessionToken } from '@/domain/token/value-objects/session-token.vo';
|
|
2
|
+
import { SessionId } from '@/domain/session/value-objects/session-id.vo';
|
|
3
|
+
import { Session } from '@/domain/session/entities/session.entity';
|
|
4
|
+
|
|
5
|
+
export type SessionRepositoryPort = {
|
|
6
|
+
save: (session: Session) => Promise<void>;
|
|
7
|
+
findById: (id: SessionId) => Promise<Session | null>;
|
|
8
|
+
findByToken: (token: SessionToken) => Promise<Session | null>;
|
|
9
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OauthAuthorization } from '@/domain/oauth/aggregates/oauth-authorization.aggregate';
|
|
2
|
+
|
|
3
|
+
export type OAuthAuthorizationRepository = {
|
|
4
|
+
/**
|
|
5
|
+
* Persists an OAuth authorization aggregate.
|
|
6
|
+
*/
|
|
7
|
+
save: (authorization: OauthAuthorization) => Promise<void>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Loads authorization by its identifier.
|
|
11
|
+
*/
|
|
12
|
+
getById: (id: string) => Promise<OauthAuthorization | null>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Finds active authorization by client and identity.
|
|
16
|
+
*/
|
|
17
|
+
findActiveByClientAndIdentity: (params: {
|
|
18
|
+
readonly clientId: string;
|
|
19
|
+
readonly identityId: string;
|
|
20
|
+
}) => Promise<OauthAuthorization | null>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type TokenRepository = {
|
|
2
|
+
save: (token: Token) => Promise<void>;
|
|
3
|
+
|
|
4
|
+
getById: (id: string) => Promise<Token | null>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Revokes all active tokens for an identity.
|
|
8
|
+
* Used for logout-all / security events.
|
|
9
|
+
*/
|
|
10
|
+
revokeAllByIdentity: (identityId: string) => Promise<void>;
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry for authentication method identifiers.
|
|
3
|
+
*
|
|
4
|
+
* This type is intentionally open for module augmentation.
|
|
5
|
+
* Each auth-method package extends this registry.
|
|
6
|
+
*/
|
|
7
|
+
export type AuthMethodRegistry = {
|
|
8
|
+
readonly passwordless: true;
|
|
9
|
+
readonly otp: true;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Union of all registered authentication method names.
|
|
14
|
+
*
|
|
15
|
+
* This enables:
|
|
16
|
+
* - IDE autocomplete
|
|
17
|
+
* - Type safety
|
|
18
|
+
* - Plugin-based extension
|
|
19
|
+
*/
|
|
20
|
+
export type AuthMethodName = keyof AuthMethodRegistry;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of authentication policy identifiers.
|
|
3
|
+
*
|
|
4
|
+
* This registry is intentionally open for module augmentation.
|
|
5
|
+
* Each policy package extends this type.
|
|
6
|
+
*/
|
|
7
|
+
export type AuthPolicyRegistry = {
|
|
8
|
+
readonly base: true;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Union of all registered authentication policy names.
|
|
13
|
+
*
|
|
14
|
+
* Enables autocomplete and compile-time safety.
|
|
15
|
+
*/
|
|
16
|
+
export type AuthPolicyName = keyof AuthPolicyRegistry;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base structure for observability events.
|
|
3
|
+
*
|
|
4
|
+
* These events:
|
|
5
|
+
* - are NOT domain events
|
|
6
|
+
* - must be safe to store long-term
|
|
7
|
+
* - must never contain secrets
|
|
8
|
+
*/
|
|
9
|
+
export type ObservabilityEventProps = {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
readonly occurredAt?: Date;
|
|
12
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class ObservabilityEvent {
|
|
16
|
+
public readonly name: string;
|
|
17
|
+
public readonly occurredAt: Date;
|
|
18
|
+
public readonly payload: Readonly<Record<string, unknown>>;
|
|
19
|
+
|
|
20
|
+
constructor(props: ObservabilityEventProps) {
|
|
21
|
+
this.name = props.name;
|
|
22
|
+
this.occurredAt = props.occurredAt ?? new Date();
|
|
23
|
+
this.payload = props.payload;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
toPrimitives() {
|
|
27
|
+
return {
|
|
28
|
+
occurredAt: this.occurredAt.toISOString(),
|
|
29
|
+
payload: this.payload,
|
|
30
|
+
name: this.name,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry of risk signal identifiers.
|
|
3
|
+
*
|
|
4
|
+
* Signals are produced by detectors and consumed by policies.
|
|
5
|
+
*/
|
|
6
|
+
export type RiskSignalRegistry = {};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Union of all registered risk signal names.
|
|
10
|
+
*/
|
|
11
|
+
export type RiskSignalName = keyof RiskSignalRegistry;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import isEmpty from 'lodash.isempty';
|
|
2
|
+
import isNil from 'lodash.isnil';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a default value if the input string is nil or empty.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The input string to check.
|
|
8
|
+
* @param defaultValue - The default string to return if input is nil or empty.
|
|
9
|
+
* @returns The input string if non-empty, otherwise the default value.
|
|
10
|
+
*/
|
|
11
|
+
export function defaultIfBlank<T extends string = string>(
|
|
12
|
+
value: T,
|
|
13
|
+
defaultValue: T,
|
|
14
|
+
): T {
|
|
15
|
+
if (isNil(value) || isEmpty(value)) {
|
|
16
|
+
return defaultValue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns null if the input string is nil or empty, otherwise returns the input.
|
|
24
|
+
*
|
|
25
|
+
* @param value - The input string to check.
|
|
26
|
+
* @returns The input string if non-empty, otherwise null.
|
|
27
|
+
*/
|
|
28
|
+
export function defaultIfNilOrEmpty<T extends string = string>(
|
|
29
|
+
value: T | null | undefined,
|
|
30
|
+
): T | null {
|
|
31
|
+
if (isNil(value) || isEmpty(value)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function defaultIfUndefinedOrEmpty<T extends string = string>(
|
|
39
|
+
value: T | undefined,
|
|
40
|
+
): T | undefined {
|
|
41
|
+
if (isNil(value) || isEmpty(value)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return value;
|
|
46
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"module": "commonjs",
|
|
4
4
|
"declaration": true,
|
|
5
|
-
"noImplicitAny": false,
|
|
6
5
|
"noUnusedLocals": false,
|
|
6
|
+
"strictNullChecks": true,
|
|
7
7
|
"importHelpers": true,
|
|
8
8
|
"removeComments": false,
|
|
9
9
|
"noLib": false,
|
|
10
10
|
"emitDecoratorMetadata": true,
|
|
11
11
|
"esModuleInterop": true,
|
|
12
12
|
"experimentalDecorators": true,
|
|
13
|
+
"noImplicitThis": true,
|
|
13
14
|
"target": "es6",
|
|
14
15
|
"sourceMap": false,
|
|
15
16
|
"outDir": "./dist",
|
|
17
|
+
"noImplicitAny": true,
|
|
16
18
|
"rootDir": ".",
|
|
19
|
+
"strictPropertyInitialization": true,
|
|
17
20
|
"paths": {
|
|
18
21
|
"@/*": ["./src/*"]
|
|
19
22
|
},
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Entity } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { IdentityId } from '../value-objects/identity-id.vo';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Represents an authenticated or authenticating identity.
|
|
7
|
-
*
|
|
8
|
-
* This entity is intentionally minimal.
|
|
9
|
-
* All profile, permissions, and roles belong elsewhere.
|
|
10
|
-
*/
|
|
11
|
-
export class Identity extends Entity<IdentityId> {
|
|
12
|
-
validate(): void {}
|
|
13
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Represents the authentication method requested or used.
|
|
5
|
-
*
|
|
6
|
-
* Examples:
|
|
7
|
-
* - passwordless
|
|
8
|
-
* - otp
|
|
9
|
-
* - oauth
|
|
10
|
-
* - oidc
|
|
11
|
-
* - passkey
|
|
12
|
-
*
|
|
13
|
-
* This is a value object, NOT a strategy.
|
|
14
|
-
*/
|
|
15
|
-
export class AuthMethod extends PrimitiveValueObject<string> {
|
|
16
|
-
protected validate(value: string): void {
|
|
17
|
-
if (!value) {
|
|
18
|
-
throw new Error('AuthMethod cannot be empty');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|