ofauth-shared-core 0.1.0-alpha.0 → 0.2.0-alpha.0
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 -0
- package/dist/index.js +1 -36
- 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 +21 -10
- package/dist/OfauthCore.js +0 -200
- package/dist/contracts/AuthAuditContract.js +0 -16
- package/dist/contracts/AuthErrorContract.js +0 -2
- package/dist/contracts/AuthPolicyContract.js +0 -2
- package/dist/contracts/ContextContract.js +0 -8
- package/dist/contracts/IdentityContract.js +0 -2
- package/dist/contracts/SessionContract.js +0 -10
- package/dist/data/applyPendingMigrations.js +0 -30
- package/dist/data/migrations.js +0 -47
- package/dist/data/schemas.js +0 -109
- package/dist/runtime/ContractStage.js +0 -4
- package/dist/services/AuthAuditService.js +0 -2
- package/dist/services/AuthHostComposition.js +0 -100
- package/dist/services/AuthPolicyService.js +0 -2
- package/dist/services/ContextService.js +0 -2
- package/dist/services/IdentityService.js +0 -2
- package/dist/services/SessionService.js +0 -2
- package/dist/services/createContractOnlyOfauthServices.js +0 -82
- package/dist/services/createDbAdapterOfauthServices.js +0 -22
- package/dist/services/errors.js +0 -20
- package/dist/services/impl/DbAdapterAuthAuditService.js +0 -107
- package/dist/services/impl/DbAdapterAuthPolicyService.js +0 -114
- package/dist/services/impl/DbAdapterContextService.js +0 -79
- package/dist/services/impl/DbAdapterIdentityService.js +0 -146
- package/dist/services/impl/DbAdapterSessionService.js +0 -63
- package/dist/services/impl/runtimeSupport.js +0 -112
|
@@ -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,35 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofauth-shared-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-alpha.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Offline-first auth shared-core (identity/session/context/assurance) for of* domain modules.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Agus Made",
|
|
8
|
+
"email": "krisnaparta@gmail.com"
|
|
9
|
+
},
|
|
6
10
|
"main": "dist/index.js",
|
|
11
|
+
"module": "dist/index.esm.js",
|
|
7
12
|
"types": "dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.esm.js",
|
|
17
|
+
"require": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
8
20
|
"files": [
|
|
9
21
|
"dist"
|
|
10
22
|
],
|
|
11
23
|
"scripts": {
|
|
12
24
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
13
|
-
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
25
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && node esbuild.config.js",
|
|
14
26
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
15
27
|
"test": "NODE_PATH=.. npm run build && NODE_PATH=.. node --test ./tests/*.test.js",
|
|
16
28
|
"verify:contract": "node ./scripts/verify-surface.js",
|
|
17
29
|
"verify:surface": "node ./scripts/verify-surface.js",
|
|
30
|
+
"verify:exception-audit": "node ./scripts/verify-exception-audit.js",
|
|
18
31
|
"verify:logic": "NODE_PATH=.. npm run build && NODE_PATH=.. node ./scripts/verify-logic.js",
|
|
19
32
|
"verify:factory-boundary": "node ./scripts/verify-no-service-logic-in-factory.js",
|
|
20
|
-
"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",
|
|
21
34
|
"prepublishOnly": "npm run ci:check",
|
|
22
|
-
"prepack": "npm run build"
|
|
35
|
+
"prepack": "npm run build",
|
|
36
|
+
"bundle": "node esbuild.config.js"
|
|
23
37
|
},
|
|
24
38
|
"dependencies": {
|
|
25
|
-
"ofcore": "0.
|
|
39
|
+
"ofcore": "0.2.0-alpha.0"
|
|
26
40
|
},
|
|
27
41
|
"devDependencies": {
|
|
28
|
-
"typescript": "^5.9.3"
|
|
29
|
-
|
|
30
|
-
"author": {
|
|
31
|
-
"name": "Agus Made",
|
|
32
|
-
"email": "krisnaparta@gmail.com"
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"esbuild": "^0.25.11"
|
|
33
44
|
},
|
|
34
45
|
"publishConfig": {
|
|
35
46
|
"access": "public"
|
package/dist/OfauthCore.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OfauthCoreBuilder = exports.OfauthCore = void 0;
|
|
4
|
-
const ofcore_1 = require("ofcore");
|
|
5
|
-
const ofcore_2 = require("ofcore");
|
|
6
|
-
const ofcore_3 = require("ofcore");
|
|
7
|
-
const createDbAdapterOfauthServices_1 = require("./services/createDbAdapterOfauthServices");
|
|
8
|
-
const applyPendingMigrations_1 = require("./data/applyPendingMigrations");
|
|
9
|
-
class OfauthCore {
|
|
10
|
-
constructor(runtime, runtimeStateStore) {
|
|
11
|
-
this.runtime = runtime;
|
|
12
|
-
this.runtimeStateStore = runtimeStateStore;
|
|
13
|
-
this.runtimeStore = (0, ofcore_3.asReadonlyStore)(this.runtimeStateStore);
|
|
14
|
-
}
|
|
15
|
-
static builder() {
|
|
16
|
-
return new OfauthCoreBuilder();
|
|
17
|
-
}
|
|
18
|
-
get registry() {
|
|
19
|
-
return this.runtime.registry;
|
|
20
|
-
}
|
|
21
|
-
async start(options = {}) {
|
|
22
|
-
this.runtimeStateStore.setState({
|
|
23
|
-
phase: 'starting',
|
|
24
|
-
started: false,
|
|
25
|
-
lastError: null,
|
|
26
|
-
lastTransitionAt: new Date().toISOString(),
|
|
27
|
-
});
|
|
28
|
-
try {
|
|
29
|
-
await this.runtime.start(options);
|
|
30
|
-
this.domainServices = this.runtime.domainServices;
|
|
31
|
-
this.runtimeStateStore.setState((state) => ({
|
|
32
|
-
...state,
|
|
33
|
-
phase: 'started',
|
|
34
|
-
started: true,
|
|
35
|
-
startCount: state.startCount + 1,
|
|
36
|
-
lastError: null,
|
|
37
|
-
lastTransitionAt: new Date().toISOString(),
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
this.runtimeStateStore.setState({
|
|
42
|
-
phase: 'error',
|
|
43
|
-
started: false,
|
|
44
|
-
lastError: error instanceof Error ? error.message : String(error),
|
|
45
|
-
lastTransitionAt: new Date().toISOString(),
|
|
46
|
-
});
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async init(options = {}) {
|
|
51
|
-
this.runtimeStateStore.setState({
|
|
52
|
-
phase: 'starting',
|
|
53
|
-
started: false,
|
|
54
|
-
lastError: null,
|
|
55
|
-
lastTransitionAt: new Date().toISOString(),
|
|
56
|
-
});
|
|
57
|
-
try {
|
|
58
|
-
await this.runtime.init(options);
|
|
59
|
-
this.domainServices = this.runtime.domainServices;
|
|
60
|
-
this.runtimeStateStore.setState((state) => ({
|
|
61
|
-
...state,
|
|
62
|
-
phase: 'started',
|
|
63
|
-
started: true,
|
|
64
|
-
startCount: state.startCount + 1,
|
|
65
|
-
lastError: null,
|
|
66
|
-
lastTransitionAt: new Date().toISOString(),
|
|
67
|
-
}));
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
this.runtimeStateStore.setState({
|
|
71
|
-
phase: 'error',
|
|
72
|
-
started: false,
|
|
73
|
-
lastError: error instanceof Error ? error.message : String(error),
|
|
74
|
-
lastTransitionAt: new Date().toISOString(),
|
|
75
|
-
});
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
async stop() {
|
|
80
|
-
this.runtimeStateStore.setState({
|
|
81
|
-
phase: 'stopping',
|
|
82
|
-
started: this.runtime.isStarted(),
|
|
83
|
-
lastError: null,
|
|
84
|
-
lastTransitionAt: new Date().toISOString(),
|
|
85
|
-
});
|
|
86
|
-
try {
|
|
87
|
-
await this.runtime.stop();
|
|
88
|
-
this.runtimeStateStore.setState((state) => ({
|
|
89
|
-
...state,
|
|
90
|
-
phase: 'stopped',
|
|
91
|
-
started: false,
|
|
92
|
-
stopCount: state.stopCount + 1,
|
|
93
|
-
lastError: null,
|
|
94
|
-
lastTransitionAt: new Date().toISOString(),
|
|
95
|
-
}));
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
this.runtimeStateStore.setState({
|
|
99
|
-
phase: 'error',
|
|
100
|
-
started: this.runtime.isStarted(),
|
|
101
|
-
lastError: error instanceof Error ? error.message : String(error),
|
|
102
|
-
lastTransitionAt: new Date().toISOString(),
|
|
103
|
-
});
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
isStarted() {
|
|
108
|
-
return this.runtime.isStarted();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
exports.OfauthCore = OfauthCore;
|
|
112
|
-
class OfauthCoreBuilder {
|
|
113
|
-
constructor() {
|
|
114
|
-
this.runtimeBuilder = ofcore_1.CoreRuntime.builder();
|
|
115
|
-
this.domainServiceOverrides = {};
|
|
116
|
-
this.runtimeHooks = {};
|
|
117
|
-
this.runtimeBuilder.withDbAdapter(() => new ofcore_2.InMemoryDbAdapter());
|
|
118
|
-
}
|
|
119
|
-
withPlatformAdapter(factory) {
|
|
120
|
-
this.runtimeBuilder.withPlatformAdapter(factory);
|
|
121
|
-
return this;
|
|
122
|
-
}
|
|
123
|
-
withDbAdapter(factory) {
|
|
124
|
-
this.runtimeBuilder.withDbAdapter(factory);
|
|
125
|
-
return this;
|
|
126
|
-
}
|
|
127
|
-
withHttpAdapter(factory) {
|
|
128
|
-
this.runtimeBuilder.withHttpAdapter(factory);
|
|
129
|
-
return this;
|
|
130
|
-
}
|
|
131
|
-
withSocketAdapter(factory) {
|
|
132
|
-
this.runtimeBuilder.withSocketAdapter(factory);
|
|
133
|
-
return this;
|
|
134
|
-
}
|
|
135
|
-
withLoggerAdapter(factory) {
|
|
136
|
-
this.runtimeBuilder.withLoggerAdapter(factory);
|
|
137
|
-
return this;
|
|
138
|
-
}
|
|
139
|
-
withActivitySink(factory) {
|
|
140
|
-
this.runtimeBuilder.withActivitySink(factory);
|
|
141
|
-
return this;
|
|
142
|
-
}
|
|
143
|
-
withExtension(name, factory) {
|
|
144
|
-
this.runtimeBuilder.withExtension(name, factory);
|
|
145
|
-
return this;
|
|
146
|
-
}
|
|
147
|
-
withDomainServicesFactory(factory) {
|
|
148
|
-
this.domainServicesFactory = factory;
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
overrideDomainServices(overrides) {
|
|
152
|
-
this.domainServiceOverrides = { ...this.domainServiceOverrides, ...overrides };
|
|
153
|
-
return this;
|
|
154
|
-
}
|
|
155
|
-
withRuntimeHooks(hooks) {
|
|
156
|
-
this.runtimeHooks = { ...this.runtimeHooks, ...hooks };
|
|
157
|
-
return this;
|
|
158
|
-
}
|
|
159
|
-
withMigrationRunner(runner) {
|
|
160
|
-
return this.withRuntimeHooks({ runMigrations: runner });
|
|
161
|
-
}
|
|
162
|
-
withSeedRunner(runner) {
|
|
163
|
-
return this.withRuntimeHooks({ runSeed: runner });
|
|
164
|
-
}
|
|
165
|
-
build() {
|
|
166
|
-
const runtimeStateStore = (0, ofcore_3.createStore)({
|
|
167
|
-
phase: 'idle',
|
|
168
|
-
started: false,
|
|
169
|
-
startCount: 0,
|
|
170
|
-
stopCount: 0,
|
|
171
|
-
lastError: null,
|
|
172
|
-
lastTransitionAt: new Date().toISOString(),
|
|
173
|
-
});
|
|
174
|
-
const defaultRunMigrations = async (runtime) => {
|
|
175
|
-
const dbAdapter = runtime.registry?.dbAdapter;
|
|
176
|
-
if (!dbAdapter)
|
|
177
|
-
return;
|
|
178
|
-
await (0, applyPendingMigrations_1.applyPendingMigrations)(dbAdapter, runtime.registry?.loggerAdapter);
|
|
179
|
-
};
|
|
180
|
-
this.runtimeBuilder.withHooks({
|
|
181
|
-
...this.runtimeHooks,
|
|
182
|
-
runMigrations: this.runtimeHooks.runMigrations ?? defaultRunMigrations,
|
|
183
|
-
createDomainServices: async (runtime) => {
|
|
184
|
-
const base = this.domainServicesFactory
|
|
185
|
-
? await this.domainServicesFactory()
|
|
186
|
-
: await (0, createDbAdapterOfauthServices_1.createDbAdapterOfauthServices)(runtime.registry?.dbAdapter ?? new ofcore_2.InMemoryDbAdapter(), {
|
|
187
|
-
logger: runtime.registry?.loggerAdapter,
|
|
188
|
-
platformAdapter: runtime.registry?.platformAdapter,
|
|
189
|
-
emitActivity: async (record) => {
|
|
190
|
-
await runtime.emitActivity(record);
|
|
191
|
-
},
|
|
192
|
-
});
|
|
193
|
-
return { ...base, ...this.domainServiceOverrides };
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
const runtime = this.runtimeBuilder.build();
|
|
197
|
-
return new OfauthCore(runtime, runtimeStateStore);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
exports.OfauthCoreBuilder = OfauthCoreBuilder;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAuthAuditEventType = isAuthAuditEventType;
|
|
4
|
-
function isAuthAuditEventType(value) {
|
|
5
|
-
const allowed = [
|
|
6
|
-
'LOGIN_SUCCESS',
|
|
7
|
-
'LOGIN_FAILED',
|
|
8
|
-
'LOCKOUT_TRIGGERED',
|
|
9
|
-
'SESSION_REVOKED',
|
|
10
|
-
'CONTEXT_SWITCHED',
|
|
11
|
-
'STEP_UP_CHALLENGED',
|
|
12
|
-
'STEP_UP_PASSED',
|
|
13
|
-
'STEP_UP_FAILED',
|
|
14
|
-
];
|
|
15
|
-
return allowed.includes(value);
|
|
16
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasAnyScopeDimension = hasAnyScopeDimension;
|
|
4
|
-
function hasAnyScopeDimension(scope) {
|
|
5
|
-
return Boolean(scope.tenantId ||
|
|
6
|
-
scope.branchId ||
|
|
7
|
-
(scope.attributes && Object.keys(scope.attributes).length > 0));
|
|
8
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAssuranceLevelAtLeast = isAssuranceLevelAtLeast;
|
|
4
|
-
function isAssuranceLevelAtLeast(current, required) {
|
|
5
|
-
const rank = {
|
|
6
|
-
basic: 1,
|
|
7
|
-
elevated: 2,
|
|
8
|
-
};
|
|
9
|
-
return rank[current] >= rank[required];
|
|
10
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.applyPendingMigrations = applyPendingMigrations;
|
|
4
|
-
const migrations_1 = require("./migrations");
|
|
5
|
-
const noopLogger = {
|
|
6
|
-
logInfo() { },
|
|
7
|
-
logWarn() { },
|
|
8
|
-
logError() { },
|
|
9
|
-
};
|
|
10
|
-
async function applyPendingMigrations(adapter, logger = noopLogger) {
|
|
11
|
-
logger.logInfo('[ofauth] starting database migration process');
|
|
12
|
-
const sorted = [...migrations_1.migrations].sort((a, b) => a.toVersion - b.toVersion);
|
|
13
|
-
const targetVersion = sorted.length > 0 ? sorted[sorted.length - 1].toVersion : 0;
|
|
14
|
-
let currentVersion = await adapter.getSchemaVersion();
|
|
15
|
-
if (currentVersion == null || currentVersion < 0)
|
|
16
|
-
currentVersion = 0;
|
|
17
|
-
if (currentVersion >= targetVersion) {
|
|
18
|
-
logger.logInfo(`[ofauth] database already up-to-date at v${currentVersion}`);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
for (const migration of sorted) {
|
|
22
|
-
if (migration.toVersion > currentVersion) {
|
|
23
|
-
logger.logInfo(`[ofauth] applying migration v${migration.toVersion}`);
|
|
24
|
-
await migration.up(adapter);
|
|
25
|
-
await adapter.setSchemaVersion(migration.toVersion);
|
|
26
|
-
currentVersion = migration.toVersion;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
logger.logInfo(`[ofauth] migration completed at v${currentVersion}`);
|
|
30
|
-
}
|
package/dist/data/migrations.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.migrations = void 0;
|
|
4
|
-
const schemas_1 = require("./schemas");
|
|
5
|
-
exports.migrations = [
|
|
6
|
-
{
|
|
7
|
-
toVersion: 1,
|
|
8
|
-
up: async (adapter) => {
|
|
9
|
-
const tables = (0, schemas_1.getOfauthTableSchemas)();
|
|
10
|
-
for (const table of tables) {
|
|
11
|
-
try {
|
|
12
|
-
await adapter.addTable(table);
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
// keep migration idempotent for adapters that throw when table exists
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
toVersion: 2,
|
|
22
|
-
up: async (adapter) => {
|
|
23
|
-
try {
|
|
24
|
-
await adapter.addColumn(schemas_1.OFAUTH_TABLES.auditEvents, {
|
|
25
|
-
name: 'syncStatus',
|
|
26
|
-
type: 'string',
|
|
27
|
-
enum: ['pending', 'replayed'],
|
|
28
|
-
isIndexed: true,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
// keep migration idempotent
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
await adapter.addColumn(schemas_1.OFAUTH_TABLES.auditEvents, {
|
|
36
|
-
name: 'replayedAt',
|
|
37
|
-
type: 'string',
|
|
38
|
-
isOptional: true,
|
|
39
|
-
isIndexed: true,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// keep migration idempotent
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
];
|
package/dist/data/schemas.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ofauthSchema = exports.OFAUTH_TABLES = void 0;
|
|
4
|
-
exports.getOfauthTableSchemas = getOfauthTableSchemas;
|
|
5
|
-
exports.OFAUTH_TABLES = {
|
|
6
|
-
identities: 'ofauth_identities',
|
|
7
|
-
assignments: 'ofauth_assignments',
|
|
8
|
-
sessions: 'ofauth_sessions',
|
|
9
|
-
policyProfiles: 'ofauth_policy_profiles',
|
|
10
|
-
policyStepups: 'ofauth_policy_stepups',
|
|
11
|
-
auditEvents: 'ofauth_audit_events',
|
|
12
|
-
};
|
|
13
|
-
const tableSchemas = [
|
|
14
|
-
{
|
|
15
|
-
name: exports.OFAUTH_TABLES.identities,
|
|
16
|
-
columns: [
|
|
17
|
-
{ name: 'id', type: 'string' },
|
|
18
|
-
{ name: 'principal', type: 'string', isIndexed: true },
|
|
19
|
-
{ name: 'secretHash', type: 'string' },
|
|
20
|
-
{ name: 'verifyMethod', type: 'string', enum: ['pin', 'password', 'custom'] },
|
|
21
|
-
{ name: 'status', type: 'string', enum: ['active', 'disabled', 'locked'], isIndexed: true },
|
|
22
|
-
{ name: 'failedAttempts', type: 'number' },
|
|
23
|
-
{ name: 'lockoutUntil', type: 'string', isOptional: true },
|
|
24
|
-
{ name: 'version', type: 'number' },
|
|
25
|
-
{ name: 'lastModified', type: 'string' },
|
|
26
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: exports.OFAUTH_TABLES.assignments,
|
|
31
|
-
columns: [
|
|
32
|
-
{ name: 'id', type: 'string' },
|
|
33
|
-
{ name: 'identityId', type: 'string', isIndexed: true },
|
|
34
|
-
{ name: 'roleRef', type: 'string', isIndexed: true },
|
|
35
|
-
{ name: 'tenantId', type: 'string', isOptional: true, isIndexed: true },
|
|
36
|
-
{ name: 'branchId', type: 'string', isOptional: true, isIndexed: true },
|
|
37
|
-
{ name: 'scopeAttributes', type: 'json', isOptional: true },
|
|
38
|
-
{ name: 'version', type: 'number' },
|
|
39
|
-
{ name: 'lastModified', type: 'string' },
|
|
40
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: exports.OFAUTH_TABLES.sessions,
|
|
45
|
-
columns: [
|
|
46
|
-
{ name: 'id', type: 'string' },
|
|
47
|
-
{ name: 'identityId', type: 'string', isIndexed: true },
|
|
48
|
-
{ name: 'activeAssignmentId', type: 'string', isOptional: true, isIndexed: true },
|
|
49
|
-
{ name: 'assuranceLevel', type: 'string', enum: ['basic', 'elevated'], isIndexed: true },
|
|
50
|
-
{ name: 'issuedAt', type: 'string' },
|
|
51
|
-
{ name: 'expiresAt', type: 'string', isIndexed: true },
|
|
52
|
-
{ name: 'revokedAt', type: 'string', isOptional: true },
|
|
53
|
-
{ name: 'version', type: 'number' },
|
|
54
|
-
{ name: 'lastModified', type: 'string' },
|
|
55
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: exports.OFAUTH_TABLES.policyProfiles,
|
|
60
|
-
columns: [
|
|
61
|
-
{ name: 'id', type: 'string' },
|
|
62
|
-
{ name: 'profileId', type: 'string', isIndexed: true },
|
|
63
|
-
{ name: 'defaultAssuranceLevel', type: 'string', enum: ['basic', 'elevated'] },
|
|
64
|
-
{ name: 'lockoutPolicyRef', type: 'string' },
|
|
65
|
-
{ name: 'version', type: 'number' },
|
|
66
|
-
{ name: 'lastModified', type: 'string' },
|
|
67
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
68
|
-
],
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: exports.OFAUTH_TABLES.policyStepups,
|
|
72
|
-
columns: [
|
|
73
|
-
{ name: 'id', type: 'string' },
|
|
74
|
-
{ name: 'actionRef', type: 'string', isIndexed: true },
|
|
75
|
-
{ name: 'requiredAssuranceLevel', type: 'string', enum: ['basic', 'elevated'] },
|
|
76
|
-
{ name: 'reauthWindowSeconds', type: 'number', isOptional: true },
|
|
77
|
-
{ name: 'version', type: 'number' },
|
|
78
|
-
{ name: 'lastModified', type: 'string' },
|
|
79
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
80
|
-
],
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: exports.OFAUTH_TABLES.auditEvents,
|
|
84
|
-
columns: [
|
|
85
|
-
{ name: 'id', type: 'string' },
|
|
86
|
-
{ name: 'eventType', type: 'string', isIndexed: true },
|
|
87
|
-
{ name: 'identityId', type: 'string', isOptional: true, isIndexed: true },
|
|
88
|
-
{ name: 'sessionId', type: 'string', isOptional: true, isIndexed: true },
|
|
89
|
-
{ name: 'tenantId', type: 'string', isOptional: true, isIndexed: true },
|
|
90
|
-
{ name: 'branchId', type: 'string', isOptional: true, isIndexed: true },
|
|
91
|
-
{ name: 'scopeAttributes', type: 'json', isOptional: true },
|
|
92
|
-
{ name: 'result', type: 'string', enum: ['success', 'failed'], isIndexed: true },
|
|
93
|
-
{ name: 'reasonCode', type: 'string', isOptional: true },
|
|
94
|
-
{ name: 'timestamp', type: 'string', isIndexed: true },
|
|
95
|
-
{ name: 'syncStatus', type: 'string', enum: ['pending', 'replayed'], isIndexed: true },
|
|
96
|
-
{ name: 'replayedAt', type: 'string', isOptional: true, isIndexed: true },
|
|
97
|
-
{ name: 'version', type: 'number' },
|
|
98
|
-
{ name: 'lastModified', type: 'string' },
|
|
99
|
-
{ name: 'deleted', type: 'boolean', isIndexed: true },
|
|
100
|
-
],
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
exports.ofauthSchema = {
|
|
104
|
-
version: 2,
|
|
105
|
-
tables: tableSchemas,
|
|
106
|
-
};
|
|
107
|
-
function getOfauthTableSchemas() {
|
|
108
|
-
return exports.ofauthSchema.tables;
|
|
109
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loginWithResolvedContext = loginWithResolvedContext;
|
|
4
|
-
exports.refreshResolvedContext = refreshResolvedContext;
|
|
5
|
-
exports.switchContextWithResolvedState = switchContextWithResolvedState;
|
|
6
|
-
exports.evaluateProtectedRouteGate = evaluateProtectedRouteGate;
|
|
7
|
-
const SessionContract_1 = require("../contracts/SessionContract");
|
|
8
|
-
function findAssignment(assignments, assignmentId) {
|
|
9
|
-
if (!assignmentId)
|
|
10
|
-
return undefined;
|
|
11
|
-
return assignments.find((row) => row.assignmentId === assignmentId);
|
|
12
|
-
}
|
|
13
|
-
function isScopeAllowed(scope, policy) {
|
|
14
|
-
if (policy.requiredTenantId && scope.tenantId !== policy.requiredTenantId)
|
|
15
|
-
return false;
|
|
16
|
-
if (policy.requiredBranchId && scope.branchId !== policy.requiredBranchId)
|
|
17
|
-
return false;
|
|
18
|
-
if (policy.requiredScopeAttributes) {
|
|
19
|
-
const attrs = scope.attributes ?? {};
|
|
20
|
-
for (const [key, value] of Object.entries(policy.requiredScopeAttributes)) {
|
|
21
|
-
if (attrs[key] !== value)
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
async function buildResolvedContext(services, input) {
|
|
28
|
-
const session = await services.sessionService.getActiveSession(input.sessionId);
|
|
29
|
-
if (!session)
|
|
30
|
-
return null;
|
|
31
|
-
const assignments = await services.contextService.listAssignments(input.identityId);
|
|
32
|
-
let activeContext = await services.contextService.getActiveContext(input.sessionId);
|
|
33
|
-
if (!activeContext && assignments[0]) {
|
|
34
|
-
const switched = await services.contextService.switchContext({
|
|
35
|
-
sessionId: input.sessionId,
|
|
36
|
-
targetAssignmentId: assignments[0].assignmentId,
|
|
37
|
-
...(input.assignmentFallbackReason ? { reasonCode: input.assignmentFallbackReason } : {}),
|
|
38
|
-
});
|
|
39
|
-
activeContext = switched.activeContext;
|
|
40
|
-
}
|
|
41
|
-
const activeAssignment = findAssignment(assignments, activeContext?.assignmentId);
|
|
42
|
-
return {
|
|
43
|
-
sessionId: input.sessionId,
|
|
44
|
-
identityId: input.identityId,
|
|
45
|
-
principal: input.principal,
|
|
46
|
-
assignments,
|
|
47
|
-
activeContextAssignmentId: activeContext?.assignmentId,
|
|
48
|
-
activeAssignment,
|
|
49
|
-
scopeRef: activeContext?.scopeRef ?? activeAssignment?.scopeRef ?? session.activeScopeRef ?? {},
|
|
50
|
-
roleRef: activeAssignment?.roleRef,
|
|
51
|
-
assuranceLevel: session.assuranceLevel,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
async function loginWithResolvedContext(services, input) {
|
|
55
|
-
const login = await services.identityService.login(input);
|
|
56
|
-
const resolved = await buildResolvedContext(services, {
|
|
57
|
-
sessionId: login.sessionId,
|
|
58
|
-
identityId: login.identity.identityId,
|
|
59
|
-
principal: login.identity.principal,
|
|
60
|
-
assignmentFallbackReason: 'AUTO_CONTEXT_AFTER_LOGIN',
|
|
61
|
-
});
|
|
62
|
-
if (!resolved) {
|
|
63
|
-
throw new Error('session is not active after login');
|
|
64
|
-
}
|
|
65
|
-
return resolved;
|
|
66
|
-
}
|
|
67
|
-
async function refreshResolvedContext(services, input) {
|
|
68
|
-
return buildResolvedContext(services, input);
|
|
69
|
-
}
|
|
70
|
-
async function switchContextWithResolvedState(services, input) {
|
|
71
|
-
await services.contextService.switchContext({
|
|
72
|
-
sessionId: input.sessionId,
|
|
73
|
-
targetAssignmentId: input.targetAssignmentId,
|
|
74
|
-
...(input.reasonCode ? { reasonCode: input.reasonCode } : {}),
|
|
75
|
-
});
|
|
76
|
-
const resolved = await buildResolvedContext(services, input);
|
|
77
|
-
if (!resolved) {
|
|
78
|
-
throw new Error('session is not active while switching context');
|
|
79
|
-
}
|
|
80
|
-
return resolved;
|
|
81
|
-
}
|
|
82
|
-
function evaluateProtectedRouteGate(resolved, policy = {}) {
|
|
83
|
-
if (!resolved) {
|
|
84
|
-
return { allowed: false, reasonCode: 'NO_SESSION' };
|
|
85
|
-
}
|
|
86
|
-
const minimumAssurance = policy.minimumAssuranceLevel ?? 'basic';
|
|
87
|
-
if (!(0, SessionContract_1.isAssuranceLevelAtLeast)(resolved.assuranceLevel, minimumAssurance)) {
|
|
88
|
-
return { allowed: false, reasonCode: 'ASSURANCE_TOO_LOW' };
|
|
89
|
-
}
|
|
90
|
-
if (policy.requiredRoleRefs?.length) {
|
|
91
|
-
const hasRole = !!resolved.roleRef && policy.requiredRoleRefs.includes(resolved.roleRef);
|
|
92
|
-
if (!hasRole) {
|
|
93
|
-
return { allowed: false, reasonCode: 'ROLE_FORBIDDEN' };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
if (!isScopeAllowed(resolved.scopeRef, policy)) {
|
|
97
|
-
return { allowed: false, reasonCode: 'SCOPE_FORBIDDEN' };
|
|
98
|
-
}
|
|
99
|
-
return { allowed: true };
|
|
100
|
-
}
|