ofauth-shared-core 0.1.0-alpha.0 → 0.1.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/dist/index.esm.js +1 -0
- package/dist/index.js +1 -36
- package/package.json +18 -8
- 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
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbAdapterIdentityService = void 0;
|
|
4
|
-
const schemas_1 = require("../../data/schemas");
|
|
5
|
-
const errors_1 = require("../errors");
|
|
6
|
-
const runtimeSupport_1 = require("./runtimeSupport");
|
|
7
|
-
class DbAdapterIdentityService {
|
|
8
|
-
constructor(db, options, sessionService, contextService, authAuditService) {
|
|
9
|
-
this.db = db;
|
|
10
|
-
this.options = options;
|
|
11
|
-
this.sessionService = sessionService;
|
|
12
|
-
this.contextService = contextService;
|
|
13
|
-
this.authAuditService = authAuditService;
|
|
14
|
-
this.newEventId = (0, runtimeSupport_1.makeIdFactory)('auth-event');
|
|
15
|
-
}
|
|
16
|
-
async findIdentityByPrincipal(principal) {
|
|
17
|
-
const rows = await this.db.query(schemas_1.OFAUTH_TABLES.identities, {
|
|
18
|
-
filters: { principal, deleted: false },
|
|
19
|
-
limit: 1,
|
|
20
|
-
});
|
|
21
|
-
return rows[0] ?? null;
|
|
22
|
-
}
|
|
23
|
-
async persistFailedAttempt(identity) {
|
|
24
|
-
const failedAttempts = identity.failedAttempts + 1;
|
|
25
|
-
const lockoutTriggered = failedAttempts >= runtimeSupport_1.DEFAULT_LOCKOUT_THRESHOLD;
|
|
26
|
-
const lockoutUntil = lockoutTriggered ? (0, runtimeSupport_1.plusSecondsIso)((0, runtimeSupport_1.nowIso)(), runtimeSupport_1.DEFAULT_LOCKOUT_COOLDOWN_SECONDS) : null;
|
|
27
|
-
const status = lockoutTriggered ? 'locked' : identity.status;
|
|
28
|
-
return this.db.update(schemas_1.OFAUTH_TABLES.identities, identity.id, {
|
|
29
|
-
failedAttempts,
|
|
30
|
-
lockoutUntil,
|
|
31
|
-
status,
|
|
32
|
-
version: identity.version + 1,
|
|
33
|
-
lastModified: (0, runtimeSupport_1.nowIso)(),
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
async clearFailedAttempt(identity) {
|
|
37
|
-
if (identity.failedAttempts === 0 && !identity.lockoutUntil && identity.status === 'active')
|
|
38
|
-
return;
|
|
39
|
-
await this.db.update(schemas_1.OFAUTH_TABLES.identities, identity.id, {
|
|
40
|
-
failedAttempts: 0,
|
|
41
|
-
lockoutUntil: null,
|
|
42
|
-
status: 'active',
|
|
43
|
-
version: identity.version + 1,
|
|
44
|
-
lastModified: (0, runtimeSupport_1.nowIso)(),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
async emitAudit(event) {
|
|
48
|
-
await this.authAuditService.appendEvent(event);
|
|
49
|
-
}
|
|
50
|
-
async verifyCredential(input) {
|
|
51
|
-
const identity = await this.findIdentityByPrincipal(input.principal);
|
|
52
|
-
if (!identity) {
|
|
53
|
-
await this.emitAudit({
|
|
54
|
-
eventId: this.newEventId(),
|
|
55
|
-
eventType: 'LOGIN_FAILED',
|
|
56
|
-
result: 'failed',
|
|
57
|
-
reasonCode: 'INVALID_CREDENTIAL',
|
|
58
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
59
|
-
});
|
|
60
|
-
return { ok: false, reasonCode: 'INVALID_CREDENTIAL', retryable: true };
|
|
61
|
-
}
|
|
62
|
-
if (identity.status === 'disabled') {
|
|
63
|
-
await this.emitAudit({
|
|
64
|
-
eventId: this.newEventId(),
|
|
65
|
-
eventType: 'LOGIN_FAILED',
|
|
66
|
-
identityId: identity.id,
|
|
67
|
-
result: 'failed',
|
|
68
|
-
reasonCode: 'IDENTITY_DISABLED',
|
|
69
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
70
|
-
});
|
|
71
|
-
return { ok: false, reasonCode: 'IDENTITY_DISABLED', retryable: false };
|
|
72
|
-
}
|
|
73
|
-
if (identity.lockoutUntil && !(0, runtimeSupport_1.isPast)(identity.lockoutUntil)) {
|
|
74
|
-
await this.emitAudit({
|
|
75
|
-
eventId: this.newEventId(),
|
|
76
|
-
eventType: 'LOCKOUT_TRIGGERED',
|
|
77
|
-
identityId: identity.id,
|
|
78
|
-
result: 'failed',
|
|
79
|
-
reasonCode: 'IDENTITY_LOCKED',
|
|
80
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
81
|
-
});
|
|
82
|
-
return { ok: false, reasonCode: 'IDENTITY_LOCKED', retryable: false };
|
|
83
|
-
}
|
|
84
|
-
const verified = await (0, runtimeSupport_1.verifySecret)(this.options.platformAdapter, input.secret, identity.secretHash);
|
|
85
|
-
if (!verified) {
|
|
86
|
-
const afterFail = await this.persistFailedAttempt(identity);
|
|
87
|
-
const reasonCode = afterFail.status === 'locked' ? 'IDENTITY_LOCKED' : 'INVALID_CREDENTIAL';
|
|
88
|
-
await this.emitAudit({
|
|
89
|
-
eventId: this.newEventId(),
|
|
90
|
-
eventType: reasonCode === 'IDENTITY_LOCKED' ? 'LOCKOUT_TRIGGERED' : 'LOGIN_FAILED',
|
|
91
|
-
identityId: identity.id,
|
|
92
|
-
result: 'failed',
|
|
93
|
-
reasonCode,
|
|
94
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
95
|
-
});
|
|
96
|
-
return { ok: false, reasonCode, retryable: reasonCode === 'INVALID_CREDENTIAL' };
|
|
97
|
-
}
|
|
98
|
-
await this.clearFailedAttempt(identity);
|
|
99
|
-
const assignments = await this.contextService.listAssignments(identity.id);
|
|
100
|
-
await this.emitAudit({
|
|
101
|
-
eventId: this.newEventId(),
|
|
102
|
-
eventType: 'LOGIN_SUCCESS',
|
|
103
|
-
identityId: identity.id,
|
|
104
|
-
scopeRef: input.scopeRef,
|
|
105
|
-
result: 'success',
|
|
106
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
107
|
-
});
|
|
108
|
-
return {
|
|
109
|
-
ok: true,
|
|
110
|
-
identity: (0, runtimeSupport_1.toIdentityRef)(identity),
|
|
111
|
-
assignments,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
async login(input) {
|
|
115
|
-
const verified = await this.verifyCredential({
|
|
116
|
-
principal: input.principal,
|
|
117
|
-
secret: input.secret,
|
|
118
|
-
verifyMethod: input.verifyMethod,
|
|
119
|
-
});
|
|
120
|
-
if (!verified.ok) {
|
|
121
|
-
throw (0, errors_1.unauthorized)('AUTH_INVALID_CREDENTIAL', `Authentication failed: ${verified.reasonCode}`);
|
|
122
|
-
}
|
|
123
|
-
const defaultAssignment = [...verified.assignments].sort((a, b) => a.assignmentId.localeCompare(b.assignmentId))[0];
|
|
124
|
-
const session = await this.sessionService.createSession({
|
|
125
|
-
identityId: verified.identity.identityId,
|
|
126
|
-
assignmentId: defaultAssignment?.assignmentId,
|
|
127
|
-
assuranceLevel: 'basic',
|
|
128
|
-
});
|
|
129
|
-
return {
|
|
130
|
-
identity: verified.identity,
|
|
131
|
-
sessionId: session.sessionId,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
async logout(sessionId) {
|
|
135
|
-
await this.sessionService.revokeSession({ sessionId, reasonCode: 'LOGOUT' });
|
|
136
|
-
await this.emitAudit({
|
|
137
|
-
eventId: this.newEventId(),
|
|
138
|
-
eventType: 'SESSION_REVOKED',
|
|
139
|
-
sessionId,
|
|
140
|
-
result: 'success',
|
|
141
|
-
reasonCode: 'LOGOUT',
|
|
142
|
-
timestamp: (0, runtimeSupport_1.nowIso)(),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
exports.DbAdapterIdentityService = DbAdapterIdentityService;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbAdapterSessionService = void 0;
|
|
4
|
-
const schemas_1 = require("../../data/schemas");
|
|
5
|
-
const errors_1 = require("../errors");
|
|
6
|
-
const runtimeSupport_1 = require("./runtimeSupport");
|
|
7
|
-
class DbAdapterSessionService {
|
|
8
|
-
constructor(db, options) {
|
|
9
|
-
this.db = db;
|
|
10
|
-
this.options = options;
|
|
11
|
-
this.newId = (0, runtimeSupport_1.makeIdFactory)('auth-session');
|
|
12
|
-
}
|
|
13
|
-
async createSession(input) {
|
|
14
|
-
const issuedAt = (0, runtimeSupport_1.nowIso)();
|
|
15
|
-
const expiresAt = (0, runtimeSupport_1.plusSecondsIso)(issuedAt, input.ttlSeconds ?? runtimeSupport_1.DEFAULT_SESSION_TTL_SECONDS);
|
|
16
|
-
const created = await this.db.create(schemas_1.OFAUTH_TABLES.sessions, {
|
|
17
|
-
id: this.newId(),
|
|
18
|
-
identityId: input.identityId,
|
|
19
|
-
activeAssignmentId: input.assignmentId ?? null,
|
|
20
|
-
assuranceLevel: input.assuranceLevel ?? 'basic',
|
|
21
|
-
issuedAt,
|
|
22
|
-
expiresAt,
|
|
23
|
-
revokedAt: null,
|
|
24
|
-
version: 1,
|
|
25
|
-
lastModified: issuedAt,
|
|
26
|
-
deleted: false,
|
|
27
|
-
});
|
|
28
|
-
const assignment = await (0, runtimeSupport_1.getAssignmentById)(this.db, created.activeAssignmentId);
|
|
29
|
-
this.options.logger?.logInfo('[ofauth] session created', { sessionId: created.id, identityId: created.identityId });
|
|
30
|
-
return (0, runtimeSupport_1.toSessionRef)(created, assignment);
|
|
31
|
-
}
|
|
32
|
-
async refreshSession(input) {
|
|
33
|
-
const current = await this.db.get(schemas_1.OFAUTH_TABLES.sessions, input.sessionId);
|
|
34
|
-
if (!current || current.deleted || current.revokedAt)
|
|
35
|
-
throw (0, errors_1.unauthorized)('AUTH_SESSION_NOT_FOUND', 'Session not found');
|
|
36
|
-
const refreshed = await this.db.update(schemas_1.OFAUTH_TABLES.sessions, input.sessionId, {
|
|
37
|
-
expiresAt: (0, runtimeSupport_1.plusSecondsIso)((0, runtimeSupport_1.nowIso)(), input.ttlSeconds ?? runtimeSupport_1.DEFAULT_SESSION_TTL_SECONDS),
|
|
38
|
-
version: current.version + 1,
|
|
39
|
-
lastModified: (0, runtimeSupport_1.nowIso)(),
|
|
40
|
-
});
|
|
41
|
-
const assignment = await (0, runtimeSupport_1.getAssignmentById)(this.db, refreshed.activeAssignmentId);
|
|
42
|
-
return (0, runtimeSupport_1.toSessionRef)(refreshed, assignment);
|
|
43
|
-
}
|
|
44
|
-
async revokeSession(input) {
|
|
45
|
-
const current = await this.db.get(schemas_1.OFAUTH_TABLES.sessions, input.sessionId);
|
|
46
|
-
if (!current || current.deleted || current.revokedAt)
|
|
47
|
-
return;
|
|
48
|
-
await this.db.update(schemas_1.OFAUTH_TABLES.sessions, input.sessionId, {
|
|
49
|
-
revokedAt: (0, runtimeSupport_1.nowIso)(),
|
|
50
|
-
version: current.version + 1,
|
|
51
|
-
lastModified: (0, runtimeSupport_1.nowIso)(),
|
|
52
|
-
});
|
|
53
|
-
this.options.logger?.logInfo('[ofauth] session revoked', { sessionId: input.sessionId, reasonCode: input.reasonCode ?? null });
|
|
54
|
-
}
|
|
55
|
-
async getActiveSession(sessionId) {
|
|
56
|
-
const row = await this.db.get(schemas_1.OFAUTH_TABLES.sessions, sessionId);
|
|
57
|
-
if (!row || row.deleted || row.revokedAt || (0, runtimeSupport_1.isPast)(row.expiresAt))
|
|
58
|
-
return null;
|
|
59
|
-
const assignment = await (0, runtimeSupport_1.getAssignmentById)(this.db, row.activeAssignmentId);
|
|
60
|
-
return (0, runtimeSupport_1.toSessionRef)(row, assignment);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
exports.DbAdapterSessionService = DbAdapterSessionService;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_SESSION_TTL_SECONDS = exports.DEFAULT_LOCKOUT_COOLDOWN_SECONDS = exports.DEFAULT_LOCKOUT_THRESHOLD = void 0;
|
|
4
|
-
exports.nowIso = nowIso;
|
|
5
|
-
exports.plusSecondsIso = plusSecondsIso;
|
|
6
|
-
exports.isPast = isPast;
|
|
7
|
-
exports.makeIdFactory = makeIdFactory;
|
|
8
|
-
exports.toIdentityRef = toIdentityRef;
|
|
9
|
-
exports.toAssignment = toAssignment;
|
|
10
|
-
exports.toSessionRef = toSessionRef;
|
|
11
|
-
exports.verifySecret = verifySecret;
|
|
12
|
-
exports.emitAuthActivity = emitAuthActivity;
|
|
13
|
-
exports.getAssignmentById = getAssignmentById;
|
|
14
|
-
exports.fallbackPolicyProfiles = fallbackPolicyProfiles;
|
|
15
|
-
const schemas_1 = require("../../data/schemas");
|
|
16
|
-
exports.DEFAULT_LOCKOUT_THRESHOLD = 5;
|
|
17
|
-
exports.DEFAULT_LOCKOUT_COOLDOWN_SECONDS = 15 * 60;
|
|
18
|
-
exports.DEFAULT_SESSION_TTL_SECONDS = 8 * 60 * 60;
|
|
19
|
-
function nowIso() {
|
|
20
|
-
return new Date().toISOString();
|
|
21
|
-
}
|
|
22
|
-
function plusSecondsIso(baseIso, seconds) {
|
|
23
|
-
return new Date(new Date(baseIso).getTime() + seconds * 1000).toISOString();
|
|
24
|
-
}
|
|
25
|
-
function isPast(iso) {
|
|
26
|
-
return new Date(iso).getTime() <= Date.now();
|
|
27
|
-
}
|
|
28
|
-
function makeIdFactory(prefix) {
|
|
29
|
-
let counter = 0;
|
|
30
|
-
return () => {
|
|
31
|
-
counter += 1;
|
|
32
|
-
return `${prefix}-${Date.now()}-${counter}`;
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function toIdentityRef(row) {
|
|
36
|
-
return {
|
|
37
|
-
identityId: row.id,
|
|
38
|
-
principal: row.principal,
|
|
39
|
-
status: row.status,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function toAssignment(row) {
|
|
43
|
-
return {
|
|
44
|
-
assignmentId: row.id,
|
|
45
|
-
identityId: row.identityId,
|
|
46
|
-
roleRef: row.roleRef,
|
|
47
|
-
scopeRef: {
|
|
48
|
-
...(row.tenantId ? { tenantId: row.tenantId } : {}),
|
|
49
|
-
...(row.branchId ? { branchId: row.branchId } : {}),
|
|
50
|
-
...(row.scopeAttributes ? { attributes: row.scopeAttributes } : {}),
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function toSessionRef(row, assignment) {
|
|
55
|
-
return {
|
|
56
|
-
sessionId: row.id,
|
|
57
|
-
identityId: row.identityId,
|
|
58
|
-
activeScopeRef: assignment
|
|
59
|
-
? {
|
|
60
|
-
...(assignment.tenantId ? { tenantId: assignment.tenantId } : {}),
|
|
61
|
-
...(assignment.branchId ? { branchId: assignment.branchId } : {}),
|
|
62
|
-
...(assignment.scopeAttributes ? { attributes: assignment.scopeAttributes } : {}),
|
|
63
|
-
}
|
|
64
|
-
: undefined,
|
|
65
|
-
assuranceLevel: row.assuranceLevel,
|
|
66
|
-
issuedAt: row.issuedAt,
|
|
67
|
-
expiresAt: row.expiresAt,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
async function verifySecret(platformAdapter, plainSecret, secretHash) {
|
|
71
|
-
if (platformAdapter) {
|
|
72
|
-
return platformAdapter.verifyPin(plainSecret, secretHash);
|
|
73
|
-
}
|
|
74
|
-
return plainSecret === secretHash;
|
|
75
|
-
}
|
|
76
|
-
async function emitAuthActivity(options, event) {
|
|
77
|
-
if (!options.emitActivity)
|
|
78
|
-
return;
|
|
79
|
-
await options.emitActivity({
|
|
80
|
-
activityId: event.eventId,
|
|
81
|
-
activityType: `ofauth.${event.eventType.toLowerCase()}`,
|
|
82
|
-
occurredAt: event.timestamp,
|
|
83
|
-
scopeRef: {
|
|
84
|
-
domain: 'ofauth',
|
|
85
|
-
...(event.scopeRef?.tenantId ? { tenantId: event.scopeRef.tenantId } : {}),
|
|
86
|
-
...(event.scopeRef?.branchId ? { branchId: event.scopeRef.branchId } : {}),
|
|
87
|
-
},
|
|
88
|
-
actor: event.identityId ? { userId: event.identityId } : undefined,
|
|
89
|
-
payload: {
|
|
90
|
-
eventType: event.eventType,
|
|
91
|
-
result: event.result,
|
|
92
|
-
reasonCode: event.reasonCode ?? null,
|
|
93
|
-
sessionId: event.sessionId ?? null,
|
|
94
|
-
scopeAttributes: event.scopeRef?.attributes ?? null,
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
async function getAssignmentById(db, id) {
|
|
99
|
-
if (!id)
|
|
100
|
-
return null;
|
|
101
|
-
const row = await db.get(schemas_1.OFAUTH_TABLES.assignments, id);
|
|
102
|
-
if (!row || row.deleted)
|
|
103
|
-
return null;
|
|
104
|
-
return row;
|
|
105
|
-
}
|
|
106
|
-
function fallbackPolicyProfiles() {
|
|
107
|
-
return [
|
|
108
|
-
{ profileId: 'cashier_fast', defaultAssuranceLevel: 'basic', lockoutPolicyRef: 'default' },
|
|
109
|
-
{ profileId: 'supervisor_strict', defaultAssuranceLevel: 'elevated', lockoutPolicyRef: 'strict' },
|
|
110
|
-
{ profileId: 'member_self_service', defaultAssuranceLevel: 'basic', lockoutPolicyRef: 'member' },
|
|
111
|
-
];
|
|
112
|
-
}
|