ofauth-shared-core 0.1.0-alpha.1 → 0.2.0-alpha.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/README.md +10 -0
- package/dist/OfauthCore.d.ts +0 -1
- package/dist/contracts/AuthAuditContract.d.ts +6 -0
- package/dist/contracts/AuthErrorContract.d.ts +1 -1
- package/dist/contracts/AuthPolicyContract.d.ts +13 -0
- package/dist/contracts/ContextContract.d.ts +23 -4
- package/dist/contracts/IdentityContract.d.ts +14 -0
- package/dist/contracts/SessionContract.d.ts +20 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/services/AuthAuthorityAdapter.d.ts +42 -0
- package/dist/services/AuthHostComposition.d.ts +5 -2
- package/dist/services/AuthOfflinePolicy.d.ts +11 -0
- package/dist/services/AuthSessionCacheAdapter.d.ts +16 -0
- package/dist/services/contractCompatibility.d.ts +12 -0
- package/dist/services/createContractOnlyOfauthServices.d.ts +8 -8
- package/dist/services/createDbAdapterOfauthServices.d.ts +0 -8
- package/dist/services/createServerAuthoritativeAuthBridge.d.ts +25 -0
- package/dist/services/errors.d.ts +10 -0
- package/dist/services/impl/DbAdapterSessionService.d.ts +2 -0
- package/dist/services/impl/runtimeSupport.d.ts +1 -1
- package/dist/services/localProvisioning.d.ts +23 -0
- package/dist/services/responseEnvelope.d.ts +10 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
- auth audit contract,
|
|
9
9
|
- runtime entrypoint `OfauthCore`.
|
|
10
10
|
|
|
11
|
+
## Server-Authoritative Offline Continuity
|
|
12
|
+
- Authority login utama: adapter server (`AuthAuthorityAdapter`).
|
|
13
|
+
- Offline login: hanya untuk identity yang pernah online-success di device yang sama (`AuthSessionCacheAdapter` + `AuthOfflinePolicy`).
|
|
14
|
+
- Boundary: domain-agnostic; mapping model domain host tetap di layer host/adapter.
|
|
15
|
+
- Pemilihan assignment/context aktif adalah tanggung jawab consumer domain (host/service), bukan authority endpoint khusus context-switch.
|
|
16
|
+
- Entrypoint bridge: `createServerAuthoritativeAuthBridge(...)`.
|
|
17
|
+
|
|
11
18
|
## Build and Validate
|
|
12
19
|
- `npm run build`
|
|
13
20
|
- `npm run verify:contract`
|
|
@@ -18,3 +25,6 @@
|
|
|
18
25
|
1. `npm run ci:check`
|
|
19
26
|
2. `npm pack --dry-run`
|
|
20
27
|
3. `npm publish --access public`
|
|
28
|
+
|
|
29
|
+
## Roadmap
|
|
30
|
+
- `docs/01-SERVER-AUTHORITATIVE-OFFLINE-CONTINUITY-ROADMAP-V1.md`
|
package/dist/OfauthCore.d.ts
CHANGED
|
@@ -43,6 +43,5 @@ export declare class OfauthCoreBuilder {
|
|
|
43
43
|
overrideDomainServices(overrides: OfauthDomainServicesOverride): this;
|
|
44
44
|
withRuntimeHooks(hooks: Partial<OfauthRuntimeHooks>): this;
|
|
45
45
|
withMigrationRunner(runner: NonNullable<OfauthRuntimeHooks['runMigrations']>): this;
|
|
46
|
-
withSeedRunner(runner: NonNullable<OfauthRuntimeHooks['runSeed']>): this;
|
|
47
46
|
build(): OfauthCore;
|
|
48
47
|
}
|
|
@@ -22,4 +22,10 @@ export interface AuthAuditQuery {
|
|
|
22
22
|
export interface AuthAuditReplayQuery {
|
|
23
23
|
limit?: number;
|
|
24
24
|
}
|
|
25
|
+
export interface AuthAuditServiceContract {
|
|
26
|
+
appendEvent(event: AuthAuditEvent): Promise<void>;
|
|
27
|
+
queryEvents(query: AuthAuditQuery): Promise<AuthAuditEvent[]>;
|
|
28
|
+
listPendingReplay(query?: AuthAuditReplayQuery): Promise<AuthAuditEvent[]>;
|
|
29
|
+
markReplayed(eventIds: string[], replayedAt?: string): Promise<number>;
|
|
30
|
+
}
|
|
25
31
|
export declare function isAuthAuditEventType(value: string): value is AuthAuditEventType;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ErrorEnvelope } from 'ofcore';
|
|
2
|
-
export type AuthErrorCode = 'AUTH_INVALID_CREDENTIAL' | 'AUTH_LOCKED' | 'AUTH_DISABLED' | 'AUTH_SESSION_NOT_FOUND' | 'AUTH_STEP_UP_REQUIRED' | 'AUTH_CONTEXT_FORBIDDEN' | 'AUTH_NOT_IMPLEMENTED';
|
|
2
|
+
export type AuthErrorCode = 'AUTH_INVALID_CREDENTIAL' | 'AUTH_LOCKED' | 'AUTH_DISABLED' | 'AUTH_SESSION_NOT_FOUND' | 'AUTH_STEP_UP_REQUIRED' | 'AUTH_CONTEXT_FORBIDDEN' | 'AUTH_CONTEXT_INVALID' | 'AUTH_NOT_IMPLEMENTED';
|
|
3
3
|
export interface AuthErrorEnvelope extends ErrorEnvelope {
|
|
4
4
|
code: AuthErrorCode;
|
|
5
5
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResponseEnvelope } from 'ofcore';
|
|
1
2
|
import type { AssuranceLevel } from './SessionContract';
|
|
2
3
|
export type AuthProfileId = 'cashier_fast' | 'supervisor_strict' | 'member_self_service';
|
|
3
4
|
export interface AuthProfile {
|
|
@@ -23,3 +24,15 @@ export interface AuthPolicySnapshot {
|
|
|
23
24
|
version: string;
|
|
24
25
|
updatedAt: string;
|
|
25
26
|
}
|
|
27
|
+
export interface AuthStepUpEnforceResult {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
actionRef: string;
|
|
30
|
+
enforced: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface AuthPolicyServiceContractV2 {
|
|
33
|
+
getPolicySnapshot(): Promise<ResponseEnvelope<AuthPolicySnapshot>>;
|
|
34
|
+
getStepUpRequirement(actionRef: string): Promise<ResponseEnvelope<StepUpRequirement | null>>;
|
|
35
|
+
evaluateStepUp(sessionId: string, actionRef: string): Promise<ResponseEnvelope<StepUpEvaluation>>;
|
|
36
|
+
enforceStepUp(sessionId: string, actionRef: string): Promise<ResponseEnvelope<AuthStepUpEnforceResult>>;
|
|
37
|
+
markStepUpPassed(sessionId: string, actionRef: string): Promise<ResponseEnvelope<import('./SessionContract').SessionRef>>;
|
|
38
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { ResponseEnvelope, ScopeRef } from 'ofcore';
|
|
2
|
+
/**
|
|
3
|
+
* Scope reference untuk auth domain — extends ScopeRef dari ofcore.
|
|
4
|
+
* Tambahan: `attributes` untuk arbitrary key-value scope context
|
|
5
|
+
* (misalnya koperasi tier, subsidiary id, dsb).
|
|
6
|
+
*/
|
|
7
|
+
export interface AuthScopeRef extends ScopeRef {
|
|
4
8
|
attributes?: Record<string, string>;
|
|
5
9
|
}
|
|
6
10
|
export interface ContextAssignment {
|
|
@@ -16,7 +20,17 @@ export interface ActiveContext {
|
|
|
16
20
|
}
|
|
17
21
|
export interface ContextSwitchRequest {
|
|
18
22
|
sessionId: string;
|
|
19
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Ganti primary focus ke assignment tertentu (tidak mengubah grantedAssignments).
|
|
25
|
+
* Wajib diisi jika `targetTenantId` tidak diberikan.
|
|
26
|
+
*/
|
|
27
|
+
targetAssignmentId?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Ganti seluruh tenant context: grantedAssignments diganti ke semua assignment
|
|
30
|
+
* dalam tenant target, dan activeScopeRef diset ke assignment primary di tenant itu.
|
|
31
|
+
* Wajib diisi jika `targetAssignmentId` tidak diberikan.
|
|
32
|
+
*/
|
|
33
|
+
targetTenantId?: string;
|
|
20
34
|
reasonCode?: string;
|
|
21
35
|
}
|
|
22
36
|
export interface ContextSwitchResult {
|
|
@@ -25,3 +39,8 @@ export interface ContextSwitchResult {
|
|
|
25
39
|
reasonCode?: string;
|
|
26
40
|
}
|
|
27
41
|
export declare function hasAnyScopeDimension(scope: AuthScopeRef): boolean;
|
|
42
|
+
export interface ContextServiceContractV2 {
|
|
43
|
+
listAssignments(identityId: string): Promise<ResponseEnvelope<ContextAssignment[]>>;
|
|
44
|
+
getActiveContext(sessionId: string): Promise<ResponseEnvelope<ActiveContext | null>>;
|
|
45
|
+
switchContext(request: ContextSwitchRequest): Promise<ResponseEnvelope<ContextSwitchResult>>;
|
|
46
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResponseEnvelope } from 'ofcore';
|
|
1
2
|
import type { AuthScopeRef, ContextAssignment } from './ContextContract';
|
|
2
3
|
export type IdentityStatus = 'active' | 'disabled' | 'locked';
|
|
3
4
|
export type CredentialVerifyMethod = 'pin' | 'password' | 'custom';
|
|
@@ -34,3 +35,16 @@ export interface CredentialVerifyFailure {
|
|
|
34
35
|
retryable: boolean;
|
|
35
36
|
}
|
|
36
37
|
export type CredentialVerifyResult = CredentialVerifySuccess | CredentialVerifyFailure;
|
|
38
|
+
export interface IdentityLoginResult {
|
|
39
|
+
identity: IdentityRef;
|
|
40
|
+
sessionId: string;
|
|
41
|
+
}
|
|
42
|
+
export interface IdentityLogoutResult {
|
|
43
|
+
sessionId: string;
|
|
44
|
+
revoked: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface IdentityServiceContractV2 {
|
|
47
|
+
verifyCredential(request: CredentialVerifyRequest): Promise<ResponseEnvelope<CredentialVerifyResult>>;
|
|
48
|
+
login(principal: string, secret: string, scopeRef?: AuthScopeRef): Promise<ResponseEnvelope<IdentityLoginResult>>;
|
|
49
|
+
logout(sessionId: string): Promise<ResponseEnvelope<IdentityLogoutResult>>;
|
|
50
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ResponseEnvelope } from 'ofcore';
|
|
2
|
+
import type { AuthScopeRef, ContextAssignment } from './ContextContract';
|
|
2
3
|
export type AssuranceLevel = 'basic' | 'elevated';
|
|
3
4
|
export interface SessionRef {
|
|
4
5
|
sessionId: string;
|
|
5
6
|
identityId: string;
|
|
7
|
+
/** Primary focus assignment saat ini. Backward-compat untuk service single-domain. */
|
|
6
8
|
activeScopeRef?: AuthScopeRef;
|
|
9
|
+
/**
|
|
10
|
+
* Semua assignment yang di-grant untuk sesi ini (Fase 2).
|
|
11
|
+
* Diisi otomatis: single tenant → semua assignment dalam tenant tersebut;
|
|
12
|
+
* multi-tenant → hanya assignment dari tenant yang dipilih.
|
|
13
|
+
* Selalu array (kosong jika belum diisi / sesi lama). Tidak pernah null.
|
|
14
|
+
*/
|
|
15
|
+
grantedAssignments?: ContextAssignment[];
|
|
7
16
|
assuranceLevel: AssuranceLevel;
|
|
8
17
|
issuedAt: string;
|
|
9
18
|
expiresAt: string;
|
|
@@ -23,3 +32,13 @@ export interface RevokeSessionRequest {
|
|
|
23
32
|
reasonCode?: string;
|
|
24
33
|
}
|
|
25
34
|
export declare function isAssuranceLevelAtLeast(current: AssuranceLevel, required: AssuranceLevel): boolean;
|
|
35
|
+
export interface SessionRevokeResult {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
revoked: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface SessionServiceContractV2 {
|
|
40
|
+
createSession(request: CreateSessionRequest): Promise<ResponseEnvelope<SessionRef>>;
|
|
41
|
+
refreshSession(request: RefreshSessionRequest): Promise<ResponseEnvelope<SessionRef>>;
|
|
42
|
+
revokeSession(request: RevokeSessionRequest): Promise<ResponseEnvelope<SessionRevokeResult>>;
|
|
43
|
+
getActiveSession(sessionId: string): Promise<ResponseEnvelope<SessionRef | null>>;
|
|
44
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,14 +4,16 @@ export * from './contracts/SessionContract';
|
|
|
4
4
|
export * from './contracts/AuthPolicyContract';
|
|
5
5
|
export * from './contracts/AuthAuditContract';
|
|
6
6
|
export * from './contracts/AuthErrorContract';
|
|
7
|
-
export * from './services/IdentityService';
|
|
8
|
-
export * from './services/SessionService';
|
|
9
|
-
export * from './services/ContextService';
|
|
10
7
|
export * from './services/AuthHostComposition';
|
|
11
|
-
export * from './services/AuthPolicyService';
|
|
12
8
|
export * from './services/AuthAuditService';
|
|
9
|
+
export * from './services/AuthAuthorityAdapter';
|
|
10
|
+
export * from './services/AuthSessionCacheAdapter';
|
|
11
|
+
export * from './services/AuthOfflinePolicy';
|
|
12
|
+
export * from './services/createServerAuthoritativeAuthBridge';
|
|
13
13
|
export * from './services/createContractOnlyOfauthServices';
|
|
14
14
|
export * from './services/createDbAdapterOfauthServices';
|
|
15
|
+
export * from './services/localProvisioning';
|
|
16
|
+
export * from './services/responseEnvelope';
|
|
15
17
|
export * from './services/errors';
|
|
16
18
|
export * from './runtime/ContractStage';
|
|
17
19
|
export * from './OfauthCore';
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function ee(r){return!!(r.tenantId||r.branchId||r.attributes&&Object.keys(r.attributes).length>0)}function I(r,e){let t={basic:1,elevated:2};return t[r]>=t[e]}function ne(r){return["LOGIN_SUCCESS","LOGIN_FAILED","LOCKOUT_TRIGGERED","SESSION_REVOKED","CONTEXT_SWITCHED","STEP_UP_CHALLENGED","STEP_UP_PASSED","STEP_UP_FAILED"].includes(r)}function $(r,e){if(e)return r.find(t=>t.assignmentId===e)}function Q(r,e){var t;if(e.requiredTenantId&&r.tenantId!==e.requiredTenantId||e.requiredBranchId&&r.branchId!==e.requiredBranchId)return!1;if(e.requiredScopeAttributes){let i=(t=r.attributes)!=null?t:{};for(let[n,s]of Object.entries(e.requiredScopeAttributes))if(i[n]!==s)return!1}return!0}async function O(r,e){var a,d,u;let t=await r.sessionService.getActiveSession(e.sessionId);if(!t)return null;let i=await r.contextService.listAssignments(e.identityId),n=await r.contextService.getActiveContext(e.sessionId);!n&&i[0]&&(n=(await r.contextService.switchContext({sessionId:e.sessionId,targetAssignmentId:i[0].assignmentId,...e.assignmentFallbackReason?{reasonCode:e.assignmentFallbackReason}:{}})).activeContext);let s=$(i,n==null?void 0:n.assignmentId);return{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,assignments:i,activeContextAssignmentId:n==null?void 0:n.assignmentId,activeAssignment:s,scopeRef:(u=(d=(a=n==null?void 0:n.scopeRef)!=null?a:s==null?void 0:s.scopeRef)!=null?d:t.activeScopeRef)!=null?u:{},roleRef:s==null?void 0:s.roleRef,assuranceLevel:t.assuranceLevel}}async function oe(r,e){let t=await r.identityService.login(e),i=await O(r,{sessionId:t.sessionId,identityId:t.identity.identityId,principal:t.identity.principal,assignmentFallbackReason:"AUTO_CONTEXT_AFTER_LOGIN"});if(!i)throw new Error("session is not active after login");return i}async function ae(r,e){return O(r,e)}async function de(r,e){await r.contextService.switchContext({sessionId:e.sessionId,targetAssignmentId:e.targetAssignmentId,...e.reasonCode?{reasonCode:e.reasonCode}:{}});let t=await O(r,e);if(!t)throw new Error("session is not active while switching context");return t}function ce(r,e={}){var i,n;if(!r)return{allowed:!1,reasonCode:"NO_SESSION"};let t=(i=e.minimumAssuranceLevel)!=null?i:"basic";return I(r.assuranceLevel,t)?(n=e.requiredRoleRefs)!=null&&n.length&&!(!!r.roleRef&&e.requiredRoleRefs.includes(r.roleRef))?{allowed:!1,reasonCode:"ROLE_FORBIDDEN"}:Q(r.scopeRef,e)?{allowed:!0}:{allowed:!1,reasonCode:"SCOPE_FORBIDDEN"}:{allowed:!1,reasonCode:"ASSURANCE_TOO_LOW"}}function p(r){throw{code:"AUTH_NOT_IMPLEMENTED",message:r,retryable:!1}}function pe(){return{identityService:{async verifyCredential(){return p("identityService.verifyCredential not implemented yet")},async login(){return p("identityService.login not implemented yet")},async logout(){return p("identityService.logout not implemented yet")}},sessionService:{async createSession(){return p("sessionService.createSession not implemented yet")},async refreshSession(){return p("sessionService.refreshSession not implemented yet")},async revokeSession(){return p("sessionService.revokeSession not implemented yet")},async getActiveSession(){return p("sessionService.getActiveSession not implemented yet")}},contextService:{async listAssignments(){return p("contextService.listAssignments not implemented yet")},async getActiveContext(){return p("contextService.getActiveContext not implemented yet")},async switchContext(){return p("contextService.switchContext not implemented yet")}},authPolicyService:{async getPolicySnapshot(){return p("authPolicyService.getPolicySnapshot not implemented yet")},async getStepUpRequirement(){return p("authPolicyService.getStepUpRequirement not implemented yet")},async evaluateStepUp(){return p("authPolicyService.evaluateStepUp not implemented yet")},async enforceStepUp(){return p("authPolicyService.enforceStepUp not implemented yet")},async markStepUpPassed(){return p("authPolicyService.markStepUpPassed not implemented yet")}},authAuditService:{async appendEvent(){return p("authAuditService.appendEvent not implemented yet")},async queryEvents(){return p("authAuditService.queryEvents not implemented yet")},async listPendingReplay(){return p("authAuditService.listPendingReplay not implemented yet")},async markReplayed(){return p("authAuditService.markReplayed not implemented yet")}}}}var o={identities:"ofauth_identities",assignments:"ofauth_assignments",sessions:"ofauth_sessions",policyProfiles:"ofauth_policy_profiles",policyStepups:"ofauth_policy_stepups",auditEvents:"ofauth_audit_events"},Y=[{name:o.identities,columns:[{name:"id",type:"string"},{name:"principal",type:"string",isIndexed:!0},{name:"secretHash",type:"string"},{name:"verifyMethod",type:"string",enum:["pin","password","custom"]},{name:"status",type:"string",enum:["active","disabled","locked"],isIndexed:!0},{name:"failedAttempts",type:"number"},{name:"lockoutUntil",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.assignments,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"roleRef",type:"string",isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.sessions,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"activeAssignmentId",type:"string",isOptional:!0,isIndexed:!0},{name:"assuranceLevel",type:"string",enum:["basic","elevated"],isIndexed:!0},{name:"issuedAt",type:"string"},{name:"expiresAt",type:"string",isIndexed:!0},{name:"revokedAt",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.policyProfiles,columns:[{name:"id",type:"string"},{name:"profileId",type:"string",isIndexed:!0},{name:"defaultAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"lockoutPolicyRef",type:"string"},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.policyStepups,columns:[{name:"id",type:"string"},{name:"actionRef",type:"string",isIndexed:!0},{name:"requiredAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"reauthWindowSeconds",type:"number",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.auditEvents,columns:[{name:"id",type:"string"},{name:"eventType",type:"string",isIndexed:!0},{name:"identityId",type:"string",isOptional:!0,isIndexed:!0},{name:"sessionId",type:"string",isOptional:!0,isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"result",type:"string",enum:["success","failed"],isIndexed:!0},{name:"reasonCode",type:"string",isOptional:!0},{name:"timestamp",type:"string",isIndexed:!0},{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0},{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]}],z={version:2,tables:Y};function k(){return z.tables}var U=5,N=900,T=480*60;function c(){return new Date().toISOString()}function h(r,e){return new Date(new Date(r).getTime()+e*1e3).toISOString()}function S(r){return new Date(r).getTime()<=Date.now()}function f(r){let e=0;return()=>(e+=1,`${r}-${Date.now()}-${e}`)}function F(r){return{identityId:r.id,principal:r.principal,status:r.status}}function H(r){return{assignmentId:r.id,identityId:r.identityId,roleRef:r.roleRef,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}}}function v(r,e){return{sessionId:r.id,identityId:r.identityId,activeScopeRef:e?{...e.tenantId?{tenantId:e.tenantId}:{},...e.branchId?{branchId:e.branchId}:{},...e.scopeAttributes?{attributes:e.scopeAttributes}:{}}:void 0,assuranceLevel:r.assuranceLevel,issuedAt:r.issuedAt,expiresAt:r.expiresAt}}async function q(r,e,t){return r?r.verifyPin(e,t):e===t}async function M(r,e){var t,i,n,s,a,d;r.emitActivity&&await r.emitActivity({activityId:e.eventId,activityType:`ofauth.${e.eventType.toLowerCase()}`,occurredAt:e.timestamp,scopeRef:{domain:"ofauth",...(t=e.scopeRef)!=null&&t.tenantId?{tenantId:e.scopeRef.tenantId}:{},...(i=e.scopeRef)!=null&&i.branchId?{branchId:e.scopeRef.branchId}:{}},actor:e.identityId?{userId:e.identityId}:void 0,payload:{eventType:e.eventType,result:e.result,reasonCode:(n=e.reasonCode)!=null?n:null,sessionId:(s=e.sessionId)!=null?s:null,scopeAttributes:(d=(a=e.scopeRef)==null?void 0:a.attributes)!=null?d:null}})}async function y(r,e){if(!e)return null;let t=await r.get(o.assignments,e);return!t||t.deleted?null:t}function B(){return[{profileId:"cashier_fast",defaultAssuranceLevel:"basic",lockoutPolicyRef:"default"},{profileId:"supervisor_strict",defaultAssuranceLevel:"elevated",lockoutPolicyRef:"strict"},{profileId:"member_self_service",defaultAssuranceLevel:"basic",lockoutPolicyRef:"member"}]}var R=class{constructor(e,t){this.db=e;this.options=t;this.newId=f("auth-audit")}async appendEvent(e){var i,n,s,a,d,u,l,A,g,L,_;let t={id:e.eventId||this.newId(),eventType:e.eventType,identityId:(i=e.identityId)!=null?i:null,sessionId:(n=e.sessionId)!=null?n:null,tenantId:(a=(s=e.scopeRef)==null?void 0:s.tenantId)!=null?a:null,branchId:(u=(d=e.scopeRef)==null?void 0:d.branchId)!=null?u:null,scopeAttributes:(A=(l=e.scopeRef)==null?void 0:l.attributes)!=null?A:null,result:e.result,reasonCode:(g=e.reasonCode)!=null?g:null,timestamp:e.timestamp,syncStatus:(L=e.syncStatus)!=null?L:"pending",replayedAt:(_=e.replayedAt)!=null?_:null,version:1,lastModified:c(),deleted:!1};await this.db.create(o.auditEvents,t),await M(this.options,e)}async queryEvents(e){return(await this.db.query(o.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"desc"}]})).filter(i=>e.identityId?i.identityId===e.identityId:!0).filter(i=>e.sessionId?i.sessionId===e.sessionId:!0).filter(i=>{var n;return(n=e.eventTypes)!=null&&n.length?e.eventTypes.includes(i.eventType):!0}).filter(i=>e.fromTimestamp?i.timestamp>=e.fromTimestamp:!0).filter(i=>e.toTimestamp?i.timestamp<=e.toTimestamp:!0).map(i=>{var n,s,a,d,u;return{eventId:i.id,eventType:i.eventType,identityId:(n=i.identityId)!=null?n:void 0,sessionId:(s=i.sessionId)!=null?s:void 0,scopeRef:i.tenantId||i.branchId||i.scopeAttributes?{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}:void 0,result:i.result,reasonCode:(a=i.reasonCode)!=null?a:void 0,timestamp:i.timestamp,syncStatus:(d=i.syncStatus)!=null?d:"pending",replayedAt:(u=i.replayedAt)!=null?u:void 0}})}async listPendingReplay(e={}){var i;let t=await this.db.query(o.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"asc"}]});return t.filter(n=>n.syncStatus!=="replayed").slice(0,(i=e.limit)!=null?i:t.length).map(n=>{var s,a,d,u,l;return{eventId:n.id,eventType:n.eventType,identityId:(s=n.identityId)!=null?s:void 0,sessionId:(a=n.sessionId)!=null?a:void 0,scopeRef:n.tenantId||n.branchId||n.scopeAttributes?{...n.tenantId?{tenantId:n.tenantId}:{},...n.branchId?{branchId:n.branchId}:{},...n.scopeAttributes?{attributes:n.scopeAttributes}:{}}:void 0,result:n.result,reasonCode:(d=n.reasonCode)!=null?d:void 0,timestamp:n.timestamp,syncStatus:(u=n.syncStatus)!=null?u:"pending",replayedAt:(l=n.replayedAt)!=null?l:void 0}})}async markReplayed(e,t=c()){let i=0;for(let n of e){let s=await this.db.get(o.auditEvents,n);!s||s.deleted||s.syncStatus==="replayed"||(await this.db.update(o.auditEvents,n,{syncStatus:"replayed",replayedAt:t,version:s.version+1,lastModified:c()}),i+=1)}return i}};var b=class extends Error{constructor(t,i,n=!1){super(i);this.code=t;this.retryable=n;this.name="OfauthDomainError"}};function m(r,e){return new b(r,e,!1)}function he(r){return new b("AUTH_NOT_IMPLEMENTED",r,!1)}var w=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=f("auth-event")}async getActiveSessionOrThrow(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||S(t.expiresAt))throw m("AUTH_SESSION_NOT_FOUND","Session not found");return t}async getPolicySnapshot(){let e=await this.db.query(o.policyProfiles,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]}),t=await this.db.query(o.policyStepups,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]});return{profiles:e.length>0?e.map(i=>({profileId:i.profileId,defaultAssuranceLevel:i.defaultAssuranceLevel,lockoutPolicyRef:i.lockoutPolicyRef})):B(),stepUpRequirements:t.map(i=>{var n;return{actionRef:i.actionRef,requiredAssuranceLevel:i.requiredAssuranceLevel,reauthWindowSeconds:(n=i.reauthWindowSeconds)!=null?n:void 0}}),version:"1",updatedAt:c()}}async getStepUpRequirement(e){var i;let t=await this.db.query(o.policyStepups,{filters:{actionRef:e,deleted:!1},limit:1});return t[0]?{actionRef:t[0].actionRef,requiredAssuranceLevel:t[0].requiredAssuranceLevel,reauthWindowSeconds:(i=t[0].reauthWindowSeconds)!=null?i:void 0}:null}async evaluateStepUp(e,t){var d;let i=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(d=n==null?void 0:n.requiredAssuranceLevel)!=null?d:"basic",a=!I(i.assuranceLevel,s);return{actionRef:t,requiredAssuranceLevel:s,currentAssuranceLevel:i.assuranceLevel,requiresStepUp:a,reauthWindowSeconds:n==null?void 0:n.reauthWindowSeconds}}async enforceStepUp(e,t){let i=await this.getActiveSessionOrThrow(e),n=await this.evaluateStepUp(e,t);if(n.requiresStepUp)throw await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_CHALLENGED",identityId:i.identityId,sessionId:e,result:"failed",reasonCode:"AUTH_STEP_UP_REQUIRED",timestamp:c()}),m("AUTH_STEP_UP_REQUIRED",`Step-up required for action '${t}' (needs ${n.requiredAssuranceLevel})`)}async markStepUpPassed(e,t){var u;let i=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(u=n==null?void 0:n.requiredAssuranceLevel)!=null?u:"elevated";await this.db.update(o.sessions,e,{assuranceLevel:s,version:i.version+1,lastModified:c()}),await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_PASSED",identityId:i.identityId,sessionId:e,result:"success",reasonCode:t,timestamp:c()});let a=await this.db.get(o.sessions,e),d=a?await y(this.db,a.activeAssignmentId):null;if(!a)throw m("AUTH_SESSION_NOT_FOUND","Session not found");return v(a,d!=null?d:void 0)}};var C=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=f("auth-event")}async listAssignments(e){return(await this.db.query(o.assignments,{filters:{identityId:e,deleted:!1},sort:[{field:"lastModified",direction:"desc"}]})).map(H)}async getActiveContext(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||!t.activeAssignmentId)return null;let i=await y(this.db,t.activeAssignmentId);return i?{sessionId:e,assignmentId:i.id,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}}:null}async switchContext(e){let t=await this.db.get(o.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw m("AUTH_SESSION_NOT_FOUND","Session not found");let i=await y(this.db,e.targetAssignmentId);if(!i||i.identityId!==t.identityId)throw m("AUTH_CONTEXT_FORBIDDEN","Requested context is not assigned to this identity");let n=await this.db.update(o.sessions,e.sessionId,{activeAssignmentId:i.id,version:t.version+1,lastModified:c()});return await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"CONTEXT_SWITCHED",identityId:t.identityId,sessionId:e.sessionId,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}},result:"success",reasonCode:e.reasonCode,timestamp:n.lastModified}),{activeContext:{sessionId:e.sessionId,assignmentId:i.id,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}},requiresStepUp:!1,reasonCode:e.reasonCode}}};var x=class{constructor(e,t,i,n,s){this.db=e;this.options=t;this.sessionService=i;this.contextService=n;this.authAuditService=s;this.newEventId=f("auth-event")}async findIdentityByPrincipal(e){var i;return(i=(await this.db.query(o.identities,{filters:{principal:e,deleted:!1},limit:1}))[0])!=null?i:null}async persistFailedAttempt(e){let t=e.failedAttempts+1,i=t>=U,n=i?h(c(),N):null,s=i?"locked":e.status;return this.db.update(o.identities,e.id,{failedAttempts:t,lockoutUntil:n,status:s,version:e.version+1,lastModified:c()})}async clearFailedAttempt(e){e.failedAttempts===0&&!e.lockoutUntil&&e.status==="active"||await this.db.update(o.identities,e.id,{failedAttempts:0,lockoutUntil:null,status:"active",version:e.version+1,lastModified:c()})}async emitAudit(e){await this.authAuditService.appendEvent(e)}async verifyCredential(e){let t=await this.findIdentityByPrincipal(e.principal);if(!t)return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",result:"failed",reasonCode:"INVALID_CREDENTIAL",timestamp:c()}),{ok:!1,reasonCode:"INVALID_CREDENTIAL",retryable:!0};if(t.status==="disabled")return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_DISABLED",timestamp:c()}),{ok:!1,reasonCode:"IDENTITY_DISABLED",retryable:!1};if(t.lockoutUntil&&!S(t.lockoutUntil))return await this.emitAudit({eventId:this.newEventId(),eventType:"LOCKOUT_TRIGGERED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_LOCKED",timestamp:c()}),{ok:!1,reasonCode:"IDENTITY_LOCKED",retryable:!1};if(!await q(this.options.platformAdapter,e.secret,t.secretHash)){let a=(await this.persistFailedAttempt(t)).status==="locked"?"IDENTITY_LOCKED":"INVALID_CREDENTIAL";return await this.emitAudit({eventId:this.newEventId(),eventType:a==="IDENTITY_LOCKED"?"LOCKOUT_TRIGGERED":"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:a,timestamp:c()}),{ok:!1,reasonCode:a,retryable:a==="INVALID_CREDENTIAL"}}await this.clearFailedAttempt(t);let n=await this.contextService.listAssignments(t.id);return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_SUCCESS",identityId:t.id,scopeRef:e.scopeRef,result:"success",timestamp:c()}),{ok:!0,identity:F(t),assignments:n}}async login(e){let t=await this.verifyCredential({principal:e.principal,secret:e.secret,verifyMethod:e.verifyMethod});if(!t.ok)throw m("AUTH_INVALID_CREDENTIAL",`Authentication failed: ${t.reasonCode}`);let i=[...t.assignments].sort((s,a)=>s.assignmentId.localeCompare(a.assignmentId))[0],n=await this.sessionService.createSession({identityId:t.identity.identityId,assignmentId:i==null?void 0:i.assignmentId,assuranceLevel:"basic"});return{identity:t.identity,sessionId:n.sessionId}}async logout(e){await this.sessionService.revokeSession({sessionId:e,reasonCode:"LOGOUT"}),await this.emitAudit({eventId:this.newEventId(),eventType:"SESSION_REVOKED",sessionId:e,result:"success",reasonCode:"LOGOUT",timestamp:c()})}};var E=class{constructor(e,t){this.db=e;this.options=t;this.newId=f("auth-session")}async createSession(e){var a,d,u,l;let t=c(),i=h(t,(a=e.ttlSeconds)!=null?a:T),n=await this.db.create(o.sessions,{id:this.newId(),identityId:e.identityId,activeAssignmentId:(d=e.assignmentId)!=null?d:null,assuranceLevel:(u=e.assuranceLevel)!=null?u:"basic",issuedAt:t,expiresAt:i,revokedAt:null,version:1,lastModified:t,deleted:!1}),s=await y(this.db,n.activeAssignmentId);return(l=this.options.logger)==null||l.logInfo("[ofauth] session created",{sessionId:n.id,identityId:n.identityId}),v(n,s)}async refreshSession(e){var s;let t=await this.db.get(o.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw m("AUTH_SESSION_NOT_FOUND","Session not found");let i=await this.db.update(o.sessions,e.sessionId,{expiresAt:h(c(),(s=e.ttlSeconds)!=null?s:T),version:t.version+1,lastModified:c()}),n=await y(this.db,i.activeAssignmentId);return v(i,n)}async revokeSession(e){var i,n;let t=await this.db.get(o.sessions,e.sessionId);!t||t.deleted||t.revokedAt||(await this.db.update(o.sessions,e.sessionId,{revokedAt:c(),version:t.version+1,lastModified:c()}),(n=this.options.logger)==null||n.logInfo("[ofauth] session revoked",{sessionId:e.sessionId,reasonCode:(i=e.reasonCode)!=null?i:null}))}async getActiveSession(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||S(t.expiresAt))return null;let i=await y(this.db,t.activeAssignmentId);return v(t,i)}};async function G(r,e={}){let t=new R(r,e),i=new E(r,e),n=new C(r,t),s=new w(r,t);return{identityService:new x(r,e,i,n,t),sessionService:i,contextService:n,authPolicyService:s,authAuditService:t}}var We="phase1-contract-only";import{CoreRuntime as j}from"ofcore";import{InMemoryDbAdapter as K}from"ofcore";import{asReadonlyStore as J,createStore as Z}from"ofcore";var V=[{toVersion:1,up:async r=>{let e=k();for(let t of e)try{await r.addTable(t)}catch{}}},{toVersion:2,up:async r=>{try{await r.addColumn(o.auditEvents,{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0})}catch{}try{await r.addColumn(o.auditEvents,{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0})}catch{}}}];var X={logInfo(){},logWarn(){},logError(){}};async function W(r,e=X){e.logInfo("[ofauth] starting database migration process");let t=[...V].sort((s,a)=>s.toVersion-a.toVersion),i=t.length>0?t[t.length-1].toVersion:0,n=await r.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=i){e.logInfo(`[ofauth] database already up-to-date at v${n}`);return}for(let s of t)s.toVersion>n&&(e.logInfo(`[ofauth] applying migration v${s.toVersion}`),await s.up(r),await r.setSchemaVersion(s.toVersion),n=s.toVersion);e.logInfo(`[ofauth] migration completed at v${n}`)}var P=class{constructor(e,t){this.runtime=e;this.runtimeStateStore=t;this.runtimeStore=J(this.runtimeStateStore)}static builder(){return new D}get registry(){return this.runtime.registry}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.domainServices=this.runtime.domainServices,this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async init(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.init(e),this.domainServices=this.runtime.domainServices,this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async stop(){this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}isStarted(){return this.runtime.isStarted()}},D=class{constructor(){this.runtimeBuilder=j.builder();this.domainServiceOverrides={};this.runtimeHooks={};this.runtimeBuilder.withDbAdapter(()=>new K)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,t){return this.runtimeBuilder.withExtension(e,t),this}withDomainServicesFactory(e){return this.domainServicesFactory=e,this}overrideDomainServices(e){return this.domainServiceOverrides={...this.domainServiceOverrides,...e},this}withRuntimeHooks(e){return this.runtimeHooks={...this.runtimeHooks,...e},this}withMigrationRunner(e){return this.withRuntimeHooks({runMigrations:e})}withSeedRunner(e){return this.withRuntimeHooks({runSeed:e})}build(){var n;let e=Z({phase:"idle",started:!1,startCount:0,stopCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),t=async s=>{var d,u;let a=(d=s.registry)==null?void 0:d.dbAdapter;a&&await W(a,(u=s.registry)==null?void 0:u.loggerAdapter)};this.runtimeBuilder.withHooks({...this.runtimeHooks,runMigrations:(n=this.runtimeHooks.runMigrations)!=null?n:t,createDomainServices:async s=>{var d,u,l,A;return{...this.domainServicesFactory?await this.domainServicesFactory():await G((u=(d=s.registry)==null?void 0:d.dbAdapter)!=null?u:new K,{logger:(l=s.registry)==null?void 0:l.loggerAdapter,platformAdapter:(A=s.registry)==null?void 0:A.platformAdapter,emitActivity:async g=>{await s.emitActivity(g)}}),...this.domainServiceOverrides}}});let i=this.runtimeBuilder.build();return new P(i,e)}};export{We as OFAUTH_CONTRACT_STAGE,o as OFAUTH_TABLES,P as OfauthCore,D as OfauthCoreBuilder,b as OfauthDomainError,W as applyPendingMigrations,pe as createContractOnlyOfauthServices,G as createDbAdapterOfauthServices,ce as evaluateProtectedRouteGate,k as getOfauthTableSchemas,ee as hasAnyScopeDimension,he as invalidState,I as isAssuranceLevelAtLeast,ne as isAuthAuditEventType,oe as loginWithResolvedContext,V as migrations,z as ofauthSchema,ae as refreshResolvedContext,de as switchContextWithResolvedState,m as unauthorized};
|
|
1
|
+
function Oe(i){return!!(i.tenantId||i.branchId||i.attributes&&Object.keys(i.attributes).length>0)}function T(i,e){let t={basic:1,elevated:2};return t[i]>=t[e]}function De(i){return["LOGIN_SUCCESS","LOGIN_FAILED","LOCKOUT_TRIGGERED","SESSION_REVOKED","CONTEXT_SWITCHED","STEP_UP_CHALLENGED","STEP_UP_PASSED","STEP_UP_FAILED"].includes(i)}function z(i,e){if(e)return i.find(t=>t.assignmentId===e)}function ye(i,e,t){var r,n;return(n=(r=z(i,t))!=null?r:z(i,e))!=null?n:i[0]}function Se(i,e){var t;if(e.requiredTenantId&&i.tenantId!==e.requiredTenantId||e.requiredBranchId&&i.branchId!==e.requiredBranchId)return!1;if(e.requiredScopeAttributes){let r=(t=i.attributes)!=null?t:{};for(let[n,s]of Object.entries(e.requiredScopeAttributes))if(r[n]!==s)return!1}return!0}function O(i){var e;if(i&&typeof i=="object"&&i.error&&typeof i.error.code=="string"){let t=new Error(i.error.message||i.error.code);throw t.code=i.error.code,t}return(e=i==null?void 0:i.data)!=null?e:i}async function H(i,e){var o,d,c;let t=O(await i.sessionService.getActiveSession(e.sessionId));if(!t)return null;let r=O(await i.contextService.listAssignments(e.identityId)),n=O(await i.contextService.getActiveContext(e.sessionId)),s=ye(r,n==null?void 0:n.assignmentId,e.preferredAssignmentId);return{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,assignments:r,activeContextAssignmentId:s==null?void 0:s.assignmentId,activeAssignment:s,scopeRef:(c=(d=(o=s==null?void 0:s.scopeRef)!=null?o:n==null?void 0:n.scopeRef)!=null?d:t.activeScopeRef)!=null?c:{},roleRef:s==null?void 0:s.roleRef,assuranceLevel:t.assuranceLevel}}async function ke(i,e){let t=O(await i.identityService.login(e.principal,e.secret,e.scopeRef)),r=await H(i,{sessionId:t.sessionId,identityId:t.identity.identityId,principal:t.identity.principal});if(!r)throw new Error("session is not active after login");return r}async function Ne(i,e){return H(i,e)}async function qe(i,e){let t=await H(i,{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,preferredAssignmentId:e.targetAssignmentId});if(!t)throw new Error("session is not active while switching context");if(t.activeContextAssignmentId!==e.targetAssignmentId)throw new Error("requested assignment is not available for the active identity");return t}function He(i,e={}){var r,n;if(!i)return{allowed:!1,reasonCode:"NO_SESSION"};let t=(r=e.minimumAssuranceLevel)!=null?r:"basic";return T(i.assuranceLevel,t)?(n=e.requiredRoleRefs)!=null&&n.length&&!(!!i.roleRef&&e.requiredRoleRefs.includes(i.roleRef))?{allowed:!1,reasonCode:"ROLE_FORBIDDEN"}:Se(i.scopeRef,e)?{allowed:!0}:{allowed:!1,reasonCode:"SCOPE_FORBIDDEN"}:{allowed:!1,reasonCode:"ASSURANCE_TOO_LOW"}}var P=class{canLoginOffline(e){let t=new Date(e.nowIso).getTime(),r=new Date(e.envelope.expiresAt).getTime();return!Number.isFinite(t)||!Number.isFinite(r)?!1:t<=r}};var L=class extends Error{constructor(t,r,n=!1){super(r);this.code=t;this.retryable=n;this.name="OfauthDomainError"}},D=class extends Error{constructor(t){super(t);this.code=t;this.name="AuthRejectionError"}};function A(i,e){return new L(i,e,!1)}function Fe(i){return new L("AUTH_NOT_IMPLEMENTED",i,!1)}var Ae=new Set(["AUTH_UNAUTHORIZED","AUTH_INVALID_CREDENTIAL","AUTH_LOCKED","AUTH_DISABLED"]);function he(i){var e;return{principal:i.principal,accessToken:i.accessToken,refreshToken:i.refreshToken,assuranceLevel:i.assuranceLevel,issuedAt:i.issuedAt,expiresAt:i.expiresAt,activeRoleRef:i.activeRoleRef,activeContext:(e=i.activeContext)!=null?e:null}}function Ke(i){var n,s,o;let e=(n=i.now)!=null?n:(()=>new Date().toISOString()),t=(s=i.isOnline)!=null?s:(()=>!0),r=(o=i.offlinePolicy)!=null?o:new P;return{async login(d){var I,R;let c=String(d.principal||"").trim();if(!c)return{status:"rejected",reasonCode:"AUTH_INVALID_CREDENTIAL"};if(t())try{let p=await i.authority.login(d),w=he({principal:c,accessToken:p.accessToken,refreshToken:p.refreshToken,assuranceLevel:p.session.assuranceLevel,issuedAt:p.session.issuedAt,expiresAt:p.session.expiresAt,activeRoleRef:p.activeRoleRef,activeContext:(I=p.activeContext)!=null?I:null}),x={principal:c,identityId:p.session.identityId,roleRef:(R=p.activeRoleRef)!=null?R:null,lastOnlineAuthAt:e()};return await i.cache.setCachedIdentity(x),await i.cache.setSessionEnvelope(c,w),{status:"authenticated",mode:"online",session:w}}catch(p){return p instanceof D?{status:"rejected",reasonCode:p.code}:p instanceof Error&&Ae.has(p.message)?{status:"rejected",reasonCode:p.message}:{status:"rejected",reasonCode:"AUTH_AUTHORITY_UNAVAILABLE"}}let l=await i.cache.getCachedIdentity(c),m=await i.cache.getSessionEnvelope(c);return!l||!m?{status:"rejected",reasonCode:"OFFLINE_SESSION_NOT_AVAILABLE"}:r.canLoginOffline({nowIso:e(),envelope:m})?await i.verifyOfflineSecret({principal:c,secret:d.secret})?{status:"authenticated",mode:"offline",session:m}:{status:"rejected",reasonCode:"AUTH_INVALID_CREDENTIAL"}:{status:"rejected",reasonCode:"OFFLINE_SESSION_EXPIRED"}},async logout(d){let c=await i.cache.getSessionEnvelope(d);if(c)try{await i.authority.logout({accessToken:c.accessToken,refreshToken:c.refreshToken})}catch{}}}}function S(i){return{error:{code:"AUTH_NOT_IMPLEMENTED",message:i,retryable:!1}}}function Qe(){return{identityService:{async verifyCredential(){return S("identityService.verifyCredential not implemented yet")},async login(i,e){return S("identityService.login not implemented yet")},async logout(){return S("identityService.logout not implemented yet")}},sessionService:{async createSession(){return S("sessionService.createSession not implemented yet")},async refreshSession(){return S("sessionService.refreshSession not implemented yet")},async revokeSession(){return S("sessionService.revokeSession not implemented yet")},async getActiveSession(){return S("sessionService.getActiveSession not implemented yet")}},contextService:{async listAssignments(){return S("contextService.listAssignments not implemented yet")},async getActiveContext(){return S("contextService.getActiveContext not implemented yet")},async switchContext(){return S("contextService.switchContext not implemented yet")}},authPolicyService:{async getPolicySnapshot(){return S("authPolicyService.getPolicySnapshot not implemented yet")},async getStepUpRequirement(){return S("authPolicyService.getStepUpRequirement not implemented yet")},async evaluateStepUp(){return S("authPolicyService.evaluateStepUp not implemented yet")},async enforceStepUp(){return S("authPolicyService.enforceStepUp not implemented yet")},async markStepUpPassed(){return S("authPolicyService.markStepUpPassed not implemented yet")}},authAuditService:{async appendEvent(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.appendEvent not implemented yet",retryable:!1}},async queryEvents(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.queryEvents not implemented yet",retryable:!1}},async listPendingReplay(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.listPendingReplay not implemented yet",retryable:!1}},async markReplayed(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.markReplayed not implemented yet",retryable:!1}}}}}function f(i,e){return{data:i,meta:{request_id:e!=null?e:null,timestamp:new Date().toISOString()}}}function M(i,e,t={}){var r;return{error:{code:i,message:e,details:t.details,retryable:t.retryable,correlationId:t.correlationId,timestamp:new Date().toISOString()},meta:{request_id:(r=t.requestId)!=null?r:null,timestamp:new Date().toISOString()}}}function y(i,e="AUTH_NOT_IMPLEMENTED",t="Unhandled auth error."){var r;if(i&&typeof i=="object"){let n=i;return M((r=n.code)!=null?r:e,n.message||t,{retryable:n.retryable})}return i instanceof Error?M(e,i.message||t):M(e,t)}function J(i){return{async verifyCredential(e){try{return f(await i.verifyCredential(e))}catch(t){return y(t)}},async login(e,t,r){try{let n=await i.login({principal:e,secret:t,verifyMethod:"password"});return f(n)}catch(n){return y(n)}},async logout(e){try{return await i.logout(e),f({sessionId:e,revoked:!0})}catch(t){return y(t)}}}}function Z(i){return{async createSession(e){try{return f(await i.createSession(e))}catch(t){return y(t)}},async refreshSession(e){try{return f(await i.refreshSession(e))}catch(t){return y(t)}},async revokeSession(e){try{return await i.revokeSession(e),f({sessionId:e.sessionId,revoked:!0})}catch(t){return y(t)}},async getActiveSession(e){try{return f(await i.getActiveSession(e))}catch(t){return y(t)}}}}function ee(i){return{async listAssignments(e){try{return f(await i.listAssignments(e))}catch(t){return y(t)}},async getActiveContext(e){try{return f(await i.getActiveContext(e))}catch(t){return y(t)}},async switchContext(e){try{return f(await i.switchContext(e))}catch(t){return y(t)}}}}function te(i){return{async getPolicySnapshot(){try{return f(await i.getPolicySnapshot())}catch(e){return y(e)}},async getStepUpRequirement(e){try{return f(await i.getStepUpRequirement(e))}catch(t){return y(t)}},async evaluateStepUp(e,t){try{return f(await i.evaluateStepUp(e,t))}catch(r){return y(r)}},async enforceStepUp(e,t){try{return await i.enforceStepUp(e,t),f({sessionId:e,actionRef:t,enforced:!0})}catch(r){return y(r)}},async markStepUpPassed(e,t){try{return f(await i.markStepUpPassed(e,t))}catch(r){return y(r)}}}}var a={identities:"ofauth_identities",assignments:"ofauth_assignments",sessions:"ofauth_sessions",policyProfiles:"ofauth_policy_profiles",policyStepups:"ofauth_policy_stepups",auditEvents:"ofauth_audit_events"},ve=[{name:a.identities,columns:[{name:"id",type:"string"},{name:"principal",type:"string",isIndexed:!0},{name:"secretHash",type:"string"},{name:"verifyMethod",type:"string",enum:["pin","password","custom"]},{name:"status",type:"string",enum:["active","disabled","locked"],isIndexed:!0},{name:"failedAttempts",type:"number"},{name:"lockoutUntil",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.assignments,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"roleRef",type:"string",isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.sessions,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"activeAssignmentId",type:"string",isOptional:!0,isIndexed:!0},{name:"assuranceLevel",type:"string",enum:["basic","elevated"],isIndexed:!0},{name:"issuedAt",type:"string"},{name:"expiresAt",type:"string",isIndexed:!0},{name:"revokedAt",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.policyProfiles,columns:[{name:"id",type:"string"},{name:"profileId",type:"string",isIndexed:!0},{name:"defaultAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"lockoutPolicyRef",type:"string"},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.policyStepups,columns:[{name:"id",type:"string"},{name:"actionRef",type:"string",isIndexed:!0},{name:"requiredAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"reauthWindowSeconds",type:"number",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.auditEvents,columns:[{name:"id",type:"string"},{name:"eventType",type:"string",isIndexed:!0},{name:"identityId",type:"string",isOptional:!0,isIndexed:!0},{name:"sessionId",type:"string",isOptional:!0,isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"result",type:"string",enum:["success","failed"],isIndexed:!0},{name:"reasonCode",type:"string",isOptional:!0},{name:"timestamp",type:"string",isIndexed:!0},{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0},{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]}],ge={version:2,tables:ve};function ie(){return ge.tables}import{makeIdFactory as Ie,nowIso as Re}from"ofcore";var re=5,ne=900,V=480*60;function u(){return Re()}function b(i,e){return new Date(new Date(i).getTime()+e*1e3).toISOString()}function E(i){return new Date(i).getTime()<=Date.now()}function h(i){return Ie(i)}function se(i){return{identityId:i.id,principal:i.principal,status:i.status}}function F(i){return{assignmentId:i.id,identityId:i.identityId,roleRef:i.roleRef,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}}}function C(i,e,t){return{sessionId:i.id,identityId:i.identityId,activeScopeRef:e?{...e.tenantId?{tenantId:e.tenantId}:{},...e.branchId?{branchId:e.branchId}:{},...e.scopeAttributes?{attributes:e.scopeAttributes}:{}}:void 0,grantedAssignments:t!==void 0?t.map(F):void 0,assuranceLevel:i.assuranceLevel,issuedAt:i.issuedAt,expiresAt:i.expiresAt}}async function oe(i,e,t){return i?i.verifyPin(e,t):e===t}async function ae(i,e){var t,r,n,s,o,d;i.emitActivity&&await i.emitActivity({activityId:e.eventId,activityType:`ofauth.${e.eventType.toLowerCase()}`,occurredAt:e.timestamp,scopeRef:{domain:"ofauth",...(t=e.scopeRef)!=null&&t.tenantId?{tenantId:e.scopeRef.tenantId}:{},...(r=e.scopeRef)!=null&&r.branchId?{branchId:e.scopeRef.branchId}:{}},actor:e.identityId?{userId:e.identityId}:void 0,payload:{eventType:e.eventType,result:e.result,reasonCode:(n=e.reasonCode)!=null?n:null,sessionId:(s=e.sessionId)!=null?s:null,scopeAttributes:(d=(o=e.scopeRef)==null?void 0:o.attributes)!=null?d:null}})}async function v(i,e){if(!e)return null;let t=await i.get(a.assignments,e);return!t||t.deleted?null:t}function de(){return[{profileId:"cashier_fast",defaultAssuranceLevel:"basic",lockoutPolicyRef:"default"},{profileId:"supervisor_strict",defaultAssuranceLevel:"elevated",lockoutPolicyRef:"strict"},{profileId:"member_self_service",defaultAssuranceLevel:"basic",lockoutPolicyRef:"member"}]}var _=class{constructor(e,t){this.db=e;this.options=t;this.newId=h("auth-audit")}async appendEvent(e){var r,n,s,o,d,c,l,m,g,I,R;let t={id:e.eventId||this.newId(),eventType:e.eventType,identityId:(r=e.identityId)!=null?r:null,sessionId:(n=e.sessionId)!=null?n:null,tenantId:(o=(s=e.scopeRef)==null?void 0:s.tenantId)!=null?o:null,branchId:(c=(d=e.scopeRef)==null?void 0:d.branchId)!=null?c:null,scopeAttributes:(m=(l=e.scopeRef)==null?void 0:l.attributes)!=null?m:null,result:e.result,reasonCode:(g=e.reasonCode)!=null?g:null,timestamp:e.timestamp,syncStatus:(I=e.syncStatus)!=null?I:"pending",replayedAt:(R=e.replayedAt)!=null?R:null,version:1,lastModified:u(),deleted:!1};await this.db.create(a.auditEvents,t),await ae(this.options,e)}async queryEvents(e){return(await this.db.query(a.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"desc"}]})).filter(r=>e.identityId?r.identityId===e.identityId:!0).filter(r=>e.sessionId?r.sessionId===e.sessionId:!0).filter(r=>{var n;return(n=e.eventTypes)!=null&&n.length?e.eventTypes.includes(r.eventType):!0}).filter(r=>e.fromTimestamp?r.timestamp>=e.fromTimestamp:!0).filter(r=>e.toTimestamp?r.timestamp<=e.toTimestamp:!0).map(r=>{var n,s,o,d,c;return{eventId:r.id,eventType:r.eventType,identityId:(n=r.identityId)!=null?n:void 0,sessionId:(s=r.sessionId)!=null?s:void 0,scopeRef:r.tenantId||r.branchId||r.scopeAttributes?{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}:void 0,result:r.result,reasonCode:(o=r.reasonCode)!=null?o:void 0,timestamp:r.timestamp,syncStatus:(d=r.syncStatus)!=null?d:"pending",replayedAt:(c=r.replayedAt)!=null?c:void 0}})}async listPendingReplay(e={}){var r;let t=await this.db.query(a.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"asc"}]});return t.filter(n=>n.syncStatus!=="replayed").slice(0,(r=e.limit)!=null?r:t.length).map(n=>{var s,o,d,c,l;return{eventId:n.id,eventType:n.eventType,identityId:(s=n.identityId)!=null?s:void 0,sessionId:(o=n.sessionId)!=null?o:void 0,scopeRef:n.tenantId||n.branchId||n.scopeAttributes?{...n.tenantId?{tenantId:n.tenantId}:{},...n.branchId?{branchId:n.branchId}:{},...n.scopeAttributes?{attributes:n.scopeAttributes}:{}}:void 0,result:n.result,reasonCode:(d=n.reasonCode)!=null?d:void 0,timestamp:n.timestamp,syncStatus:(c=n.syncStatus)!=null?c:"pending",replayedAt:(l=n.replayedAt)!=null?l:void 0}})}async markReplayed(e,t=u()){let r=0;for(let n of e){let s=await this.db.get(a.auditEvents,n);!s||s.deleted||s.syncStatus==="replayed"||(await this.db.update(a.auditEvents,n,{syncStatus:"replayed",replayedAt:t,version:s.version+1,lastModified:u()}),r+=1)}return r}};var U=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=h("auth-event")}async getActiveSessionOrThrow(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||E(t.expiresAt))throw A("AUTH_SESSION_NOT_FOUND","Session not found");return t}async getPolicySnapshot(){let e=await this.db.query(a.policyProfiles,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]}),t=await this.db.query(a.policyStepups,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]});return{profiles:e.length>0?e.map(r=>({profileId:r.profileId,defaultAssuranceLevel:r.defaultAssuranceLevel,lockoutPolicyRef:r.lockoutPolicyRef})):de(),stepUpRequirements:t.map(r=>{var n;return{actionRef:r.actionRef,requiredAssuranceLevel:r.requiredAssuranceLevel,reauthWindowSeconds:(n=r.reauthWindowSeconds)!=null?n:void 0}}),version:"1",updatedAt:u()}}async getStepUpRequirement(e){var r;let t=await this.db.query(a.policyStepups,{filters:{actionRef:e,deleted:!1},limit:1});return t[0]?{actionRef:t[0].actionRef,requiredAssuranceLevel:t[0].requiredAssuranceLevel,reauthWindowSeconds:(r=t[0].reauthWindowSeconds)!=null?r:void 0}:null}async evaluateStepUp(e,t){var d;let r=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(d=n==null?void 0:n.requiredAssuranceLevel)!=null?d:"basic",o=!T(r.assuranceLevel,s);return{actionRef:t,requiredAssuranceLevel:s,currentAssuranceLevel:r.assuranceLevel,requiresStepUp:o,reauthWindowSeconds:n==null?void 0:n.reauthWindowSeconds}}async enforceStepUp(e,t){let r=await this.getActiveSessionOrThrow(e),n=await this.evaluateStepUp(e,t);if(n.requiresStepUp)throw await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_CHALLENGED",identityId:r.identityId,sessionId:e,result:"failed",reasonCode:"AUTH_STEP_UP_REQUIRED",timestamp:u()}),A("AUTH_STEP_UP_REQUIRED",`Step-up required for action '${t}' (needs ${n.requiredAssuranceLevel})`)}async markStepUpPassed(e,t){var c;let r=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(c=n==null?void 0:n.requiredAssuranceLevel)!=null?c:"elevated";await this.db.update(a.sessions,e,{assuranceLevel:s,version:r.version+1,lastModified:u()}),await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_PASSED",identityId:r.identityId,sessionId:e,result:"success",reasonCode:t,timestamp:u()});let o=await this.db.get(a.sessions,e),d=o?await v(this.db,o.activeAssignmentId):null;if(!o)throw A("AUTH_SESSION_NOT_FOUND","Session not found");return C(o,d!=null?d:void 0)}};var k=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=h("auth-event")}async listAssignments(e){return(await this.db.query(a.assignments,{filters:{identityId:e,deleted:!1},sort:[{field:"lastModified",direction:"desc"}]})).map(F)}async getActiveContext(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||!t.activeAssignmentId)return null;let r=await v(this.db,t.activeAssignmentId);return r?{sessionId:e,assignmentId:r.id,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}}:null}async switchContext(e){let t=await this.db.get(a.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw A("AUTH_SESSION_NOT_FOUND","Session not found");let r=null;if(e.targetAssignmentId){if(r=await v(this.db,e.targetAssignmentId),!r||r.identityId!==t.identityId)throw A("AUTH_CONTEXT_FORBIDDEN","Requested context is not assigned to this identity")}else if(e.targetTenantId){let s=await this.db.query(a.assignments,{filters:{identityId:t.identityId,tenantId:e.targetTenantId,deleted:!1},sort:[{field:"lastModified",direction:"asc"}],limit:1});if(!s[0])throw A("AUTH_CONTEXT_FORBIDDEN","No assignments found in target tenant");r=s[0]}else throw A("AUTH_CONTEXT_INVALID","Either targetAssignmentId or targetTenantId must be provided");let n=await this.db.update(a.sessions,e.sessionId,{activeAssignmentId:r.id,version:t.version+1,lastModified:u()});return await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"CONTEXT_SWITCHED",identityId:t.identityId,sessionId:e.sessionId,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}},result:"success",reasonCode:e.reasonCode,timestamp:n.lastModified}),{activeContext:{sessionId:e.sessionId,assignmentId:r.id,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}},requiresStepUp:!1,reasonCode:e.reasonCode}}};var N=class{constructor(e,t,r,n,s){this.db=e;this.options=t;this.sessionService=r;this.contextService=n;this.authAuditService=s;this.newEventId=h("auth-event")}async findIdentityByPrincipal(e){var r;return(r=(await this.db.query(a.identities,{filters:{principal:e,deleted:!1},limit:1}))[0])!=null?r:null}async persistFailedAttempt(e){let t=e.failedAttempts+1,r=t>=re,n=r?b(u(),ne):null,s=r?"locked":e.status;return this.db.update(a.identities,e.id,{failedAttempts:t,lockoutUntil:n,status:s,version:e.version+1,lastModified:u()})}async clearFailedAttempt(e){e.failedAttempts===0&&!e.lockoutUntil&&e.status==="active"||await this.db.update(a.identities,e.id,{failedAttempts:0,lockoutUntil:null,status:"active",version:e.version+1,lastModified:u()})}async emitAudit(e){await this.authAuditService.appendEvent(e)}async verifyCredential(e){let t=await this.findIdentityByPrincipal(e.principal);if(!t)return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",result:"failed",reasonCode:"INVALID_CREDENTIAL",timestamp:u()}),{ok:!1,reasonCode:"INVALID_CREDENTIAL",retryable:!0};if(t.status==="disabled")return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_DISABLED",timestamp:u()}),{ok:!1,reasonCode:"IDENTITY_DISABLED",retryable:!1};if(t.lockoutUntil&&!E(t.lockoutUntil))return await this.emitAudit({eventId:this.newEventId(),eventType:"LOCKOUT_TRIGGERED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_LOCKED",timestamp:u()}),{ok:!1,reasonCode:"IDENTITY_LOCKED",retryable:!1};if(!await oe(this.options.platformAdapter,e.secret,t.secretHash)){let o=(await this.persistFailedAttempt(t)).status==="locked"?"IDENTITY_LOCKED":"INVALID_CREDENTIAL";return await this.emitAudit({eventId:this.newEventId(),eventType:o==="IDENTITY_LOCKED"?"LOCKOUT_TRIGGERED":"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:o,timestamp:u()}),{ok:!1,reasonCode:o,retryable:o==="INVALID_CREDENTIAL"}}await this.clearFailedAttempt(t);let n=await this.contextService.listAssignments(t.id);return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_SUCCESS",identityId:t.id,scopeRef:e.scopeRef,result:"success",timestamp:u()}),{ok:!0,identity:se(t),assignments:n}}async login(e){let t=await this.verifyCredential({principal:e.principal,secret:e.secret,verifyMethod:e.verifyMethod});if(!t.ok)throw A("AUTH_INVALID_CREDENTIAL",`Authentication failed: ${t.reasonCode}`);let r=[...t.assignments].sort((s,o)=>s.assignmentId.localeCompare(o.assignmentId))[0],n=await this.sessionService.createSession({identityId:t.identity.identityId,assignmentId:r==null?void 0:r.assignmentId,assuranceLevel:"basic"});return{identity:t.identity,sessionId:n.sessionId}}async logout(e){await this.sessionService.revokeSession({sessionId:e,reasonCode:"LOGOUT"}),await this.emitAudit({eventId:this.newEventId(),eventType:"SESSION_REVOKED",sessionId:e,result:"success",reasonCode:"LOGOUT",timestamp:u()})}};var q=class{constructor(e,t){this.db=e;this.options=t;this.newId=h("auth-session")}async resolveGrantedAssignments(e,t){let r=await this.db.query(a.assignments,{filters:{identityId:e,deleted:!1}});return t!=null&&t.tenantId?r.filter(n=>n.tenantId===t.tenantId):r}async createSession(e){var d,c,l,m;let t=u(),r=b(t,(d=e.ttlSeconds)!=null?d:V),n=await this.db.create(a.sessions,{id:this.newId(),identityId:e.identityId,activeAssignmentId:(c=e.assignmentId)!=null?c:null,assuranceLevel:(l=e.assuranceLevel)!=null?l:"basic",issuedAt:t,expiresAt:r,revokedAt:null,version:1,lastModified:t,deleted:!1}),s=await v(this.db,n.activeAssignmentId),o=await this.resolveGrantedAssignments(n.identityId,s);return(m=this.options.logger)==null||m.logInfo("[ofauth] session created",{sessionId:n.id,identityId:n.identityId}),C(n,s,o)}async refreshSession(e){var o;let t=await this.db.get(a.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw A("AUTH_SESSION_NOT_FOUND","Session not found");let r=await this.db.update(a.sessions,e.sessionId,{expiresAt:b(u(),(o=e.ttlSeconds)!=null?o:V),version:t.version+1,lastModified:u()}),n=await v(this.db,r.activeAssignmentId),s=await this.resolveGrantedAssignments(r.identityId,n);return C(r,n,s)}async revokeSession(e){var r,n;let t=await this.db.get(a.sessions,e.sessionId);!t||t.deleted||t.revokedAt||(await this.db.update(a.sessions,e.sessionId,{revokedAt:u(),version:t.version+1,lastModified:u()}),(n=this.options.logger)==null||n.logInfo("[ofauth] session revoked",{sessionId:e.sessionId,reasonCode:(r=e.reasonCode)!=null?r:null}))}async getActiveSession(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||E(t.expiresAt))return null;let r=await v(this.db,t.activeAssignmentId),n=await this.resolveGrantedAssignments(t.identityId,r);return C(t,r,n)}};async function ce(i,e={}){let t=new _(i,e),r=new q(i,e),n=new k(i,t),s=new U(i,t),o=new N(i,e,r,n,t);return{identityService:J(o),sessionService:Z(r),contextService:ee(n),authPolicyService:te(s),authAuditService:t}}function Ee(){return new Date().toISOString()}function ue(i){return`${i}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`}async function Ce(i,e){return i!=null&&i.hashPin?i.hashPin(e):`sha256:${e}`}async function Pt(i){var I,R,p,w,x,W,K,j,Q,$,X,Y;let e=i.principal.trim();if(!e)throw new Error("AUTH_PROVISION_PRINCIPAL_REQUIRED");if(!((I=i.roleRef)!=null&&I.trim()))throw new Error("AUTH_PROVISION_ROLE_REQUIRED");let t=Ee(),r=await Ce(i.platformAdapter,i.secret),n=i.dbAdapter,o=(await n.query(a.identities,{filters:{principal:e,deleted:!1},limit:1}))[0],d=(R=o==null?void 0:o.id)!=null?R:ue("ofauth-identity");o?await n.update(a.identities,d,{secretHash:r,status:"active",lastModified:t}):await n.create(a.identities,{id:d,principal:e,secretHash:r,verifyMethod:"pin",status:"active",failedAttempts:0,lockoutUntil:null,version:1,lastModified:t,deleted:!1});let l=(await n.query(a.assignments,{filters:{identityId:d,deleted:!1},limit:1}))[0],m=(p=l==null?void 0:l.id)!=null?p:ue("ofauth-assignment"),g={source:(w=i.source)!=null?w:"host-app"};return l?await n.update(a.assignments,m,{roleRef:i.roleRef,tenantId:(W=(x=i.scope)==null?void 0:x.tenantId)!=null?W:null,branchId:(j=(K=i.scope)==null?void 0:K.branchId)!=null?j:null,scopeAttributes:g,lastModified:t}):await n.create(a.assignments,{id:m,identityId:d,roleRef:i.roleRef,tenantId:($=(Q=i.scope)==null?void 0:Q.tenantId)!=null?$:null,branchId:(Y=(X=i.scope)==null?void 0:X.branchId)!=null?Y:null,scopeAttributes:g,version:1,lastModified:t,deleted:!1}),{identityId:d,assignmentId:m}}var Dt="phase2-runtime-logic";import{CoreRuntime as be}from"ofcore";import{InMemoryDbAdapter as me}from"ofcore";import{asReadonlyStore as xe,createStore as Te}from"ofcore";var le=[{toVersion:1,up:async i=>{let e=ie();for(let t of e)try{await i.addTable(t)}catch{}}}];var we={logInfo(){},logWarn(){},logError(){}};async function pe(i,e=we){e.logInfo("[ofauth] starting database migration process");let t=[...le].sort((s,o)=>s.toVersion-o.toVersion),r=t.length>0?t[t.length-1].toVersion:0,n=await i.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=r){e.logInfo(`[ofauth] database already up-to-date at v${n}`);return}for(let s of t)s.toVersion>n&&(e.logInfo(`[ofauth] applying migration v${s.toVersion}`),await s.up(i),await i.setSchemaVersion(s.toVersion),n=s.toVersion);e.logInfo(`[ofauth] migration completed at v${n}`)}var B=class{constructor(e,t){this.runtime=e;this.runtimeStateStore=t;this.runtimeStore=xe(this.runtimeStateStore)}static builder(){return new G}get registry(){return this.runtime.registry}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.domainServices=fe(this.runtime.domainServices),this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async init(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.init(e),this.domainServices=fe(this.runtime.domainServices),this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async stop(){this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}isStarted(){return this.runtime.isStarted()}};function fe(i){if(!i)throw new Error("Ofauth runtime started without domain services");return i}var G=class{constructor(){this.runtimeBuilder=be.builder();this.domainServiceOverrides={};this.runtimeHooks={};this.runtimeBuilder.withDbAdapter(()=>new me)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,t){return this.runtimeBuilder.withExtension(e,t),this}withDomainServicesFactory(e){return this.domainServicesFactory=e,this}overrideDomainServices(e){return this.domainServiceOverrides={...this.domainServiceOverrides,...e},this}withRuntimeHooks(e){return this.runtimeHooks={...this.runtimeHooks,...e},this}withMigrationRunner(e){return this.withRuntimeHooks({runMigrations:e})}build(){var n;let e=Te({phase:"idle",started:!1,startCount:0,stopCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),t=async s=>{var d,c;let o=(d=s.registry)==null?void 0:d.dbAdapter;o&&await pe(o,(c=s.registry)==null?void 0:c.loggerAdapter)};this.runtimeBuilder.withHooks({...this.runtimeHooks,runMigrations:(n=this.runtimeHooks.runMigrations)!=null?n:t,createDomainServices:async s=>{var d,c,l,m;return{...this.domainServicesFactory?await this.domainServicesFactory():await ce((c=(d=s.registry)==null?void 0:d.dbAdapter)!=null?c:new me,{logger:(l=s.registry)==null?void 0:l.loggerAdapter,platformAdapter:(m=s.registry)==null?void 0:m.platformAdapter,emitActivity:async g=>{await s.emitActivity(g)}}),...this.domainServiceOverrides}}});let r=this.runtimeBuilder.build();return new B(r,e)}};export{D as AuthRejectionError,P as DefaultAuthOfflinePolicy,Dt as OFAUTH_CONTRACT_STAGE,a as OFAUTH_TABLES,B as OfauthCore,G as OfauthCoreBuilder,L as OfauthDomainError,pe as applyPendingMigrations,Qe as createContractOnlyOfauthServices,ce as createDbAdapterOfauthServices,Ke as createServerAuthoritativeAuthBridge,He as evaluateProtectedRouteGate,ie as getOfauthTableSchemas,Oe as hasAnyScopeDimension,Fe as invalidState,T as isAssuranceLevelAtLeast,De as isAuthAuditEventType,ke as loginWithResolvedContext,y as mapAuthErrorToEnvelope,le as migrations,ge as ofauthSchema,Ne as refreshResolvedContext,qe as switchContextWithResolvedState,M as toAuthFailureEnvelope,f as toAuthSuccessEnvelope,A as unauthorized,Pt as upsertLocalIdentityAndAssignment};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var D=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var Z=(r,e)=>{for(var t in e)D(r,t,{get:e[t],enumerable:!0})},ee=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of j(e))!J.call(r,n)&&n!==t&&D(r,n,{get:()=>e[n],enumerable:!(i=X(e,n))||i.enumerable});return r};var te=r=>ee(D({},"__esModule",{value:!0}),r);var ye={};Z(ye,{OFAUTH_CONTRACT_STAGE:()=>me,OFAUTH_TABLES:()=>o,OfauthCore:()=>O,OfauthCoreBuilder:()=>T,OfauthDomainError:()=>I,applyPendingMigrations:()=>F,createContractOnlyOfauthServices:()=>ue,createDbAdapterOfauthServices:()=>U,evaluateProtectedRouteGate:()=>ce,getOfauthTableSchemas:()=>_,hasAnyScopeDimension:()=>ie,invalidState:()=>le,isAssuranceLevelAtLeast:()=>h,isAuthAuditEventType:()=>ne,loginWithResolvedContext:()=>oe,migrations:()=>N,ofauthSchema:()=>B,refreshResolvedContext:()=>ae,switchContextWithResolvedState:()=>de,unauthorized:()=>m});module.exports=te(ye);function ie(r){return!!(r.tenantId||r.branchId||r.attributes&&Object.keys(r.attributes).length>0)}function h(r,e){let t={basic:1,elevated:2};return t[r]>=t[e]}function ne(r){return["LOGIN_SUCCESS","LOGIN_FAILED","LOCKOUT_TRIGGERED","SESSION_REVOKED","CONTEXT_SWITCHED","STEP_UP_CHALLENGED","STEP_UP_PASSED","STEP_UP_FAILED"].includes(r)}function re(r,e){if(e)return r.find(t=>t.assignmentId===e)}function se(r,e){var t;if(e.requiredTenantId&&r.tenantId!==e.requiredTenantId||e.requiredBranchId&&r.branchId!==e.requiredBranchId)return!1;if(e.requiredScopeAttributes){let i=(t=r.attributes)!=null?t:{};for(let[n,s]of Object.entries(e.requiredScopeAttributes))if(i[n]!==s)return!1}return!0}async function L(r,e){var a,d,u;let t=await r.sessionService.getActiveSession(e.sessionId);if(!t)return null;let i=await r.contextService.listAssignments(e.identityId),n=await r.contextService.getActiveContext(e.sessionId);!n&&i[0]&&(n=(await r.contextService.switchContext({sessionId:e.sessionId,targetAssignmentId:i[0].assignmentId,...e.assignmentFallbackReason?{reasonCode:e.assignmentFallbackReason}:{}})).activeContext);let s=re(i,n==null?void 0:n.assignmentId);return{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,assignments:i,activeContextAssignmentId:n==null?void 0:n.assignmentId,activeAssignment:s,scopeRef:(u=(d=(a=n==null?void 0:n.scopeRef)!=null?a:s==null?void 0:s.scopeRef)!=null?d:t.activeScopeRef)!=null?u:{},roleRef:s==null?void 0:s.roleRef,assuranceLevel:t.assuranceLevel}}async function oe(r,e){let t=await r.identityService.login(e),i=await L(r,{sessionId:t.sessionId,identityId:t.identity.identityId,principal:t.identity.principal,assignmentFallbackReason:"AUTO_CONTEXT_AFTER_LOGIN"});if(!i)throw new Error("session is not active after login");return i}async function ae(r,e){return L(r,e)}async function de(r,e){await r.contextService.switchContext({sessionId:e.sessionId,targetAssignmentId:e.targetAssignmentId,...e.reasonCode?{reasonCode:e.reasonCode}:{}});let t=await L(r,e);if(!t)throw new Error("session is not active while switching context");return t}function ce(r,e={}){var i,n;if(!r)return{allowed:!1,reasonCode:"NO_SESSION"};let t=(i=e.minimumAssuranceLevel)!=null?i:"basic";return h(r.assuranceLevel,t)?(n=e.requiredRoleRefs)!=null&&n.length&&!(!!r.roleRef&&e.requiredRoleRefs.includes(r.roleRef))?{allowed:!1,reasonCode:"ROLE_FORBIDDEN"}:se(r.scopeRef,e)?{allowed:!0}:{allowed:!1,reasonCode:"SCOPE_FORBIDDEN"}:{allowed:!1,reasonCode:"ASSURANCE_TOO_LOW"}}function p(r){throw{code:"AUTH_NOT_IMPLEMENTED",message:r,retryable:!1}}function ue(){return{identityService:{async verifyCredential(){return p("identityService.verifyCredential not implemented yet")},async login(){return p("identityService.login not implemented yet")},async logout(){return p("identityService.logout not implemented yet")}},sessionService:{async createSession(){return p("sessionService.createSession not implemented yet")},async refreshSession(){return p("sessionService.refreshSession not implemented yet")},async revokeSession(){return p("sessionService.revokeSession not implemented yet")},async getActiveSession(){return p("sessionService.getActiveSession not implemented yet")}},contextService:{async listAssignments(){return p("contextService.listAssignments not implemented yet")},async getActiveContext(){return p("contextService.getActiveContext not implemented yet")},async switchContext(){return p("contextService.switchContext not implemented yet")}},authPolicyService:{async getPolicySnapshot(){return p("authPolicyService.getPolicySnapshot not implemented yet")},async getStepUpRequirement(){return p("authPolicyService.getStepUpRequirement not implemented yet")},async evaluateStepUp(){return p("authPolicyService.evaluateStepUp not implemented yet")},async enforceStepUp(){return p("authPolicyService.enforceStepUp not implemented yet")},async markStepUpPassed(){return p("authPolicyService.markStepUpPassed not implemented yet")}},authAuditService:{async appendEvent(){return p("authAuditService.appendEvent not implemented yet")},async queryEvents(){return p("authAuditService.queryEvents not implemented yet")},async listPendingReplay(){return p("authAuditService.listPendingReplay not implemented yet")},async markReplayed(){return p("authAuditService.markReplayed not implemented yet")}}}}var o={identities:"ofauth_identities",assignments:"ofauth_assignments",sessions:"ofauth_sessions",policyProfiles:"ofauth_policy_profiles",policyStepups:"ofauth_policy_stepups",auditEvents:"ofauth_audit_events"},pe=[{name:o.identities,columns:[{name:"id",type:"string"},{name:"principal",type:"string",isIndexed:!0},{name:"secretHash",type:"string"},{name:"verifyMethod",type:"string",enum:["pin","password","custom"]},{name:"status",type:"string",enum:["active","disabled","locked"],isIndexed:!0},{name:"failedAttempts",type:"number"},{name:"lockoutUntil",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.assignments,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"roleRef",type:"string",isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.sessions,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"activeAssignmentId",type:"string",isOptional:!0,isIndexed:!0},{name:"assuranceLevel",type:"string",enum:["basic","elevated"],isIndexed:!0},{name:"issuedAt",type:"string"},{name:"expiresAt",type:"string",isIndexed:!0},{name:"revokedAt",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.policyProfiles,columns:[{name:"id",type:"string"},{name:"profileId",type:"string",isIndexed:!0},{name:"defaultAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"lockoutPolicyRef",type:"string"},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.policyStepups,columns:[{name:"id",type:"string"},{name:"actionRef",type:"string",isIndexed:!0},{name:"requiredAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"reauthWindowSeconds",type:"number",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:o.auditEvents,columns:[{name:"id",type:"string"},{name:"eventType",type:"string",isIndexed:!0},{name:"identityId",type:"string",isOptional:!0,isIndexed:!0},{name:"sessionId",type:"string",isOptional:!0,isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"result",type:"string",enum:["success","failed"],isIndexed:!0},{name:"reasonCode",type:"string",isOptional:!0},{name:"timestamp",type:"string",isIndexed:!0},{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0},{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]}],B={version:2,tables:pe};function _(){return B.tables}var G=5,V=900,k=480*60;function c(){return new Date().toISOString()}function g(r,e){return new Date(new Date(r).getTime()+e*1e3).toISOString()}function S(r){return new Date(r).getTime()<=Date.now()}function f(r){let e=0;return()=>(e+=1,`${r}-${Date.now()}-${e}`)}function W(r){return{identityId:r.id,principal:r.principal,status:r.status}}function K(r){return{assignmentId:r.id,identityId:r.identityId,roleRef:r.roleRef,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}}}function v(r,e){return{sessionId:r.id,identityId:r.identityId,activeScopeRef:e?{...e.tenantId?{tenantId:e.tenantId}:{},...e.branchId?{branchId:e.branchId}:{},...e.scopeAttributes?{attributes:e.scopeAttributes}:{}}:void 0,assuranceLevel:r.assuranceLevel,issuedAt:r.issuedAt,expiresAt:r.expiresAt}}async function $(r,e,t){return r?r.verifyPin(e,t):e===t}async function Q(r,e){var t,i,n,s,a,d;r.emitActivity&&await r.emitActivity({activityId:e.eventId,activityType:`ofauth.${e.eventType.toLowerCase()}`,occurredAt:e.timestamp,scopeRef:{domain:"ofauth",...(t=e.scopeRef)!=null&&t.tenantId?{tenantId:e.scopeRef.tenantId}:{},...(i=e.scopeRef)!=null&&i.branchId?{branchId:e.scopeRef.branchId}:{}},actor:e.identityId?{userId:e.identityId}:void 0,payload:{eventType:e.eventType,result:e.result,reasonCode:(n=e.reasonCode)!=null?n:null,sessionId:(s=e.sessionId)!=null?s:null,scopeAttributes:(d=(a=e.scopeRef)==null?void 0:a.attributes)!=null?d:null}})}async function y(r,e){if(!e)return null;let t=await r.get(o.assignments,e);return!t||t.deleted?null:t}function Y(){return[{profileId:"cashier_fast",defaultAssuranceLevel:"basic",lockoutPolicyRef:"default"},{profileId:"supervisor_strict",defaultAssuranceLevel:"elevated",lockoutPolicyRef:"strict"},{profileId:"member_self_service",defaultAssuranceLevel:"basic",lockoutPolicyRef:"member"}]}var b=class{constructor(e,t){this.db=e;this.options=t;this.newId=f("auth-audit")}async appendEvent(e){var i,n,s,a,d,u,l,A,R,q,M;let t={id:e.eventId||this.newId(),eventType:e.eventType,identityId:(i=e.identityId)!=null?i:null,sessionId:(n=e.sessionId)!=null?n:null,tenantId:(a=(s=e.scopeRef)==null?void 0:s.tenantId)!=null?a:null,branchId:(u=(d=e.scopeRef)==null?void 0:d.branchId)!=null?u:null,scopeAttributes:(A=(l=e.scopeRef)==null?void 0:l.attributes)!=null?A:null,result:e.result,reasonCode:(R=e.reasonCode)!=null?R:null,timestamp:e.timestamp,syncStatus:(q=e.syncStatus)!=null?q:"pending",replayedAt:(M=e.replayedAt)!=null?M:null,version:1,lastModified:c(),deleted:!1};await this.db.create(o.auditEvents,t),await Q(this.options,e)}async queryEvents(e){return(await this.db.query(o.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"desc"}]})).filter(i=>e.identityId?i.identityId===e.identityId:!0).filter(i=>e.sessionId?i.sessionId===e.sessionId:!0).filter(i=>{var n;return(n=e.eventTypes)!=null&&n.length?e.eventTypes.includes(i.eventType):!0}).filter(i=>e.fromTimestamp?i.timestamp>=e.fromTimestamp:!0).filter(i=>e.toTimestamp?i.timestamp<=e.toTimestamp:!0).map(i=>{var n,s,a,d,u;return{eventId:i.id,eventType:i.eventType,identityId:(n=i.identityId)!=null?n:void 0,sessionId:(s=i.sessionId)!=null?s:void 0,scopeRef:i.tenantId||i.branchId||i.scopeAttributes?{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}:void 0,result:i.result,reasonCode:(a=i.reasonCode)!=null?a:void 0,timestamp:i.timestamp,syncStatus:(d=i.syncStatus)!=null?d:"pending",replayedAt:(u=i.replayedAt)!=null?u:void 0}})}async listPendingReplay(e={}){var i;let t=await this.db.query(o.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"asc"}]});return t.filter(n=>n.syncStatus!=="replayed").slice(0,(i=e.limit)!=null?i:t.length).map(n=>{var s,a,d,u,l;return{eventId:n.id,eventType:n.eventType,identityId:(s=n.identityId)!=null?s:void 0,sessionId:(a=n.sessionId)!=null?a:void 0,scopeRef:n.tenantId||n.branchId||n.scopeAttributes?{...n.tenantId?{tenantId:n.tenantId}:{},...n.branchId?{branchId:n.branchId}:{},...n.scopeAttributes?{attributes:n.scopeAttributes}:{}}:void 0,result:n.result,reasonCode:(d=n.reasonCode)!=null?d:void 0,timestamp:n.timestamp,syncStatus:(u=n.syncStatus)!=null?u:"pending",replayedAt:(l=n.replayedAt)!=null?l:void 0}})}async markReplayed(e,t=c()){let i=0;for(let n of e){let s=await this.db.get(o.auditEvents,n);!s||s.deleted||s.syncStatus==="replayed"||(await this.db.update(o.auditEvents,n,{syncStatus:"replayed",replayedAt:t,version:s.version+1,lastModified:c()}),i+=1)}return i}};var I=class extends Error{constructor(t,i,n=!1){super(i);this.code=t;this.retryable=n;this.name="OfauthDomainError"}};function m(r,e){return new I(r,e,!1)}function le(r){return new I("AUTH_NOT_IMPLEMENTED",r,!1)}var w=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=f("auth-event")}async getActiveSessionOrThrow(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||S(t.expiresAt))throw m("AUTH_SESSION_NOT_FOUND","Session not found");return t}async getPolicySnapshot(){let e=await this.db.query(o.policyProfiles,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]}),t=await this.db.query(o.policyStepups,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]});return{profiles:e.length>0?e.map(i=>({profileId:i.profileId,defaultAssuranceLevel:i.defaultAssuranceLevel,lockoutPolicyRef:i.lockoutPolicyRef})):Y(),stepUpRequirements:t.map(i=>{var n;return{actionRef:i.actionRef,requiredAssuranceLevel:i.requiredAssuranceLevel,reauthWindowSeconds:(n=i.reauthWindowSeconds)!=null?n:void 0}}),version:"1",updatedAt:c()}}async getStepUpRequirement(e){var i;let t=await this.db.query(o.policyStepups,{filters:{actionRef:e,deleted:!1},limit:1});return t[0]?{actionRef:t[0].actionRef,requiredAssuranceLevel:t[0].requiredAssuranceLevel,reauthWindowSeconds:(i=t[0].reauthWindowSeconds)!=null?i:void 0}:null}async evaluateStepUp(e,t){var d;let i=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(d=n==null?void 0:n.requiredAssuranceLevel)!=null?d:"basic",a=!h(i.assuranceLevel,s);return{actionRef:t,requiredAssuranceLevel:s,currentAssuranceLevel:i.assuranceLevel,requiresStepUp:a,reauthWindowSeconds:n==null?void 0:n.reauthWindowSeconds}}async enforceStepUp(e,t){let i=await this.getActiveSessionOrThrow(e),n=await this.evaluateStepUp(e,t);if(n.requiresStepUp)throw await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_CHALLENGED",identityId:i.identityId,sessionId:e,result:"failed",reasonCode:"AUTH_STEP_UP_REQUIRED",timestamp:c()}),m("AUTH_STEP_UP_REQUIRED",`Step-up required for action '${t}' (needs ${n.requiredAssuranceLevel})`)}async markStepUpPassed(e,t){var u;let i=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(u=n==null?void 0:n.requiredAssuranceLevel)!=null?u:"elevated";await this.db.update(o.sessions,e,{assuranceLevel:s,version:i.version+1,lastModified:c()}),await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_PASSED",identityId:i.identityId,sessionId:e,result:"success",reasonCode:t,timestamp:c()});let a=await this.db.get(o.sessions,e),d=a?await y(this.db,a.activeAssignmentId):null;if(!a)throw m("AUTH_SESSION_NOT_FOUND","Session not found");return v(a,d!=null?d:void 0)}};var C=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=f("auth-event")}async listAssignments(e){return(await this.db.query(o.assignments,{filters:{identityId:e,deleted:!1},sort:[{field:"lastModified",direction:"desc"}]})).map(K)}async getActiveContext(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||!t.activeAssignmentId)return null;let i=await y(this.db,t.activeAssignmentId);return i?{sessionId:e,assignmentId:i.id,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}}:null}async switchContext(e){let t=await this.db.get(o.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw m("AUTH_SESSION_NOT_FOUND","Session not found");let i=await y(this.db,e.targetAssignmentId);if(!i||i.identityId!==t.identityId)throw m("AUTH_CONTEXT_FORBIDDEN","Requested context is not assigned to this identity");let n=await this.db.update(o.sessions,e.sessionId,{activeAssignmentId:i.id,version:t.version+1,lastModified:c()});return await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"CONTEXT_SWITCHED",identityId:t.identityId,sessionId:e.sessionId,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}},result:"success",reasonCode:e.reasonCode,timestamp:n.lastModified}),{activeContext:{sessionId:e.sessionId,assignmentId:i.id,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}},requiresStepUp:!1,reasonCode:e.reasonCode}}};var x=class{constructor(e,t,i,n,s){this.db=e;this.options=t;this.sessionService=i;this.contextService=n;this.authAuditService=s;this.newEventId=f("auth-event")}async findIdentityByPrincipal(e){var i;return(i=(await this.db.query(o.identities,{filters:{principal:e,deleted:!1},limit:1}))[0])!=null?i:null}async persistFailedAttempt(e){let t=e.failedAttempts+1,i=t>=G,n=i?g(c(),V):null,s=i?"locked":e.status;return this.db.update(o.identities,e.id,{failedAttempts:t,lockoutUntil:n,status:s,version:e.version+1,lastModified:c()})}async clearFailedAttempt(e){e.failedAttempts===0&&!e.lockoutUntil&&e.status==="active"||await this.db.update(o.identities,e.id,{failedAttempts:0,lockoutUntil:null,status:"active",version:e.version+1,lastModified:c()})}async emitAudit(e){await this.authAuditService.appendEvent(e)}async verifyCredential(e){let t=await this.findIdentityByPrincipal(e.principal);if(!t)return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",result:"failed",reasonCode:"INVALID_CREDENTIAL",timestamp:c()}),{ok:!1,reasonCode:"INVALID_CREDENTIAL",retryable:!0};if(t.status==="disabled")return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_DISABLED",timestamp:c()}),{ok:!1,reasonCode:"IDENTITY_DISABLED",retryable:!1};if(t.lockoutUntil&&!S(t.lockoutUntil))return await this.emitAudit({eventId:this.newEventId(),eventType:"LOCKOUT_TRIGGERED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_LOCKED",timestamp:c()}),{ok:!1,reasonCode:"IDENTITY_LOCKED",retryable:!1};if(!await $(this.options.platformAdapter,e.secret,t.secretHash)){let a=(await this.persistFailedAttempt(t)).status==="locked"?"IDENTITY_LOCKED":"INVALID_CREDENTIAL";return await this.emitAudit({eventId:this.newEventId(),eventType:a==="IDENTITY_LOCKED"?"LOCKOUT_TRIGGERED":"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:a,timestamp:c()}),{ok:!1,reasonCode:a,retryable:a==="INVALID_CREDENTIAL"}}await this.clearFailedAttempt(t);let n=await this.contextService.listAssignments(t.id);return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_SUCCESS",identityId:t.id,scopeRef:e.scopeRef,result:"success",timestamp:c()}),{ok:!0,identity:W(t),assignments:n}}async login(e){let t=await this.verifyCredential({principal:e.principal,secret:e.secret,verifyMethod:e.verifyMethod});if(!t.ok)throw m("AUTH_INVALID_CREDENTIAL",`Authentication failed: ${t.reasonCode}`);let i=[...t.assignments].sort((s,a)=>s.assignmentId.localeCompare(a.assignmentId))[0],n=await this.sessionService.createSession({identityId:t.identity.identityId,assignmentId:i==null?void 0:i.assignmentId,assuranceLevel:"basic"});return{identity:t.identity,sessionId:n.sessionId}}async logout(e){await this.sessionService.revokeSession({sessionId:e,reasonCode:"LOGOUT"}),await this.emitAudit({eventId:this.newEventId(),eventType:"SESSION_REVOKED",sessionId:e,result:"success",reasonCode:"LOGOUT",timestamp:c()})}};var E=class{constructor(e,t){this.db=e;this.options=t;this.newId=f("auth-session")}async createSession(e){var a,d,u,l;let t=c(),i=g(t,(a=e.ttlSeconds)!=null?a:k),n=await this.db.create(o.sessions,{id:this.newId(),identityId:e.identityId,activeAssignmentId:(d=e.assignmentId)!=null?d:null,assuranceLevel:(u=e.assuranceLevel)!=null?u:"basic",issuedAt:t,expiresAt:i,revokedAt:null,version:1,lastModified:t,deleted:!1}),s=await y(this.db,n.activeAssignmentId);return(l=this.options.logger)==null||l.logInfo("[ofauth] session created",{sessionId:n.id,identityId:n.identityId}),v(n,s)}async refreshSession(e){var s;let t=await this.db.get(o.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw m("AUTH_SESSION_NOT_FOUND","Session not found");let i=await this.db.update(o.sessions,e.sessionId,{expiresAt:g(c(),(s=e.ttlSeconds)!=null?s:k),version:t.version+1,lastModified:c()}),n=await y(this.db,i.activeAssignmentId);return v(i,n)}async revokeSession(e){var i,n;let t=await this.db.get(o.sessions,e.sessionId);!t||t.deleted||t.revokedAt||(await this.db.update(o.sessions,e.sessionId,{revokedAt:c(),version:t.version+1,lastModified:c()}),(n=this.options.logger)==null||n.logInfo("[ofauth] session revoked",{sessionId:e.sessionId,reasonCode:(i=e.reasonCode)!=null?i:null}))}async getActiveSession(e){let t=await this.db.get(o.sessions,e);if(!t||t.deleted||t.revokedAt||S(t.expiresAt))return null;let i=await y(this.db,t.activeAssignmentId);return v(t,i)}};async function U(r,e={}){let t=new b(r,e),i=new E(r,e),n=new C(r,t),s=new w(r,t);return{identityService:new x(r,e,i,n,t),sessionService:i,contextService:n,authPolicyService:s,authAuditService:t}}var me="phase1-contract-only";var z=require("ofcore"),H=require("ofcore"),P=require("ofcore");var N=[{toVersion:1,up:async r=>{let e=_();for(let t of e)try{await r.addTable(t)}catch{}}},{toVersion:2,up:async r=>{try{await r.addColumn(o.auditEvents,{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0})}catch{}try{await r.addColumn(o.auditEvents,{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0})}catch{}}}];var fe={logInfo(){},logWarn(){},logError(){}};async function F(r,e=fe){e.logInfo("[ofauth] starting database migration process");let t=[...N].sort((s,a)=>s.toVersion-a.toVersion),i=t.length>0?t[t.length-1].toVersion:0,n=await r.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=i){e.logInfo(`[ofauth] database already up-to-date at v${n}`);return}for(let s of t)s.toVersion>n&&(e.logInfo(`[ofauth] applying migration v${s.toVersion}`),await s.up(r),await r.setSchemaVersion(s.toVersion),n=s.toVersion);e.logInfo(`[ofauth] migration completed at v${n}`)}var O=class{constructor(e,t){this.runtime=e;this.runtimeStateStore=t;this.runtimeStore=(0,P.asReadonlyStore)(this.runtimeStateStore)}static builder(){return new T}get registry(){return this.runtime.registry}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.domainServices=this.runtime.domainServices,this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async init(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.init(e),this.domainServices=this.runtime.domainServices,this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async stop(){this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}isStarted(){return this.runtime.isStarted()}},T=class{constructor(){this.runtimeBuilder=z.CoreRuntime.builder();this.domainServiceOverrides={};this.runtimeHooks={};this.runtimeBuilder.withDbAdapter(()=>new H.InMemoryDbAdapter)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,t){return this.runtimeBuilder.withExtension(e,t),this}withDomainServicesFactory(e){return this.domainServicesFactory=e,this}overrideDomainServices(e){return this.domainServiceOverrides={...this.domainServiceOverrides,...e},this}withRuntimeHooks(e){return this.runtimeHooks={...this.runtimeHooks,...e},this}withMigrationRunner(e){return this.withRuntimeHooks({runMigrations:e})}withSeedRunner(e){return this.withRuntimeHooks({runSeed:e})}build(){var n;let e=(0,P.createStore)({phase:"idle",started:!1,startCount:0,stopCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),t=async s=>{var d,u;let a=(d=s.registry)==null?void 0:d.dbAdapter;a&&await F(a,(u=s.registry)==null?void 0:u.loggerAdapter)};this.runtimeBuilder.withHooks({...this.runtimeHooks,runMigrations:(n=this.runtimeHooks.runMigrations)!=null?n:t,createDomainServices:async s=>{var d,u,l,A;return{...this.domainServicesFactory?await this.domainServicesFactory():await U((u=(d=s.registry)==null?void 0:d.dbAdapter)!=null?u:new H.InMemoryDbAdapter,{logger:(l=s.registry)==null?void 0:l.loggerAdapter,platformAdapter:(A=s.registry)==null?void 0:A.platformAdapter,emitActivity:async R=>{await s.emitActivity(R)}}),...this.domainServiceOverrides}}});let i=this.runtimeBuilder.build();return new O(i,e)}};
|
|
1
|
+
"use strict";var G=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var Ie=Object.getOwnPropertyNames;var Re=Object.prototype.hasOwnProperty;var Ee=(i,e)=>{for(var t in e)G(i,t,{get:e[t],enumerable:!0})},Ce=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Ie(e))!Re.call(i,n)&&n!==t&&G(i,n,{get:()=>e[n],enumerable:!(r=ge(e,n))||r.enumerable});return i};var we=i=>Ce(G({},"__esModule",{value:!0}),i);var Ke={};Ee(Ke,{AuthRejectionError:()=>O,DefaultAuthOfflinePolicy:()=>x,OFAUTH_CONTRACT_STAGE:()=>Ge,OFAUTH_TABLES:()=>a,OfauthCore:()=>V,OfauthCoreBuilder:()=>F,OfauthDomainError:()=>T,applyPendingMigrations:()=>Y,createContractOnlyOfauthServices:()=>He,createDbAdapterOfauthServices:()=>$,createServerAuthoritativeAuthBridge:()=>qe,evaluateProtectedRouteGate:()=>_e,getOfauthTableSchemas:()=>K,hasAnyScopeDimension:()=>be,invalidState:()=>Ue,isAssuranceLevelAtLeast:()=>b,isAuthAuditEventType:()=>xe,loginWithResolvedContext:()=>Pe,mapAuthErrorToEnvelope:()=>f,migrations:()=>X,ofauthSchema:()=>ue,refreshResolvedContext:()=>Le,switchContextWithResolvedState:()=>De,toAuthFailureEnvelope:()=>_,toAuthSuccessEnvelope:()=>m,unauthorized:()=>A,upsertLocalIdentityAndAssignment:()=>Be});module.exports=we(Ke);function be(i){return!!(i.tenantId||i.branchId||i.attributes&&Object.keys(i.attributes).length>0)}function b(i,e){let t={basic:1,elevated:2};return t[i]>=t[e]}function xe(i){return["LOGIN_SUCCESS","LOGIN_FAILED","LOCKOUT_TRIGGERED","SESSION_REVOKED","CONTEXT_SWITCHED","STEP_UP_CHALLENGED","STEP_UP_PASSED","STEP_UP_FAILED"].includes(i)}function se(i,e){if(e)return i.find(t=>t.assignmentId===e)}function Te(i,e,t){var r,n;return(n=(r=se(i,t))!=null?r:se(i,e))!=null?n:i[0]}function Oe(i,e){var t;if(e.requiredTenantId&&i.tenantId!==e.requiredTenantId||e.requiredBranchId&&i.branchId!==e.requiredBranchId)return!1;if(e.requiredScopeAttributes){let r=(t=i.attributes)!=null?t:{};for(let[n,s]of Object.entries(e.requiredScopeAttributes))if(r[n]!==s)return!1}return!0}function D(i){var e;if(i&&typeof i=="object"&&i.error&&typeof i.error.code=="string"){let t=new Error(i.error.message||i.error.code);throw t.code=i.error.code,t}return(e=i==null?void 0:i.data)!=null?e:i}async function W(i,e){var o,d,c;let t=D(await i.sessionService.getActiveSession(e.sessionId));if(!t)return null;let r=D(await i.contextService.listAssignments(e.identityId)),n=D(await i.contextService.getActiveContext(e.sessionId)),s=Te(r,n==null?void 0:n.assignmentId,e.preferredAssignmentId);return{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,assignments:r,activeContextAssignmentId:s==null?void 0:s.assignmentId,activeAssignment:s,scopeRef:(c=(d=(o=s==null?void 0:s.scopeRef)!=null?o:n==null?void 0:n.scopeRef)!=null?d:t.activeScopeRef)!=null?c:{},roleRef:s==null?void 0:s.roleRef,assuranceLevel:t.assuranceLevel}}async function Pe(i,e){let t=D(await i.identityService.login(e.principal,e.secret,e.scopeRef)),r=await W(i,{sessionId:t.sessionId,identityId:t.identity.identityId,principal:t.identity.principal});if(!r)throw new Error("session is not active after login");return r}async function Le(i,e){return W(i,e)}async function De(i,e){let t=await W(i,{sessionId:e.sessionId,identityId:e.identityId,principal:e.principal,preferredAssignmentId:e.targetAssignmentId});if(!t)throw new Error("session is not active while switching context");if(t.activeContextAssignmentId!==e.targetAssignmentId)throw new Error("requested assignment is not available for the active identity");return t}function _e(i,e={}){var r,n;if(!i)return{allowed:!1,reasonCode:"NO_SESSION"};let t=(r=e.minimumAssuranceLevel)!=null?r:"basic";return b(i.assuranceLevel,t)?(n=e.requiredRoleRefs)!=null&&n.length&&!(!!i.roleRef&&e.requiredRoleRefs.includes(i.roleRef))?{allowed:!1,reasonCode:"ROLE_FORBIDDEN"}:Oe(i.scopeRef,e)?{allowed:!0}:{allowed:!1,reasonCode:"SCOPE_FORBIDDEN"}:{allowed:!1,reasonCode:"ASSURANCE_TOO_LOW"}}var x=class{canLoginOffline(e){let t=new Date(e.nowIso).getTime(),r=new Date(e.envelope.expiresAt).getTime();return!Number.isFinite(t)||!Number.isFinite(r)?!1:t<=r}};var T=class extends Error{constructor(t,r,n=!1){super(r);this.code=t;this.retryable=n;this.name="OfauthDomainError"}},O=class extends Error{constructor(t){super(t);this.code=t;this.name="AuthRejectionError"}};function A(i,e){return new T(i,e,!1)}function Ue(i){return new T("AUTH_NOT_IMPLEMENTED",i,!1)}var ke=new Set(["AUTH_UNAUTHORIZED","AUTH_INVALID_CREDENTIAL","AUTH_LOCKED","AUTH_DISABLED"]);function Ne(i){var e;return{principal:i.principal,accessToken:i.accessToken,refreshToken:i.refreshToken,assuranceLevel:i.assuranceLevel,issuedAt:i.issuedAt,expiresAt:i.expiresAt,activeRoleRef:i.activeRoleRef,activeContext:(e=i.activeContext)!=null?e:null}}function qe(i){var n,s,o;let e=(n=i.now)!=null?n:(()=>new Date().toISOString()),t=(s=i.isOnline)!=null?s:(()=>!0),r=(o=i.offlinePolicy)!=null?o:new x;return{async login(d){var I,R;let c=String(d.principal||"").trim();if(!c)return{status:"rejected",reasonCode:"AUTH_INVALID_CREDENTIAL"};if(t())try{let p=await i.authority.login(d),w=Ne({principal:c,accessToken:p.accessToken,refreshToken:p.refreshToken,assuranceLevel:p.session.assuranceLevel,issuedAt:p.session.issuedAt,expiresAt:p.session.expiresAt,activeRoleRef:p.activeRoleRef,activeContext:(I=p.activeContext)!=null?I:null}),L={principal:c,identityId:p.session.identityId,roleRef:(R=p.activeRoleRef)!=null?R:null,lastOnlineAuthAt:e()};return await i.cache.setCachedIdentity(L),await i.cache.setSessionEnvelope(c,w),{status:"authenticated",mode:"online",session:w}}catch(p){return p instanceof O?{status:"rejected",reasonCode:p.code}:p instanceof Error&&ke.has(p.message)?{status:"rejected",reasonCode:p.message}:{status:"rejected",reasonCode:"AUTH_AUTHORITY_UNAVAILABLE"}}let l=await i.cache.getCachedIdentity(c),y=await i.cache.getSessionEnvelope(c);return!l||!y?{status:"rejected",reasonCode:"OFFLINE_SESSION_NOT_AVAILABLE"}:r.canLoginOffline({nowIso:e(),envelope:y})?await i.verifyOfflineSecret({principal:c,secret:d.secret})?{status:"authenticated",mode:"offline",session:y}:{status:"rejected",reasonCode:"AUTH_INVALID_CREDENTIAL"}:{status:"rejected",reasonCode:"OFFLINE_SESSION_EXPIRED"}},async logout(d){let c=await i.cache.getSessionEnvelope(d);if(c)try{await i.authority.logout({accessToken:c.accessToken,refreshToken:c.refreshToken})}catch{}}}}function S(i){return{error:{code:"AUTH_NOT_IMPLEMENTED",message:i,retryable:!1}}}function He(){return{identityService:{async verifyCredential(){return S("identityService.verifyCredential not implemented yet")},async login(i,e){return S("identityService.login not implemented yet")},async logout(){return S("identityService.logout not implemented yet")}},sessionService:{async createSession(){return S("sessionService.createSession not implemented yet")},async refreshSession(){return S("sessionService.refreshSession not implemented yet")},async revokeSession(){return S("sessionService.revokeSession not implemented yet")},async getActiveSession(){return S("sessionService.getActiveSession not implemented yet")}},contextService:{async listAssignments(){return S("contextService.listAssignments not implemented yet")},async getActiveContext(){return S("contextService.getActiveContext not implemented yet")},async switchContext(){return S("contextService.switchContext not implemented yet")}},authPolicyService:{async getPolicySnapshot(){return S("authPolicyService.getPolicySnapshot not implemented yet")},async getStepUpRequirement(){return S("authPolicyService.getStepUpRequirement not implemented yet")},async evaluateStepUp(){return S("authPolicyService.evaluateStepUp not implemented yet")},async enforceStepUp(){return S("authPolicyService.enforceStepUp not implemented yet")},async markStepUpPassed(){return S("authPolicyService.markStepUpPassed not implemented yet")}},authAuditService:{async appendEvent(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.appendEvent not implemented yet",retryable:!1}},async queryEvents(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.queryEvents not implemented yet",retryable:!1}},async listPendingReplay(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.listPendingReplay not implemented yet",retryable:!1}},async markReplayed(){throw{code:"AUTH_NOT_IMPLEMENTED",message:"authAuditService.markReplayed not implemented yet",retryable:!1}}}}}function m(i,e){return{data:i,meta:{request_id:e!=null?e:null,timestamp:new Date().toISOString()}}}function _(i,e,t={}){var r;return{error:{code:i,message:e,details:t.details,retryable:t.retryable,correlationId:t.correlationId,timestamp:new Date().toISOString()},meta:{request_id:(r=t.requestId)!=null?r:null,timestamp:new Date().toISOString()}}}function f(i,e="AUTH_NOT_IMPLEMENTED",t="Unhandled auth error."){var r;if(i&&typeof i=="object"){let n=i;return _((r=n.code)!=null?r:e,n.message||t,{retryable:n.retryable})}return i instanceof Error?_(e,i.message||t):_(e,t)}function oe(i){return{async verifyCredential(e){try{return m(await i.verifyCredential(e))}catch(t){return f(t)}},async login(e,t,r){try{let n=await i.login({principal:e,secret:t,verifyMethod:"password"});return m(n)}catch(n){return f(n)}},async logout(e){try{return await i.logout(e),m({sessionId:e,revoked:!0})}catch(t){return f(t)}}}}function ae(i){return{async createSession(e){try{return m(await i.createSession(e))}catch(t){return f(t)}},async refreshSession(e){try{return m(await i.refreshSession(e))}catch(t){return f(t)}},async revokeSession(e){try{return await i.revokeSession(e),m({sessionId:e.sessionId,revoked:!0})}catch(t){return f(t)}},async getActiveSession(e){try{return m(await i.getActiveSession(e))}catch(t){return f(t)}}}}function de(i){return{async listAssignments(e){try{return m(await i.listAssignments(e))}catch(t){return f(t)}},async getActiveContext(e){try{return m(await i.getActiveContext(e))}catch(t){return f(t)}},async switchContext(e){try{return m(await i.switchContext(e))}catch(t){return f(t)}}}}function ce(i){return{async getPolicySnapshot(){try{return m(await i.getPolicySnapshot())}catch(e){return f(e)}},async getStepUpRequirement(e){try{return m(await i.getStepUpRequirement(e))}catch(t){return f(t)}},async evaluateStepUp(e,t){try{return m(await i.evaluateStepUp(e,t))}catch(r){return f(r)}},async enforceStepUp(e,t){try{return await i.enforceStepUp(e,t),m({sessionId:e,actionRef:t,enforced:!0})}catch(r){return f(r)}},async markStepUpPassed(e,t){try{return m(await i.markStepUpPassed(e,t))}catch(r){return f(r)}}}}var a={identities:"ofauth_identities",assignments:"ofauth_assignments",sessions:"ofauth_sessions",policyProfiles:"ofauth_policy_profiles",policyStepups:"ofauth_policy_stepups",auditEvents:"ofauth_audit_events"},Me=[{name:a.identities,columns:[{name:"id",type:"string"},{name:"principal",type:"string",isIndexed:!0},{name:"secretHash",type:"string"},{name:"verifyMethod",type:"string",enum:["pin","password","custom"]},{name:"status",type:"string",enum:["active","disabled","locked"],isIndexed:!0},{name:"failedAttempts",type:"number"},{name:"lockoutUntil",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.assignments,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"roleRef",type:"string",isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.sessions,columns:[{name:"id",type:"string"},{name:"identityId",type:"string",isIndexed:!0},{name:"activeAssignmentId",type:"string",isOptional:!0,isIndexed:!0},{name:"assuranceLevel",type:"string",enum:["basic","elevated"],isIndexed:!0},{name:"issuedAt",type:"string"},{name:"expiresAt",type:"string",isIndexed:!0},{name:"revokedAt",type:"string",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.policyProfiles,columns:[{name:"id",type:"string"},{name:"profileId",type:"string",isIndexed:!0},{name:"defaultAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"lockoutPolicyRef",type:"string"},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.policyStepups,columns:[{name:"id",type:"string"},{name:"actionRef",type:"string",isIndexed:!0},{name:"requiredAssuranceLevel",type:"string",enum:["basic","elevated"]},{name:"reauthWindowSeconds",type:"number",isOptional:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]},{name:a.auditEvents,columns:[{name:"id",type:"string"},{name:"eventType",type:"string",isIndexed:!0},{name:"identityId",type:"string",isOptional:!0,isIndexed:!0},{name:"sessionId",type:"string",isOptional:!0,isIndexed:!0},{name:"tenantId",type:"string",isOptional:!0,isIndexed:!0},{name:"branchId",type:"string",isOptional:!0,isIndexed:!0},{name:"scopeAttributes",type:"json",isOptional:!0},{name:"result",type:"string",enum:["success","failed"],isIndexed:!0},{name:"reasonCode",type:"string",isOptional:!0},{name:"timestamp",type:"string",isIndexed:!0},{name:"syncStatus",type:"string",enum:["pending","replayed"],isIndexed:!0},{name:"replayedAt",type:"string",isOptional:!0,isIndexed:!0},{name:"version",type:"number"},{name:"lastModified",type:"string"},{name:"deleted",type:"boolean",isIndexed:!0}]}],ue={version:2,tables:Me};function K(){return ue.tables}var U=require("ofcore");var le=5,pe=900,j=480*60;function u(){return(0,U.nowIso)()}function P(i,e){return new Date(new Date(i).getTime()+e*1e3).toISOString()}function E(i){return new Date(i).getTime()<=Date.now()}function h(i){return(0,U.makeIdFactory)(i)}function me(i){return{identityId:i.id,principal:i.principal,status:i.status}}function Q(i){return{assignmentId:i.id,identityId:i.identityId,roleRef:i.roleRef,scopeRef:{...i.tenantId?{tenantId:i.tenantId}:{},...i.branchId?{branchId:i.branchId}:{},...i.scopeAttributes?{attributes:i.scopeAttributes}:{}}}}function C(i,e,t){return{sessionId:i.id,identityId:i.identityId,activeScopeRef:e?{...e.tenantId?{tenantId:e.tenantId}:{},...e.branchId?{branchId:e.branchId}:{},...e.scopeAttributes?{attributes:e.scopeAttributes}:{}}:void 0,grantedAssignments:t!==void 0?t.map(Q):void 0,assuranceLevel:i.assuranceLevel,issuedAt:i.issuedAt,expiresAt:i.expiresAt}}async function fe(i,e,t){return i?i.verifyPin(e,t):e===t}async function ye(i,e){var t,r,n,s,o,d;i.emitActivity&&await i.emitActivity({activityId:e.eventId,activityType:`ofauth.${e.eventType.toLowerCase()}`,occurredAt:e.timestamp,scopeRef:{domain:"ofauth",...(t=e.scopeRef)!=null&&t.tenantId?{tenantId:e.scopeRef.tenantId}:{},...(r=e.scopeRef)!=null&&r.branchId?{branchId:e.scopeRef.branchId}:{}},actor:e.identityId?{userId:e.identityId}:void 0,payload:{eventType:e.eventType,result:e.result,reasonCode:(n=e.reasonCode)!=null?n:null,sessionId:(s=e.sessionId)!=null?s:null,scopeAttributes:(d=(o=e.scopeRef)==null?void 0:o.attributes)!=null?d:null}})}async function v(i,e){if(!e)return null;let t=await i.get(a.assignments,e);return!t||t.deleted?null:t}function Se(){return[{profileId:"cashier_fast",defaultAssuranceLevel:"basic",lockoutPolicyRef:"default"},{profileId:"supervisor_strict",defaultAssuranceLevel:"elevated",lockoutPolicyRef:"strict"},{profileId:"member_self_service",defaultAssuranceLevel:"basic",lockoutPolicyRef:"member"}]}var k=class{constructor(e,t){this.db=e;this.options=t;this.newId=h("auth-audit")}async appendEvent(e){var r,n,s,o,d,c,l,y,g,I,R;let t={id:e.eventId||this.newId(),eventType:e.eventType,identityId:(r=e.identityId)!=null?r:null,sessionId:(n=e.sessionId)!=null?n:null,tenantId:(o=(s=e.scopeRef)==null?void 0:s.tenantId)!=null?o:null,branchId:(c=(d=e.scopeRef)==null?void 0:d.branchId)!=null?c:null,scopeAttributes:(y=(l=e.scopeRef)==null?void 0:l.attributes)!=null?y:null,result:e.result,reasonCode:(g=e.reasonCode)!=null?g:null,timestamp:e.timestamp,syncStatus:(I=e.syncStatus)!=null?I:"pending",replayedAt:(R=e.replayedAt)!=null?R:null,version:1,lastModified:u(),deleted:!1};await this.db.create(a.auditEvents,t),await ye(this.options,e)}async queryEvents(e){return(await this.db.query(a.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"desc"}]})).filter(r=>e.identityId?r.identityId===e.identityId:!0).filter(r=>e.sessionId?r.sessionId===e.sessionId:!0).filter(r=>{var n;return(n=e.eventTypes)!=null&&n.length?e.eventTypes.includes(r.eventType):!0}).filter(r=>e.fromTimestamp?r.timestamp>=e.fromTimestamp:!0).filter(r=>e.toTimestamp?r.timestamp<=e.toTimestamp:!0).map(r=>{var n,s,o,d,c;return{eventId:r.id,eventType:r.eventType,identityId:(n=r.identityId)!=null?n:void 0,sessionId:(s=r.sessionId)!=null?s:void 0,scopeRef:r.tenantId||r.branchId||r.scopeAttributes?{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}:void 0,result:r.result,reasonCode:(o=r.reasonCode)!=null?o:void 0,timestamp:r.timestamp,syncStatus:(d=r.syncStatus)!=null?d:"pending",replayedAt:(c=r.replayedAt)!=null?c:void 0}})}async listPendingReplay(e={}){var r;let t=await this.db.query(a.auditEvents,{filters:{deleted:!1},sort:[{field:"timestamp",direction:"asc"}]});return t.filter(n=>n.syncStatus!=="replayed").slice(0,(r=e.limit)!=null?r:t.length).map(n=>{var s,o,d,c,l;return{eventId:n.id,eventType:n.eventType,identityId:(s=n.identityId)!=null?s:void 0,sessionId:(o=n.sessionId)!=null?o:void 0,scopeRef:n.tenantId||n.branchId||n.scopeAttributes?{...n.tenantId?{tenantId:n.tenantId}:{},...n.branchId?{branchId:n.branchId}:{},...n.scopeAttributes?{attributes:n.scopeAttributes}:{}}:void 0,result:n.result,reasonCode:(d=n.reasonCode)!=null?d:void 0,timestamp:n.timestamp,syncStatus:(c=n.syncStatus)!=null?c:"pending",replayedAt:(l=n.replayedAt)!=null?l:void 0}})}async markReplayed(e,t=u()){let r=0;for(let n of e){let s=await this.db.get(a.auditEvents,n);!s||s.deleted||s.syncStatus==="replayed"||(await this.db.update(a.auditEvents,n,{syncStatus:"replayed",replayedAt:t,version:s.version+1,lastModified:u()}),r+=1)}return r}};var N=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=h("auth-event")}async getActiveSessionOrThrow(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||E(t.expiresAt))throw A("AUTH_SESSION_NOT_FOUND","Session not found");return t}async getPolicySnapshot(){let e=await this.db.query(a.policyProfiles,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]}),t=await this.db.query(a.policyStepups,{filters:{deleted:!1},sort:[{field:"lastModified",direction:"desc"}]});return{profiles:e.length>0?e.map(r=>({profileId:r.profileId,defaultAssuranceLevel:r.defaultAssuranceLevel,lockoutPolicyRef:r.lockoutPolicyRef})):Se(),stepUpRequirements:t.map(r=>{var n;return{actionRef:r.actionRef,requiredAssuranceLevel:r.requiredAssuranceLevel,reauthWindowSeconds:(n=r.reauthWindowSeconds)!=null?n:void 0}}),version:"1",updatedAt:u()}}async getStepUpRequirement(e){var r;let t=await this.db.query(a.policyStepups,{filters:{actionRef:e,deleted:!1},limit:1});return t[0]?{actionRef:t[0].actionRef,requiredAssuranceLevel:t[0].requiredAssuranceLevel,reauthWindowSeconds:(r=t[0].reauthWindowSeconds)!=null?r:void 0}:null}async evaluateStepUp(e,t){var d;let r=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(d=n==null?void 0:n.requiredAssuranceLevel)!=null?d:"basic",o=!b(r.assuranceLevel,s);return{actionRef:t,requiredAssuranceLevel:s,currentAssuranceLevel:r.assuranceLevel,requiresStepUp:o,reauthWindowSeconds:n==null?void 0:n.reauthWindowSeconds}}async enforceStepUp(e,t){let r=await this.getActiveSessionOrThrow(e),n=await this.evaluateStepUp(e,t);if(n.requiresStepUp)throw await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_CHALLENGED",identityId:r.identityId,sessionId:e,result:"failed",reasonCode:"AUTH_STEP_UP_REQUIRED",timestamp:u()}),A("AUTH_STEP_UP_REQUIRED",`Step-up required for action '${t}' (needs ${n.requiredAssuranceLevel})`)}async markStepUpPassed(e,t){var c;let r=await this.getActiveSessionOrThrow(e),n=await this.getStepUpRequirement(t),s=(c=n==null?void 0:n.requiredAssuranceLevel)!=null?c:"elevated";await this.db.update(a.sessions,e,{assuranceLevel:s,version:r.version+1,lastModified:u()}),await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"STEP_UP_PASSED",identityId:r.identityId,sessionId:e,result:"success",reasonCode:t,timestamp:u()});let o=await this.db.get(a.sessions,e),d=o?await v(this.db,o.activeAssignmentId):null;if(!o)throw A("AUTH_SESSION_NOT_FOUND","Session not found");return C(o,d!=null?d:void 0)}};var q=class{constructor(e,t){this.db=e;this.authAuditService=t;this.newEventId=h("auth-event")}async listAssignments(e){return(await this.db.query(a.assignments,{filters:{identityId:e,deleted:!1},sort:[{field:"lastModified",direction:"desc"}]})).map(Q)}async getActiveContext(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||!t.activeAssignmentId)return null;let r=await v(this.db,t.activeAssignmentId);return r?{sessionId:e,assignmentId:r.id,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}}:null}async switchContext(e){let t=await this.db.get(a.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw A("AUTH_SESSION_NOT_FOUND","Session not found");let r=null;if(e.targetAssignmentId){if(r=await v(this.db,e.targetAssignmentId),!r||r.identityId!==t.identityId)throw A("AUTH_CONTEXT_FORBIDDEN","Requested context is not assigned to this identity")}else if(e.targetTenantId){let s=await this.db.query(a.assignments,{filters:{identityId:t.identityId,tenantId:e.targetTenantId,deleted:!1},sort:[{field:"lastModified",direction:"asc"}],limit:1});if(!s[0])throw A("AUTH_CONTEXT_FORBIDDEN","No assignments found in target tenant");r=s[0]}else throw A("AUTH_CONTEXT_INVALID","Either targetAssignmentId or targetTenantId must be provided");let n=await this.db.update(a.sessions,e.sessionId,{activeAssignmentId:r.id,version:t.version+1,lastModified:u()});return await this.authAuditService.appendEvent({eventId:this.newEventId(),eventType:"CONTEXT_SWITCHED",identityId:t.identityId,sessionId:e.sessionId,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}},result:"success",reasonCode:e.reasonCode,timestamp:n.lastModified}),{activeContext:{sessionId:e.sessionId,assignmentId:r.id,scopeRef:{...r.tenantId?{tenantId:r.tenantId}:{},...r.branchId?{branchId:r.branchId}:{},...r.scopeAttributes?{attributes:r.scopeAttributes}:{}}},requiresStepUp:!1,reasonCode:e.reasonCode}}};var H=class{constructor(e,t,r,n,s){this.db=e;this.options=t;this.sessionService=r;this.contextService=n;this.authAuditService=s;this.newEventId=h("auth-event")}async findIdentityByPrincipal(e){var r;return(r=(await this.db.query(a.identities,{filters:{principal:e,deleted:!1},limit:1}))[0])!=null?r:null}async persistFailedAttempt(e){let t=e.failedAttempts+1,r=t>=le,n=r?P(u(),pe):null,s=r?"locked":e.status;return this.db.update(a.identities,e.id,{failedAttempts:t,lockoutUntil:n,status:s,version:e.version+1,lastModified:u()})}async clearFailedAttempt(e){e.failedAttempts===0&&!e.lockoutUntil&&e.status==="active"||await this.db.update(a.identities,e.id,{failedAttempts:0,lockoutUntil:null,status:"active",version:e.version+1,lastModified:u()})}async emitAudit(e){await this.authAuditService.appendEvent(e)}async verifyCredential(e){let t=await this.findIdentityByPrincipal(e.principal);if(!t)return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",result:"failed",reasonCode:"INVALID_CREDENTIAL",timestamp:u()}),{ok:!1,reasonCode:"INVALID_CREDENTIAL",retryable:!0};if(t.status==="disabled")return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_DISABLED",timestamp:u()}),{ok:!1,reasonCode:"IDENTITY_DISABLED",retryable:!1};if(t.lockoutUntil&&!E(t.lockoutUntil))return await this.emitAudit({eventId:this.newEventId(),eventType:"LOCKOUT_TRIGGERED",identityId:t.id,result:"failed",reasonCode:"IDENTITY_LOCKED",timestamp:u()}),{ok:!1,reasonCode:"IDENTITY_LOCKED",retryable:!1};if(!await fe(this.options.platformAdapter,e.secret,t.secretHash)){let o=(await this.persistFailedAttempt(t)).status==="locked"?"IDENTITY_LOCKED":"INVALID_CREDENTIAL";return await this.emitAudit({eventId:this.newEventId(),eventType:o==="IDENTITY_LOCKED"?"LOCKOUT_TRIGGERED":"LOGIN_FAILED",identityId:t.id,result:"failed",reasonCode:o,timestamp:u()}),{ok:!1,reasonCode:o,retryable:o==="INVALID_CREDENTIAL"}}await this.clearFailedAttempt(t);let n=await this.contextService.listAssignments(t.id);return await this.emitAudit({eventId:this.newEventId(),eventType:"LOGIN_SUCCESS",identityId:t.id,scopeRef:e.scopeRef,result:"success",timestamp:u()}),{ok:!0,identity:me(t),assignments:n}}async login(e){let t=await this.verifyCredential({principal:e.principal,secret:e.secret,verifyMethod:e.verifyMethod});if(!t.ok)throw A("AUTH_INVALID_CREDENTIAL",`Authentication failed: ${t.reasonCode}`);let r=[...t.assignments].sort((s,o)=>s.assignmentId.localeCompare(o.assignmentId))[0],n=await this.sessionService.createSession({identityId:t.identity.identityId,assignmentId:r==null?void 0:r.assignmentId,assuranceLevel:"basic"});return{identity:t.identity,sessionId:n.sessionId}}async logout(e){await this.sessionService.revokeSession({sessionId:e,reasonCode:"LOGOUT"}),await this.emitAudit({eventId:this.newEventId(),eventType:"SESSION_REVOKED",sessionId:e,result:"success",reasonCode:"LOGOUT",timestamp:u()})}};var M=class{constructor(e,t){this.db=e;this.options=t;this.newId=h("auth-session")}async resolveGrantedAssignments(e,t){let r=await this.db.query(a.assignments,{filters:{identityId:e,deleted:!1}});return t!=null&&t.tenantId?r.filter(n=>n.tenantId===t.tenantId):r}async createSession(e){var d,c,l,y;let t=u(),r=P(t,(d=e.ttlSeconds)!=null?d:j),n=await this.db.create(a.sessions,{id:this.newId(),identityId:e.identityId,activeAssignmentId:(c=e.assignmentId)!=null?c:null,assuranceLevel:(l=e.assuranceLevel)!=null?l:"basic",issuedAt:t,expiresAt:r,revokedAt:null,version:1,lastModified:t,deleted:!1}),s=await v(this.db,n.activeAssignmentId),o=await this.resolveGrantedAssignments(n.identityId,s);return(y=this.options.logger)==null||y.logInfo("[ofauth] session created",{sessionId:n.id,identityId:n.identityId}),C(n,s,o)}async refreshSession(e){var o;let t=await this.db.get(a.sessions,e.sessionId);if(!t||t.deleted||t.revokedAt)throw A("AUTH_SESSION_NOT_FOUND","Session not found");let r=await this.db.update(a.sessions,e.sessionId,{expiresAt:P(u(),(o=e.ttlSeconds)!=null?o:j),version:t.version+1,lastModified:u()}),n=await v(this.db,r.activeAssignmentId),s=await this.resolveGrantedAssignments(r.identityId,n);return C(r,n,s)}async revokeSession(e){var r,n;let t=await this.db.get(a.sessions,e.sessionId);!t||t.deleted||t.revokedAt||(await this.db.update(a.sessions,e.sessionId,{revokedAt:u(),version:t.version+1,lastModified:u()}),(n=this.options.logger)==null||n.logInfo("[ofauth] session revoked",{sessionId:e.sessionId,reasonCode:(r=e.reasonCode)!=null?r:null}))}async getActiveSession(e){let t=await this.db.get(a.sessions,e);if(!t||t.deleted||t.revokedAt||E(t.expiresAt))return null;let r=await v(this.db,t.activeAssignmentId),n=await this.resolveGrantedAssignments(t.identityId,r);return C(t,r,n)}};async function $(i,e={}){let t=new k(i,e),r=new M(i,e),n=new q(i,t),s=new N(i,t),o=new H(i,e,r,n,t);return{identityService:oe(o),sessionService:ae(r),contextService:de(n),authPolicyService:ce(s),authAuditService:t}}function Ve(){return new Date().toISOString()}function Ae(i){return`${i}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`}async function Fe(i,e){return i!=null&&i.hashPin?i.hashPin(e):`sha256:${e}`}async function Be(i){var I,R,p,w,L,J,Z,ee,te,ie,re,ne;let e=i.principal.trim();if(!e)throw new Error("AUTH_PROVISION_PRINCIPAL_REQUIRED");if(!((I=i.roleRef)!=null&&I.trim()))throw new Error("AUTH_PROVISION_ROLE_REQUIRED");let t=Ve(),r=await Fe(i.platformAdapter,i.secret),n=i.dbAdapter,o=(await n.query(a.identities,{filters:{principal:e,deleted:!1},limit:1}))[0],d=(R=o==null?void 0:o.id)!=null?R:Ae("ofauth-identity");o?await n.update(a.identities,d,{secretHash:r,status:"active",lastModified:t}):await n.create(a.identities,{id:d,principal:e,secretHash:r,verifyMethod:"pin",status:"active",failedAttempts:0,lockoutUntil:null,version:1,lastModified:t,deleted:!1});let l=(await n.query(a.assignments,{filters:{identityId:d,deleted:!1},limit:1}))[0],y=(p=l==null?void 0:l.id)!=null?p:Ae("ofauth-assignment"),g={source:(w=i.source)!=null?w:"host-app"};return l?await n.update(a.assignments,y,{roleRef:i.roleRef,tenantId:(J=(L=i.scope)==null?void 0:L.tenantId)!=null?J:null,branchId:(ee=(Z=i.scope)==null?void 0:Z.branchId)!=null?ee:null,scopeAttributes:g,lastModified:t}):await n.create(a.assignments,{id:y,identityId:d,roleRef:i.roleRef,tenantId:(ie=(te=i.scope)==null?void 0:te.tenantId)!=null?ie:null,branchId:(ne=(re=i.scope)==null?void 0:re.branchId)!=null?ne:null,scopeAttributes:g,version:1,lastModified:t,deleted:!1}),{identityId:d,assignmentId:y}}var Ge="phase2-runtime-logic";var ve=require("ofcore"),z=require("ofcore"),B=require("ofcore");var X=[{toVersion:1,up:async i=>{let e=K();for(let t of e)try{await i.addTable(t)}catch{}}}];var We={logInfo(){},logWarn(){},logError(){}};async function Y(i,e=We){e.logInfo("[ofauth] starting database migration process");let t=[...X].sort((s,o)=>s.toVersion-o.toVersion),r=t.length>0?t[t.length-1].toVersion:0,n=await i.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=r){e.logInfo(`[ofauth] database already up-to-date at v${n}`);return}for(let s of t)s.toVersion>n&&(e.logInfo(`[ofauth] applying migration v${s.toVersion}`),await s.up(i),await i.setSchemaVersion(s.toVersion),n=s.toVersion);e.logInfo(`[ofauth] migration completed at v${n}`)}var V=class{constructor(e,t){this.runtime=e;this.runtimeStateStore=t;this.runtimeStore=(0,B.asReadonlyStore)(this.runtimeStateStore)}static builder(){return new F}get registry(){return this.runtime.registry}async start(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.start(e),this.domainServices=he(this.runtime.domainServices),this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async init(e={}){this.runtimeStateStore.setState({phase:"starting",started:!1,lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.init(e),this.domainServices=he(this.runtime.domainServices),this.runtimeStateStore.setState(t=>({...t,phase:"started",started:!0,startCount:t.startCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(t){throw this.runtimeStateStore.setState({phase:"error",started:!1,lastError:t instanceof Error?t.message:String(t),lastTransitionAt:new Date().toISOString()}),t}}async stop(){this.runtimeStateStore.setState({phase:"stopping",started:this.runtime.isStarted(),lastError:null,lastTransitionAt:new Date().toISOString()});try{await this.runtime.stop(),this.runtimeStateStore.setState(e=>({...e,phase:"stopped",started:!1,stopCount:e.stopCount+1,lastError:null,lastTransitionAt:new Date().toISOString()}))}catch(e){throw this.runtimeStateStore.setState({phase:"error",started:this.runtime.isStarted(),lastError:e instanceof Error?e.message:String(e),lastTransitionAt:new Date().toISOString()}),e}}isStarted(){return this.runtime.isStarted()}};function he(i){if(!i)throw new Error("Ofauth runtime started without domain services");return i}var F=class{constructor(){this.runtimeBuilder=ve.CoreRuntime.builder();this.domainServiceOverrides={};this.runtimeHooks={};this.runtimeBuilder.withDbAdapter(()=>new z.InMemoryDbAdapter)}withPlatformAdapter(e){return this.runtimeBuilder.withPlatformAdapter(e),this}withDbAdapter(e){return this.runtimeBuilder.withDbAdapter(e),this}withHttpAdapter(e){return this.runtimeBuilder.withHttpAdapter(e),this}withSocketAdapter(e){return this.runtimeBuilder.withSocketAdapter(e),this}withLoggerAdapter(e){return this.runtimeBuilder.withLoggerAdapter(e),this}withActivitySink(e){return this.runtimeBuilder.withActivitySink(e),this}withExtension(e,t){return this.runtimeBuilder.withExtension(e,t),this}withDomainServicesFactory(e){return this.domainServicesFactory=e,this}overrideDomainServices(e){return this.domainServiceOverrides={...this.domainServiceOverrides,...e},this}withRuntimeHooks(e){return this.runtimeHooks={...this.runtimeHooks,...e},this}withMigrationRunner(e){return this.withRuntimeHooks({runMigrations:e})}build(){var n;let e=(0,B.createStore)({phase:"idle",started:!1,startCount:0,stopCount:0,lastError:null,lastTransitionAt:new Date().toISOString()}),t=async s=>{var d,c;let o=(d=s.registry)==null?void 0:d.dbAdapter;o&&await Y(o,(c=s.registry)==null?void 0:c.loggerAdapter)};this.runtimeBuilder.withHooks({...this.runtimeHooks,runMigrations:(n=this.runtimeHooks.runMigrations)!=null?n:t,createDomainServices:async s=>{var d,c,l,y;return{...this.domainServicesFactory?await this.domainServicesFactory():await $((c=(d=s.registry)==null?void 0:d.dbAdapter)!=null?c:new z.InMemoryDbAdapter,{logger:(l=s.registry)==null?void 0:l.loggerAdapter,platformAdapter:(y=s.registry)==null?void 0:y.platformAdapter,emitActivity:async g=>{await s.emitActivity(g)}}),...this.domainServiceOverrides}}});let r=this.runtimeBuilder.build();return new V(r,e)}};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AssuranceLevel, SessionRef } from '../contracts/SessionContract';
|
|
2
|
+
import type { ActiveContext } from '../contracts/ContextContract';
|
|
3
|
+
export type AuthorityVerifyMethod = 'password' | 'pin' | 'custom';
|
|
4
|
+
export interface AuthorityLoginRequest {
|
|
5
|
+
principal: string;
|
|
6
|
+
secret: string;
|
|
7
|
+
verifyMethod?: AuthorityVerifyMethod;
|
|
8
|
+
}
|
|
9
|
+
export interface AuthorityLoginResponse {
|
|
10
|
+
accessToken: string;
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
session: SessionRef;
|
|
13
|
+
activeContext?: ActiveContext | null;
|
|
14
|
+
activeRoleRef?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface AuthorityRefreshRequest {
|
|
17
|
+
refreshToken: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AuthorityRefreshResponse {
|
|
20
|
+
accessToken: string;
|
|
21
|
+
refreshToken?: string;
|
|
22
|
+
session: SessionRef;
|
|
23
|
+
}
|
|
24
|
+
export interface AuthorityLogoutRequest {
|
|
25
|
+
accessToken?: string;
|
|
26
|
+
refreshToken?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AuthAuthorityAdapter {
|
|
29
|
+
login(input: AuthorityLoginRequest): Promise<AuthorityLoginResponse>;
|
|
30
|
+
refresh(input: AuthorityRefreshRequest): Promise<AuthorityRefreshResponse>;
|
|
31
|
+
logout(input: AuthorityLogoutRequest): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export interface AuthoritySessionEnvelope {
|
|
34
|
+
principal: string;
|
|
35
|
+
accessToken: string;
|
|
36
|
+
refreshToken?: string;
|
|
37
|
+
assuranceLevel: AssuranceLevel;
|
|
38
|
+
issuedAt: string;
|
|
39
|
+
expiresAt: string;
|
|
40
|
+
activeRoleRef?: string | null;
|
|
41
|
+
activeContext?: ActiveContext | null;
|
|
42
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AuthScopeRef, ContextAssignment } from '../contracts/ContextContract';
|
|
2
2
|
import { type AssuranceLevel } from '../contracts/SessionContract';
|
|
3
3
|
import type { OfauthDomainServices } from './createContractOnlyOfauthServices';
|
|
4
|
-
import type { LoginRequest } from './IdentityService';
|
|
5
4
|
export interface ResolvedAuthContext {
|
|
6
5
|
sessionId: string;
|
|
7
6
|
identityId: string;
|
|
@@ -24,7 +23,11 @@ export interface ProtectedRouteGateResult {
|
|
|
24
23
|
allowed: boolean;
|
|
25
24
|
reasonCode?: 'NO_SESSION' | 'ASSURANCE_TOO_LOW' | 'ROLE_FORBIDDEN' | 'SCOPE_FORBIDDEN';
|
|
26
25
|
}
|
|
27
|
-
export declare function loginWithResolvedContext(services: Pick<OfauthDomainServices, 'identityService' | 'sessionService' | 'contextService'>, input:
|
|
26
|
+
export declare function loginWithResolvedContext(services: Pick<OfauthDomainServices, 'identityService' | 'sessionService' | 'contextService'>, input: {
|
|
27
|
+
principal: string;
|
|
28
|
+
secret: string;
|
|
29
|
+
scopeRef?: AuthScopeRef;
|
|
30
|
+
}): Promise<ResolvedAuthContext>;
|
|
28
31
|
export declare function refreshResolvedContext(services: Pick<OfauthDomainServices, 'sessionService' | 'contextService'>, input: {
|
|
29
32
|
sessionId: string;
|
|
30
33
|
identityId: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AuthoritySessionEnvelope } from './AuthAuthorityAdapter';
|
|
2
|
+
export interface OfflinePolicyInput {
|
|
3
|
+
nowIso: string;
|
|
4
|
+
envelope: AuthoritySessionEnvelope;
|
|
5
|
+
}
|
|
6
|
+
export interface AuthOfflinePolicy {
|
|
7
|
+
canLoginOffline(input: OfflinePolicyInput): boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class DefaultAuthOfflinePolicy implements AuthOfflinePolicy {
|
|
10
|
+
canLoginOffline(input: OfflinePolicyInput): boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AuthoritySessionEnvelope } from './AuthAuthorityAdapter';
|
|
2
|
+
export interface CachedIdentityEntry {
|
|
3
|
+
principal: string;
|
|
4
|
+
identityId: string;
|
|
5
|
+
roleRef?: string | null;
|
|
6
|
+
lastOnlineAuthAt: string;
|
|
7
|
+
lastDeviceBindingRef?: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface AuthSessionCacheAdapter {
|
|
10
|
+
getCachedIdentity(principal: string): Promise<CachedIdentityEntry | null>;
|
|
11
|
+
setCachedIdentity(entry: CachedIdentityEntry): Promise<void>;
|
|
12
|
+
clearCachedIdentity(principal: string): Promise<void>;
|
|
13
|
+
getSessionEnvelope(principal: string): Promise<AuthoritySessionEnvelope | null>;
|
|
14
|
+
setSessionEnvelope(principal: string, envelope: AuthoritySessionEnvelope): Promise<void>;
|
|
15
|
+
clearSessionEnvelope(principal: string): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AuthPolicyServiceContractV2 } from '../contracts/AuthPolicyContract';
|
|
2
|
+
import type { ContextServiceContractV2 } from '../contracts/ContextContract';
|
|
3
|
+
import type { IdentityServiceContractV2 } from '../contracts/IdentityContract';
|
|
4
|
+
import type { SessionServiceContractV2 } from '../contracts/SessionContract';
|
|
5
|
+
import type { AuthPolicyService } from './AuthPolicyService';
|
|
6
|
+
import type { ContextService } from './ContextService';
|
|
7
|
+
import type { IdentityService } from './IdentityService';
|
|
8
|
+
import type { SessionService } from './SessionService';
|
|
9
|
+
export declare function wrapIdentityServiceToV2(legacy: IdentityService): IdentityServiceContractV2;
|
|
10
|
+
export declare function wrapSessionServiceToV2(legacy: SessionService): SessionServiceContractV2;
|
|
11
|
+
export declare function wrapContextServiceToV2(legacy: ContextService): ContextServiceContractV2;
|
|
12
|
+
export declare function wrapAuthPolicyServiceToV2(legacy: AuthPolicyService): AuthPolicyServiceContractV2;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import type { AuthPolicyServiceContractV2 } from '../contracts/AuthPolicyContract';
|
|
2
|
+
import type { ContextServiceContractV2 } from '../contracts/ContextContract';
|
|
3
|
+
import type { IdentityServiceContractV2 } from '../contracts/IdentityContract';
|
|
4
|
+
import type { SessionServiceContractV2 } from '../contracts/SessionContract';
|
|
1
5
|
import type { AuthAuditService } from './AuthAuditService';
|
|
2
|
-
import type { AuthPolicyService } from './AuthPolicyService';
|
|
3
|
-
import type { ContextService } from './ContextService';
|
|
4
|
-
import type { IdentityService } from './IdentityService';
|
|
5
|
-
import type { SessionService } from './SessionService';
|
|
6
6
|
export interface OfauthDomainServices {
|
|
7
|
-
identityService:
|
|
8
|
-
sessionService:
|
|
9
|
-
contextService:
|
|
10
|
-
authPolicyService:
|
|
7
|
+
identityService: IdentityServiceContractV2;
|
|
8
|
+
sessionService: SessionServiceContractV2;
|
|
9
|
+
contextService: ContextServiceContractV2;
|
|
10
|
+
authPolicyService: AuthPolicyServiceContractV2;
|
|
11
11
|
authAuditService: AuthAuditService;
|
|
12
12
|
}
|
|
13
13
|
export declare function createContractOnlyOfauthServices(): OfauthDomainServices;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { ActivityRecord, DbAdapter, LoggerAdapter, PlatformAdapter } from 'ofcore';
|
|
2
2
|
import type { AuthAuditService } from './AuthAuditService';
|
|
3
|
-
import type { AuthPolicyService } from './AuthPolicyService';
|
|
4
|
-
import type { ContextService } from './ContextService';
|
|
5
|
-
import type { IdentityService } from './IdentityService';
|
|
6
|
-
import type { SessionService } from './SessionService';
|
|
7
3
|
import type { OfauthDomainServices } from './createContractOnlyOfauthServices';
|
|
8
4
|
export interface ServiceRuntimeOptions {
|
|
9
5
|
logger?: LoggerAdapter;
|
|
@@ -11,10 +7,6 @@ export interface ServiceRuntimeOptions {
|
|
|
11
7
|
emitActivity?: (record: ActivityRecord) => Promise<void>;
|
|
12
8
|
}
|
|
13
9
|
export interface OfauthDbAdapterServices extends OfauthDomainServices {
|
|
14
|
-
identityService: IdentityService;
|
|
15
|
-
sessionService: SessionService;
|
|
16
|
-
contextService: ContextService;
|
|
17
|
-
authPolicyService: AuthPolicyService;
|
|
18
10
|
authAuditService: AuthAuditService;
|
|
19
11
|
}
|
|
20
12
|
export declare function createDbAdapterOfauthServices(db: DbAdapter, options?: ServiceRuntimeOptions): Promise<OfauthDbAdapterServices>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuthAuthorityAdapter, AuthorityLoginRequest, AuthoritySessionEnvelope } from './AuthAuthorityAdapter';
|
|
2
|
+
import type { AuthOfflinePolicy } from './AuthOfflinePolicy';
|
|
3
|
+
import type { AuthSessionCacheAdapter } from './AuthSessionCacheAdapter';
|
|
4
|
+
export interface ServerAuthBridgeLoginResult {
|
|
5
|
+
status: 'authenticated' | 'rejected';
|
|
6
|
+
reasonCode?: string;
|
|
7
|
+
mode?: 'online' | 'offline';
|
|
8
|
+
session?: AuthoritySessionEnvelope;
|
|
9
|
+
}
|
|
10
|
+
export interface CreateServerAuthoritativeAuthBridgeOptions {
|
|
11
|
+
authority: AuthAuthorityAdapter;
|
|
12
|
+
cache: AuthSessionCacheAdapter;
|
|
13
|
+
verifyOfflineSecret: (input: {
|
|
14
|
+
principal: string;
|
|
15
|
+
secret: string;
|
|
16
|
+
}) => Promise<boolean>;
|
|
17
|
+
now?: () => string;
|
|
18
|
+
isOnline?: () => boolean;
|
|
19
|
+
offlinePolicy?: AuthOfflinePolicy;
|
|
20
|
+
}
|
|
21
|
+
export interface ServerAuthoritativeAuthBridge {
|
|
22
|
+
login(input: AuthorityLoginRequest): Promise<ServerAuthBridgeLoginResult>;
|
|
23
|
+
logout(principal: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createServerAuthoritativeAuthBridge(options: CreateServerAuthoritativeAuthBridgeOptions): ServerAuthoritativeAuthBridge;
|
|
@@ -4,5 +4,15 @@ export declare class OfauthDomainError extends Error {
|
|
|
4
4
|
readonly retryable: boolean;
|
|
5
5
|
constructor(code: AuthErrorCode, message: string, retryable?: boolean);
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Thrown by AuthAuthorityAdapter implementations when the server explicitly
|
|
9
|
+
* rejects a login/refresh request (e.g. wrong credentials, revoked token).
|
|
10
|
+
* Use instanceof AuthRejectionError in the bridge to distinguish authority
|
|
11
|
+
* rejections from network/infrastructure failures.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AuthRejectionError extends Error {
|
|
14
|
+
readonly code: string;
|
|
15
|
+
constructor(code: string);
|
|
16
|
+
}
|
|
7
17
|
export declare function unauthorized(code: AuthErrorCode, message: string): OfauthDomainError;
|
|
8
18
|
export declare function invalidState(message: string): OfauthDomainError;
|
|
@@ -7,6 +7,8 @@ export declare class DbAdapterSessionService implements SessionService {
|
|
|
7
7
|
private readonly options;
|
|
8
8
|
private readonly newId;
|
|
9
9
|
constructor(db: DbAdapter, options: ServiceRuntimeOptions);
|
|
10
|
+
/** Ambil semua assignment aktif untuk identityId, disaring ke tenant yang sama jika ada. */
|
|
11
|
+
private resolveGrantedAssignments;
|
|
10
12
|
createSession(input: CreateSessionRequest): Promise<SessionRef>;
|
|
11
13
|
refreshSession(input: RefreshSessionRequest): Promise<SessionRef>;
|
|
12
14
|
revokeSession(input: RevokeSessionRequest): Promise<void>;
|
|
@@ -88,7 +88,7 @@ export declare function isPast(iso: string): boolean;
|
|
|
88
88
|
export declare function makeIdFactory(prefix: string): () => string;
|
|
89
89
|
export declare function toIdentityRef(row: IdentityRow): IdentityRef;
|
|
90
90
|
export declare function toAssignment(row: AssignmentRow): ContextAssignment;
|
|
91
|
-
export declare function toSessionRef(row: SessionRow, assignment?: AssignmentRow | null): SessionRef;
|
|
91
|
+
export declare function toSessionRef(row: SessionRow, assignment?: AssignmentRow | null, grantedAssignments?: AssignmentRow[]): SessionRef;
|
|
92
92
|
export declare function verifySecret(platformAdapter: PlatformAdapter | undefined, plainSecret: string, secretHash: string): Promise<boolean>;
|
|
93
93
|
export declare function emitAuthActivity(options: ServiceRuntimeOptions, event: AuthAuditEvent): Promise<void>;
|
|
94
94
|
export declare function getAssignmentById(db: DbAdapter, id: string | null): Promise<AssignmentRow | null>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DbAdapter } from 'ofcore';
|
|
2
|
+
type HashAdapter = {
|
|
3
|
+
hashPin?: (pin: string) => Promise<string> | string;
|
|
4
|
+
};
|
|
5
|
+
type ScopeRef = {
|
|
6
|
+
tenantId?: string | null;
|
|
7
|
+
branchId?: string | null;
|
|
8
|
+
};
|
|
9
|
+
export interface LocalIdentityProvisioningInput {
|
|
10
|
+
dbAdapter: DbAdapter;
|
|
11
|
+
principal: string;
|
|
12
|
+
secret: string;
|
|
13
|
+
roleRef: string;
|
|
14
|
+
scope?: ScopeRef;
|
|
15
|
+
platformAdapter?: HashAdapter | null;
|
|
16
|
+
source?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface LocalIdentityProvisioningResult {
|
|
19
|
+
identityId: string;
|
|
20
|
+
assignmentId: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function upsertLocalIdentityAndAssignment(input: LocalIdentityProvisioningInput): Promise<LocalIdentityProvisioningResult>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ErrorDetail, ResponseEnvelope } from 'ofcore';
|
|
2
|
+
import type { AuthErrorCode } from '../contracts/AuthErrorContract';
|
|
3
|
+
export declare function toAuthSuccessEnvelope<T>(data: T, requestId?: string): ResponseEnvelope<T>;
|
|
4
|
+
export declare function toAuthFailureEnvelope<T = never>(code: AuthErrorCode, message: string, options?: {
|
|
5
|
+
details?: ErrorDetail[];
|
|
6
|
+
retryable?: boolean;
|
|
7
|
+
correlationId?: string;
|
|
8
|
+
requestId?: string;
|
|
9
|
+
}): ResponseEnvelope<T>;
|
|
10
|
+
export declare function mapAuthErrorToEnvelope<T = never>(error: unknown, fallbackCode?: AuthErrorCode, fallbackMessage?: string): ResponseEnvelope<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofauth-shared-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Offline-first auth shared-core (identity/session/context/assurance) for of* domain modules.",
|
|
6
6
|
"author": {
|
|
@@ -27,15 +27,16 @@
|
|
|
27
27
|
"test": "NODE_PATH=.. npm run build && NODE_PATH=.. node --test ./tests/*.test.js",
|
|
28
28
|
"verify:contract": "node ./scripts/verify-surface.js",
|
|
29
29
|
"verify:surface": "node ./scripts/verify-surface.js",
|
|
30
|
+
"verify:exception-audit": "node ./scripts/verify-exception-audit.js",
|
|
30
31
|
"verify:logic": "NODE_PATH=.. npm run build && NODE_PATH=.. node ./scripts/verify-logic.js",
|
|
31
32
|
"verify:factory-boundary": "node ./scripts/verify-no-service-logic-in-factory.js",
|
|
32
|
-
"ci:check": "npm run typecheck && npm run verify:contract && npm run test && npm run verify:logic && npm run verify:factory-boundary",
|
|
33
|
+
"ci:check": "npm run typecheck && npm run verify:contract && npm run verify:exception-audit && npm run test && npm run verify:logic && npm run verify:factory-boundary",
|
|
33
34
|
"prepublishOnly": "npm run ci:check",
|
|
34
35
|
"prepack": "npm run build",
|
|
35
36
|
"bundle": "node esbuild.config.js"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"ofcore": "0.
|
|
39
|
+
"ofcore": "0.2.0-alpha.1"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"typescript": "^5.9.3",
|