@rineex/auth-core 0.0.6 → 0.1.1
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/package.json +8 -4
- package/Architecture.md +0 -257
- package/CHANGELOG.md +0 -49
- package/Definition.md +0 -1490
- package/Develop.md +0 -0
- package/RULES.md +0 -1470
- package/eslint.config.mjs +0 -59
- package/src/application/mfa/events/challenge-issue-observability.event.ts +0 -18
- package/src/application/mfa/events/session-started-observability.event.ts +0 -18
- package/src/application/mfa/events/verification-failed-observability.event.ts +0 -14
- package/src/application/mfa/events/verification-succeeded-observibility.event.ts +0 -12
- package/src/application/mfa/issue-mfa-challenge.application-service.ts +0 -75
- package/src/application/mfa/start-mfa-session.application-service.ts +0 -90
- package/src/application/mfa/verify-mfa.application-service.ts +0 -61
- package/src/application/services/auth-orchestrator.service.ts +0 -77
- package/src/application/services/oauth-authorize.service.ts +0 -12
- package/src/domain/identity/aggregates/authentication-attempt.aggregate.ts +0 -136
- package/src/domain/identity/aggregates/index.ts +0 -1
- package/src/domain/identity/entities/identity.entity.ts +0 -126
- package/src/domain/identity/entities/index.ts +0 -1
- package/src/domain/identity/events/authentication-failed.event.ts +0 -24
- package/src/domain/identity/events/authentication-started.event.ts +0 -29
- package/src/domain/identity/events/authentication-succeeded.event.ts +0 -24
- package/src/domain/identity/events/index.ts +0 -3
- package/src/domain/identity/index.ts +0 -4
- package/src/domain/identity/value-objects/__tests__/auth-attempt-id.vo.spec.ts +0 -42
- package/src/domain/identity/value-objects/__tests__/auth-factor.vo.spec.ts +0 -39
- package/src/domain/identity/value-objects/__tests__/auth-method.vo.spec.ts +0 -0
- package/src/domain/identity/value-objects/auth-attempt-id.vo.ts +0 -23
- package/src/domain/identity/value-objects/auth-factor.vo.ts +0 -17
- package/src/domain/identity/value-objects/auth-method.vo.ts +0 -34
- package/src/domain/identity/value-objects/auth-policy.vo.ts +0 -19
- package/src/domain/identity/value-objects/auth-status.vo.ts +0 -38
- package/src/domain/identity/value-objects/identity-id.vo.ts +0 -26
- package/src/domain/identity/value-objects/identity-provider.vo.ts +0 -13
- package/src/domain/identity/value-objects/index.ts +0 -8
- package/src/domain/identity/value-objects/risk-signal.vo.ts +0 -17
- package/src/domain/index.ts +0 -5
- package/src/domain/mfa/aggregates/mfa-session.aggregate.ts +0 -84
- package/src/domain/mfa/entities/mfa-challenge.entity.ts +0 -70
- package/src/domain/mfa/types/mfa-challenge-registry.ts +0 -21
- package/src/domain/mfa/value-objects/mfa-challenge-id.vo.ts +0 -19
- package/src/domain/mfa/value-objects/mfa-challenge-status.vo.ts +0 -31
- package/src/domain/mfa/value-objects/mfa-session-id.vo.ts +0 -19
- package/src/domain/mfa/violations/mfa-active-challenge-exists.violation.ts +0 -10
- package/src/domain/mfa/violations/mfa-already-verified.violation.ts +0 -10
- package/src/domain/mfa/violations/mfa-attempts-exceeded.violation.ts +0 -17
- package/src/domain/mfa/violations/mfa-expired.violation.ts +0 -10
- package/src/domain/oauth/aggregates/oauth-authorization.aggregate.ts +0 -106
- package/src/domain/oauth/aggregates/oauth-authorize.service.ts +0 -0
- package/src/domain/oauth/entities/oauth-authorization.entity.ts +0 -50
- package/src/domain/oauth/value-objects/authorization-code-id.vo.ts +0 -9
- package/src/domain/oauth/value-objects/authorization-code.vo.ts +0 -18
- package/src/domain/oauth/value-objects/client-id.vo.ts +0 -9
- package/src/domain/oauth/value-objects/code-challenge-method.vo.ts +0 -15
- package/src/domain/oauth/value-objects/code-challenge.vo.ts +0 -24
- package/src/domain/oauth/value-objects/oauth-authorization-id.vo.ts +0 -19
- package/src/domain/oauth/value-objects/oauth-provider.vo.ts +0 -15
- package/src/domain/oauth/value-objects/pkce.vo.ts +0 -29
- package/src/domain/oauth/value-objects/redirect-uri.vo.ts +0 -19
- package/src/domain/oauth/value-objects/scope-set.vo.ts +0 -37
- package/src/domain/oauth/violations/authorization-already-used.violation.ts +0 -10
- package/src/domain/oauth/violations/authorization-expired.violation.ts +0 -10
- package/src/domain/oauth/violations/consent-required.violation.ts +0 -10
- package/src/domain/oauth/violations/invalid-authorization-code.violation.ts +0 -12
- package/src/domain/oauth/violations/invalid-oauth-provider.violation.ts +0 -13
- package/src/domain/oauth/violations/invalid-pkce.violation.ts +0 -12
- package/src/domain/oauth/violations/invalid-redirect-uri.violation.ts +0 -10
- package/src/domain/policy/contracts/auth-policy-context.ts +0 -27
- package/src/domain/policy/contracts/auth-policy-decision.ts +0 -7
- package/src/domain/policy/contracts/auth-policy.ts +0 -17
- package/src/domain/policy/contracts/index.ts +0 -3
- package/src/domain/policy/engine/auth-policy-engine.ts +0 -41
- package/src/domain/policy/index.ts +0 -2
- package/src/domain/session/entities/session.entity.ts +0 -82
- package/src/domain/session/value-objects/session-id.vo.ts +0 -10
- package/src/domain/token/aggregates/token.aggregate.ts +0 -34
- package/src/domain/token/value-objects/auth-token.vo.ts +0 -29
- package/src/domain/token/value-objects/session-token.vo.ts +0 -14
- package/src/domain/violations/auth-domain.violation.ts +0 -9
- package/src/domain/violations/invalid-auth-token.violation.ts +0 -13
- package/src/domain/violations/invalid-scope.violation.ts +0 -10
- package/src/domain/violations/invalid-session.violation.ts +0 -13
- package/src/index.ts +0 -3
- package/src/ports/inbound/auth-method.port.ts +0 -18
- package/src/ports/inbound/index.ts +0 -2
- package/src/ports/inbound/start-auth.command.ts +0 -28
- package/src/ports/index.ts +0 -2
- package/src/ports/log/log.port.ts +0 -25
- package/src/ports/mfa/mfa-clock.port.ts +0 -11
- package/src/ports/mfa/mfa-session-id-generator.port.ts +0 -15
- package/src/ports/mfa/mfa-session-repository.port.ts +0 -31
- package/src/ports/observability/observability-event.port.ts +0 -16
- package/src/ports/outbound/authentication-attempt.repository.port.ts +0 -11
- package/src/ports/outbound/domain-event-publisher.port.ts +0 -13
- package/src/ports/outbound/index.ts +0 -2
- package/src/ports/outbound/session.repository.port.ts +0 -9
- package/src/ports/repositories/oauth-authorization.repository.ts +0 -21
- package/src/ports/repositories/token.repository.ts +0 -11
- package/src/types/auth-context.type.ts +0 -11
- package/src/types/auth-factor.type.ts +0 -10
- package/src/types/auth-method.type.ts +0 -20
- package/src/types/auth-policy.type.ts +0 -16
- package/src/types/identity-provider.type.ts +0 -8
- package/src/types/index.ts +0 -6
- package/src/types/observability-event.ts +0 -33
- package/src/types/risk-signal.type.ts +0 -11
- package/src/utils/default-if-blank.util.ts +0 -46
- package/tsconfig.build.json +0 -6
- package/tsconfig.json +0 -28
- package/tsup.config.ts +0 -13
- package/vitest.config.ts +0 -12
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registry for MFA challenge mechanisms.
|
|
3
|
-
*
|
|
4
|
-
* This registry is intentionally open for module augmentation.
|
|
5
|
-
* Each MFA-related package can extend it safely.
|
|
6
|
-
*/
|
|
7
|
-
export type MfaChallengeTypeRegistry = {
|
|
8
|
-
readonly authenticator_app: true;
|
|
9
|
-
readonly email: true;
|
|
10
|
-
readonly sms: true;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Union of all registered MFA challenge types.
|
|
15
|
-
*
|
|
16
|
-
* Provides:
|
|
17
|
-
* - IDE autocomplete
|
|
18
|
-
* - Type safety
|
|
19
|
-
* - Extension without modifying core
|
|
20
|
-
*/
|
|
21
|
-
export type MfaChallengeType = keyof MfaChallengeTypeRegistry;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
3
|
-
|
|
4
|
-
class InvalidMfaChallengeIdViolation extends AuthDomainViolation {
|
|
5
|
-
readonly code = 'MFA_CHALLENGE_ID_INVALID';
|
|
6
|
-
readonly message = 'MFA challenge ID is invalid';
|
|
7
|
-
|
|
8
|
-
static create(props: { value: string }) {
|
|
9
|
-
return new InvalidMfaChallengeIdViolation(props);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class MfaChallengeId extends PrimitiveValueObject<string> {
|
|
14
|
-
protected validate(value: string): void {
|
|
15
|
-
if (!value || value.length < 16) {
|
|
16
|
-
throw InvalidMfaChallengeIdViolation.create({ value });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
3
|
-
|
|
4
|
-
const AllowedStatuses = ['pending', 'verified', 'expired', 'failed'] as const;
|
|
5
|
-
|
|
6
|
-
type MfaChallengeStatusValue = (typeof AllowedStatuses)[number];
|
|
7
|
-
|
|
8
|
-
class InvalidMfaChallengeStatusViolation extends AuthDomainViolation {
|
|
9
|
-
readonly code = 'MFA_CHALLENGE_STATUS_INVALID';
|
|
10
|
-
readonly message = 'MFA challenge status is invalid';
|
|
11
|
-
|
|
12
|
-
static create(props: { value: MfaChallengeStatusValue }) {
|
|
13
|
-
return new InvalidMfaChallengeStatusViolation(props);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class MfaChallengeStatus extends PrimitiveValueObject<MfaChallengeStatusValue> {
|
|
18
|
-
protected validate(value: MfaChallengeStatusValue): void {
|
|
19
|
-
if (!AllowedStatuses.includes(value)) {
|
|
20
|
-
throw InvalidMfaChallengeStatusViolation.create({ value });
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static pending() {
|
|
25
|
-
return new MfaChallengeStatus('pending');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
static verified() {
|
|
29
|
-
return new MfaChallengeStatus('verified');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
3
|
-
|
|
4
|
-
class InvalidMfaSessionIdViolation extends AuthDomainViolation {
|
|
5
|
-
readonly code = 'MFA_SESSION_ID_INVALID';
|
|
6
|
-
readonly message = 'MFA session ID is invalid';
|
|
7
|
-
|
|
8
|
-
static create(props: { value: string }) {
|
|
9
|
-
return new InvalidMfaSessionIdViolation(props);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class MfaSessionId extends PrimitiveValueObject<string> {
|
|
14
|
-
protected validate(value: string): void {
|
|
15
|
-
if (!value || value.length < 16) {
|
|
16
|
-
throw InvalidMfaSessionIdViolation.create({ value });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class MfaActiveChallengeExistsViolation extends DomainViolation {
|
|
4
|
-
readonly code = 'MFA_ACTIVE_CHALLENGE_EXISTS';
|
|
5
|
-
readonly message = 'An active MFA challenge already exists';
|
|
6
|
-
|
|
7
|
-
static create(): MfaActiveChallengeExistsViolation {
|
|
8
|
-
return new MfaActiveChallengeExistsViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class MfaAlreadyVerifiedViolation extends DomainViolation {
|
|
4
|
-
readonly code = 'MFA_ALREADY_VERIFIED';
|
|
5
|
-
readonly message = 'MFA session is already verified';
|
|
6
|
-
|
|
7
|
-
static create(): MfaAlreadyVerifiedViolation {
|
|
8
|
-
return new MfaAlreadyVerifiedViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class MfaAttemptsExceededViolation extends DomainViolation {
|
|
4
|
-
readonly code = 'MFA_ATTEMPTS_EXCEEDED';
|
|
5
|
-
readonly message = 'Maximum MFA verification attempts exceeded';
|
|
6
|
-
|
|
7
|
-
protected constructor(attemptsUsed: number, maxAttempts: number) {
|
|
8
|
-
super({ attemptsUsed, maxAttempts });
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
static create(
|
|
12
|
-
attemptsUsed: number,
|
|
13
|
-
maxAttempts: number,
|
|
14
|
-
): MfaAttemptsExceededViolation {
|
|
15
|
-
return new MfaAttemptsExceededViolation(attemptsUsed, maxAttempts);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { DomainViolation } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class MfaExpiredViolation extends DomainViolation {
|
|
4
|
-
readonly code = 'MFA_EXPIRED';
|
|
5
|
-
readonly message = 'MFA challenge or session has expired';
|
|
6
|
-
|
|
7
|
-
static create() {
|
|
8
|
-
return new MfaExpiredViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import isNil from 'lodash.isnil';
|
|
2
|
-
|
|
3
|
-
import { defaultIfNilOrEmpty } from '@/utils/default-if-blank.util';
|
|
4
|
-
import { AggregateRoot, CreateEntityProps } from '@rineex/ddd';
|
|
5
|
-
import { IdentityId } from '@/domain/identity';
|
|
6
|
-
|
|
7
|
-
import { AuthorizationAlreadyUsedViolation } from '../violations/authorization-already-used.violation';
|
|
8
|
-
import { AuthorizationExpiredViolation } from '../violations/authorization-expired.violation';
|
|
9
|
-
import { ConsentRequiredViolation } from '../violations/consent-required.violation';
|
|
10
|
-
import { OAuthAuthorizationId } from '../value-objects/oauth-authorization-id.vo';
|
|
11
|
-
import { AuthorizationCode } from '../value-objects/authorization-code.vo';
|
|
12
|
-
import { CodeChallenge } from '../value-objects/code-challenge.vo';
|
|
13
|
-
import { RedirectUri } from '../value-objects/redirect-uri.vo';
|
|
14
|
-
import { ClientId } from '../value-objects/client-id.vo';
|
|
15
|
-
import { ScopeSet } from '../value-objects/scope-set.vo';
|
|
16
|
-
|
|
17
|
-
export interface OAuthAuthorizationProps extends CreateEntityProps<OAuthAuthorizationId> {
|
|
18
|
-
readonly identityId: IdentityId;
|
|
19
|
-
readonly clientId: ClientId;
|
|
20
|
-
readonly redirectUri: RedirectUri;
|
|
21
|
-
readonly scopes: ScopeSet;
|
|
22
|
-
readonly codeChallenge?: CodeChallenge;
|
|
23
|
-
|
|
24
|
-
consentGrantedAt?: Date;
|
|
25
|
-
authorizationCode?: AuthorizationCode;
|
|
26
|
-
readonly expiresAt: Date;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class OauthAuthorization extends AggregateRoot<OAuthAuthorizationId> {
|
|
30
|
-
protected constructor(private props: OAuthAuthorizationProps) {
|
|
31
|
-
super(props);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
validate() {
|
|
35
|
-
if (this.props.expiresAt.getTime() <= Date.now()) {
|
|
36
|
-
throw AuthorizationExpiredViolation.create();
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
issueAuthorizationCode(code: AuthorizationCode): void {
|
|
41
|
-
if (this.props.authorizationCode) {
|
|
42
|
-
throw AuthorizationAlreadyUsedViolation.create();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (this.requiresConsent()) {
|
|
46
|
-
throw ConsentRequiredViolation.create();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
this.props.authorizationCode = code;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
isExpired(now: Date): boolean {
|
|
53
|
-
return now > this.props.expiresAt;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
grantConsent(now: Date): void {
|
|
57
|
-
if (!this.requiresConsent()) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.mutate(draft => {
|
|
62
|
-
draft.props.consentGrantedAt = now;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
this.props.consentGrantedAt = now;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
protected snapshot(): Record<string, unknown> {
|
|
69
|
-
return {
|
|
70
|
-
consentGrantedAt: this.props.consentGrantedAt?.toISOString() ?? null,
|
|
71
|
-
authorizationCode: this.props.authorizationCode?.toString() ?? null,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
protected restore(snapshot: Record<string, unknown>): void {
|
|
76
|
-
this.props.consentGrantedAt = snapshot.consentGrantedAt
|
|
77
|
-
? new Date(snapshot.consentGrantedAt as string)
|
|
78
|
-
: undefined;
|
|
79
|
-
|
|
80
|
-
this.props.authorizationCode = snapshot.authorizationCode
|
|
81
|
-
? AuthorizationCode.create(snapshot.authorizationCode as string)
|
|
82
|
-
: undefined; // Assume fromString exists
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
requiresConsent(): boolean {
|
|
86
|
-
return isNil(this.props.consentGrantedAt);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
toObject() {
|
|
90
|
-
return {
|
|
91
|
-
consentGrantedAt: defaultIfNilOrEmpty(
|
|
92
|
-
this.props.consentGrantedAt?.toISOString(),
|
|
93
|
-
),
|
|
94
|
-
authorizationCode: defaultIfNilOrEmpty(
|
|
95
|
-
this.props.authorizationCode?.toString(),
|
|
96
|
-
),
|
|
97
|
-
codeChallenge: defaultIfNilOrEmpty(this.props.codeChallenge?.toString()),
|
|
98
|
-
redirectUri: this.props.redirectUri.toString(),
|
|
99
|
-
expiresAt: this.props.expiresAt.toISOString(),
|
|
100
|
-
identityId: this.props.identityId.toString(),
|
|
101
|
-
scopes: this.props.scopes.toStringArray(),
|
|
102
|
-
clientId: this.props.clientId.toString(),
|
|
103
|
-
id: this.id.toString(),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
File without changes
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { CreateEntityProps, Entity } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { InvalidRedirectUriViolation } from '../violations/invalid-redirect-uri.violation';
|
|
4
|
-
import { OAuthAuthorizationId } from '../value-objects/oauth-authorization-id.vo';
|
|
5
|
-
import { OAuthProvider } from '../value-objects/oauth-provider.vo';
|
|
6
|
-
import { Pkce } from '../value-objects/pkce.vo';
|
|
7
|
-
|
|
8
|
-
export interface OAuthAuthorizationProps extends CreateEntityProps<OAuthAuthorizationId> {
|
|
9
|
-
provider: OAuthProvider;
|
|
10
|
-
redirectUri: string;
|
|
11
|
-
scope: readonly string[];
|
|
12
|
-
pkce?: Pkce;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class OAuthAuthorization extends Entity<OAuthAuthorizationId> {
|
|
16
|
-
constructor(public props: OAuthAuthorizationProps) {
|
|
17
|
-
super(props);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
toObject(): Record<string, unknown> {
|
|
21
|
-
return {
|
|
22
|
-
pkce: this.props.pkce ? this.props.pkce.toJSON() : undefined,
|
|
23
|
-
redirectUri: this.props.redirectUri,
|
|
24
|
-
provider: this.props.provider,
|
|
25
|
-
scope: this.props.scope,
|
|
26
|
-
id: this.id.getValue(),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
protected snapshot(): Record<string, unknown> {
|
|
31
|
-
return this.toObject();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
protected restore(snapshot: Record<string, unknown>): void {
|
|
35
|
-
this.props.pkce = snapshot.pkce
|
|
36
|
-
? Pkce.fromJSON(snapshot.pkce as Record<string, unknown>)
|
|
37
|
-
: undefined;
|
|
38
|
-
this.props.redirectUri = snapshot.redirectUri as string;
|
|
39
|
-
this.props.provider = snapshot.provider as OAuthProvider;
|
|
40
|
-
this.props.scope = snapshot.scope as string[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
validate(): void {
|
|
44
|
-
if (!this.props.redirectUri.startsWith('https://')) {
|
|
45
|
-
throw InvalidRedirectUriViolation.create({
|
|
46
|
-
redirectUri: this.props.redirectUri,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class AuthorizationCodeId extends PrimitiveValueObject<string> {
|
|
4
|
-
protected validate(value: string): void {
|
|
5
|
-
if (!value || value.length < 32) {
|
|
6
|
-
throw new Error('Invalid authorization code');
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { InvalidAuthorizationCodeViolation } from '../violations/invalid-authorization-code.violation';
|
|
4
|
-
|
|
5
|
-
export class AuthorizationCode extends PrimitiveValueObject<string> {
|
|
6
|
-
protected validate(value: string): void {
|
|
7
|
-
if (value.length < 8) {
|
|
8
|
-
throw InvalidAuthorizationCodeViolation.create({
|
|
9
|
-
actualLength: value.length,
|
|
10
|
-
minLength: 8,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public static create(value: string): AuthorizationCode {
|
|
16
|
-
return new AuthorizationCode(value);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export type CodeChallengeMethodValue = 'plain' | 'S256';
|
|
4
|
-
|
|
5
|
-
export class CodeChallengeMethod extends PrimitiveValueObject<CodeChallengeMethodValue> {
|
|
6
|
-
protected validate(value: CodeChallengeMethodValue): void {
|
|
7
|
-
if (value !== 'plain' && value !== 'S256') {
|
|
8
|
-
throw new Error('Unsupported code challenge method');
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
static create(value: CodeChallengeMethodValue): CodeChallengeMethod {
|
|
13
|
-
return new CodeChallengeMethod(value);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { CodeChallengeMethod } from './code-challenge-method.vo';
|
|
4
|
-
|
|
5
|
-
export type CodeChallengeProps = {
|
|
6
|
-
readonly value: string;
|
|
7
|
-
readonly method: CodeChallengeMethod;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export class CodeChallenge extends ValueObject<CodeChallengeProps> {
|
|
11
|
-
protected validate(props: CodeChallengeProps): void {
|
|
12
|
-
if (!props.value || props.value.length < 32) {
|
|
13
|
-
throw new Error('Invalid code challenge value');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
get method(): CodeChallengeMethod {
|
|
18
|
-
return this.props.method;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static create(value: string, method: CodeChallengeMethod): CodeChallenge {
|
|
22
|
-
return new CodeChallenge({ method, value });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
3
|
-
|
|
4
|
-
class InvalidOAuthAuthorizationIdViolation extends AuthDomainViolation {
|
|
5
|
-
readonly code = 'OAUTH_AUTHORIZATION_ID_INVALID';
|
|
6
|
-
readonly message = 'OAuthAuthorization ID is invalid';
|
|
7
|
-
|
|
8
|
-
public static create(props: { value: string }) {
|
|
9
|
-
return new InvalidOAuthAuthorizationIdViolation(props);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class OAuthAuthorizationId extends PrimitiveValueObject<string> {
|
|
14
|
-
protected validate(value: string): void {
|
|
15
|
-
if (!value || value.length < 16) {
|
|
16
|
-
throw InvalidOAuthAuthorizationIdViolation.create({ value });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { InvalidOAuthProviderViolation } from '../violations/invalid-oauth-provider.violation';
|
|
4
|
-
|
|
5
|
-
export class OAuthProvider extends PrimitiveValueObject<string> {
|
|
6
|
-
protected validate(value: string): void {
|
|
7
|
-
if (!value || value.length < 2) {
|
|
8
|
-
throw InvalidOAuthProviderViolation.create({ value });
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public static create(value: string): OAuthProvider {
|
|
13
|
-
return new OAuthProvider(value);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { ValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { InvalidPkceViolation } from '../violations/invalid-pkce.violation';
|
|
4
|
-
|
|
5
|
-
export type PkceProps = {
|
|
6
|
-
readonly codeVerifier: string;
|
|
7
|
-
readonly codeChallenge: string;
|
|
8
|
-
readonly method: 'plain' | 'S256';
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export class Pkce extends ValueObject<PkceProps> {
|
|
12
|
-
protected validate(props: PkceProps): void {
|
|
13
|
-
if (!props.codeVerifier || !props.codeChallenge) {
|
|
14
|
-
throw InvalidPkceViolation.create(props);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public static create(props: PkceProps): Pkce {
|
|
19
|
-
return new Pkce(props);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public static fromJSON(json: Record<string, unknown>): Pkce {
|
|
23
|
-
return new Pkce({
|
|
24
|
-
codeChallenge: json.codeChallenge as string,
|
|
25
|
-
codeVerifier: json.codeVerifier as string,
|
|
26
|
-
method: json.method as 'plain' | 'S256',
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
import { InvalidRedirectUriViolation } from '../violations/invalid-redirect-uri.violation';
|
|
4
|
-
|
|
5
|
-
export class RedirectUri extends PrimitiveValueObject<string> {
|
|
6
|
-
protected validate(url: string): void {
|
|
7
|
-
let parsed: URL;
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
parsed = new URL(url);
|
|
11
|
-
} catch {
|
|
12
|
-
throw InvalidRedirectUriViolation.create({ redirectUri: url });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (parsed.protocol !== 'https:' || parsed.hash) {
|
|
16
|
-
throw InvalidRedirectUriViolation.create({ redirectUri: url });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { PrimitiveValueObject, ValueObject } from '@rineex/ddd';
|
|
2
|
-
|
|
3
|
-
export class Scope extends PrimitiveValueObject<string> {
|
|
4
|
-
protected validate(value: string): void {
|
|
5
|
-
if (!/^[-0-:_a-z]+$/.test(value)) {
|
|
6
|
-
throw new Error('Invalid scope format');
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
static create(scope: string): Scope {
|
|
11
|
-
return new Scope(scope);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class ScopeSet extends ValueObject<ReadonlySet<Scope>> {
|
|
16
|
-
public static create() {
|
|
17
|
-
return new ScopeSet(new Set<Scope>());
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
protected validate(value: ReadonlySet<Scope>): void {
|
|
21
|
-
if (value.size === 0) {
|
|
22
|
-
throw new Error('At least one scope is required');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static fromStrings(scopes: readonly string[]): ScopeSet {
|
|
27
|
-
return new ScopeSet(new Set(scopes.map(scope => Scope.create(scope))));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
has(scope: Scope): boolean {
|
|
31
|
-
return [...this.props].some(s => s.equals(scope));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
toStringArray(): string[] {
|
|
35
|
-
return [...this.props].map(s => s.getValue());
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class AuthorizationAlreadyUsedViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'oauth.authorization.already_used';
|
|
5
|
-
readonly message = 'The OAuth authorization has already been used';
|
|
6
|
-
|
|
7
|
-
public static create(): AuthorizationAlreadyUsedViolation {
|
|
8
|
-
return new AuthorizationAlreadyUsedViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class AuthorizationExpiredViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'oauth.authorization.expired';
|
|
5
|
-
readonly message = 'The OAuth authorization has expired';
|
|
6
|
-
|
|
7
|
-
public static create(): AuthorizationExpiredViolation {
|
|
8
|
-
return new AuthorizationExpiredViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class ConsentRequiredViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'oauth.consent.required';
|
|
5
|
-
readonly message = 'User consent is required for this OAuth authorization';
|
|
6
|
-
|
|
7
|
-
public static create(): ConsentRequiredViolation {
|
|
8
|
-
return new ConsentRequiredViolation();
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class InvalidAuthorizationCodeViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'AUTH_CODE_INVALID';
|
|
5
|
-
readonly message = 'Authorization code is invalid';
|
|
6
|
-
|
|
7
|
-
public static create(
|
|
8
|
-
details?: Record<string, unknown>,
|
|
9
|
-
): InvalidAuthorizationCodeViolation {
|
|
10
|
-
return new InvalidAuthorizationCodeViolation({ details });
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class InvalidOAuthProviderViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'OAUTH_PROVIDER_INVALID';
|
|
5
|
-
readonly message = 'OAuth provider identifier is invalid';
|
|
6
|
-
|
|
7
|
-
public static create(props: { value: string }) {
|
|
8
|
-
return new InvalidOAuthProviderViolation({
|
|
9
|
-
...props,
|
|
10
|
-
message: `OAuth provider identifier "${props.value}" is invalid`,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class InvalidPkceViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'PKCE_INVALID';
|
|
5
|
-
readonly message = 'PKCE parameters are invalid';
|
|
6
|
-
|
|
7
|
-
public static create(
|
|
8
|
-
details?: Record<string, unknown>,
|
|
9
|
-
): InvalidPkceViolation {
|
|
10
|
-
return new InvalidPkceViolation({ details });
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { AuthDomainViolation } from '@/domain/violations/auth-domain.violation';
|
|
2
|
-
|
|
3
|
-
export class InvalidRedirectUriViolation extends AuthDomainViolation {
|
|
4
|
-
readonly code = 'OAUTH_REDIRECT_URI_INVALID';
|
|
5
|
-
readonly message = 'Redirect URI is invalid or insecure';
|
|
6
|
-
|
|
7
|
-
public static create(props: { redirectUri: string }) {
|
|
8
|
-
return new InvalidRedirectUriViolation(props);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { AuthMethod, IdentityId } from '@/domain/identity/value-objects';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Immutable context passed to all authentication policies.
|
|
5
|
-
* Represents facts, not decisions.
|
|
6
|
-
*/
|
|
7
|
-
export type AuthPolicyContext = Readonly<{
|
|
8
|
-
readonly identityId?: IdentityId;
|
|
9
|
-
readonly method: AuthMethod;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Pre-calculated risk score (0-100).
|
|
13
|
-
* Produced by an upstream adapter (not domain logic).
|
|
14
|
-
*/
|
|
15
|
-
readonly riskScore?: number;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Whether the identity is already known to be blocked.
|
|
19
|
-
*/
|
|
20
|
-
readonly isBlocked?: boolean;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* True if this authentication attempt is coming
|
|
24
|
-
* from a previously trusted environment.
|
|
25
|
-
*/
|
|
26
|
-
readonly isTrustedDevice?: boolean;
|
|
27
|
-
}>;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { AuthPolicyDecision } from './auth-policy-decision';
|
|
2
|
-
import { AuthPolicyContext } from './auth-policy-context';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Authentication policy contract.
|
|
6
|
-
* A policy evaluates context and returns a partial decision.
|
|
7
|
-
*/
|
|
8
|
-
export abstract class AuthPolicyEvaluator {
|
|
9
|
-
/**
|
|
10
|
-
* Evaluates the policy against the given context.
|
|
11
|
-
*
|
|
12
|
-
* Returning `null` means:
|
|
13
|
-
* - this policy is not applicable
|
|
14
|
-
* - it has no opinion
|
|
15
|
-
*/
|
|
16
|
-
abstract evaluate(context: AuthPolicyContext): AuthPolicyDecision | null;
|
|
17
|
-
}
|