@xemahq/oidc-session-nest 0.1.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/LICENSE +176 -0
- package/README.md +101 -0
- package/dist/config/oidc-session-env.d.ts +6 -0
- package/dist/config/oidc-session-env.d.ts.map +1 -0
- package/dist/config/oidc-session-env.js +123 -0
- package/dist/config/oidc-session-env.js.map +1 -0
- package/dist/config/oidc-session-options.d.ts +33 -0
- package/dist/config/oidc-session-options.d.ts.map +1 -0
- package/dist/config/oidc-session-options.js +67 -0
- package/dist/config/oidc-session-options.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +60 -0
- package/dist/errors.js.map +1 -0
- package/dist/http/cookie.d.ts +12 -0
- package/dist/http/cookie.d.ts.map +1 -0
- package/dist/http/cookie.js +82 -0
- package/dist/http/cookie.js.map +1 -0
- package/dist/http/headers.d.ts +2 -0
- package/dist/http/headers.d.ts.map +1 -0
- package/dist/http/headers.js +5 -0
- package/dist/http/headers.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/nest/oidc-session-auth.controller.d.ts +30 -0
- package/dist/nest/oidc-session-auth.controller.d.ts.map +1 -0
- package/dist/nest/oidc-session-auth.controller.js +182 -0
- package/dist/nest/oidc-session-auth.controller.js.map +1 -0
- package/dist/nest/oidc-session-context.d.ts +11 -0
- package/dist/nest/oidc-session-context.d.ts.map +1 -0
- package/dist/nest/oidc-session-context.js +37 -0
- package/dist/nest/oidc-session-context.js.map +1 -0
- package/dist/nest/oidc-session.guard.d.ts +5 -0
- package/dist/nest/oidc-session.guard.d.ts.map +1 -0
- package/dist/nest/oidc-session.guard.js +43 -0
- package/dist/nest/oidc-session.guard.js.map +1 -0
- package/dist/nest/oidc-session.middleware.d.ts +19 -0
- package/dist/nest/oidc-session.middleware.d.ts.map +1 -0
- package/dist/nest/oidc-session.middleware.js +57 -0
- package/dist/nest/oidc-session.middleware.js.map +1 -0
- package/dist/nest/oidc-session.module.d.ts +22 -0
- package/dist/nest/oidc-session.module.d.ts.map +1 -0
- package/dist/nest/oidc-session.module.js +100 -0
- package/dist/nest/oidc-session.module.js.map +1 -0
- package/dist/nest/session-actor.decorator.d.ts +2 -0
- package/dist/nest/session-actor.decorator.d.ts.map +1 -0
- package/dist/nest/session-actor.decorator.js +13 -0
- package/dist/nest/session-actor.decorator.js.map +1 -0
- package/dist/nest/session-client-transport.d.ts +6 -0
- package/dist/nest/session-client-transport.d.ts.map +1 -0
- package/dist/nest/session-client-transport.js +41 -0
- package/dist/nest/session-client-transport.js.map +1 -0
- package/dist/nest/tokens.d.ts +3 -0
- package/dist/nest/tokens.d.ts.map +1 -0
- package/dist/nest/tokens.js +6 -0
- package/dist/nest/tokens.js.map +1 -0
- package/dist/oidc/discovery.d.ts +30 -0
- package/dist/oidc/discovery.d.ts.map +1 -0
- package/dist/oidc/discovery.js +101 -0
- package/dist/oidc/discovery.js.map +1 -0
- package/dist/oidc/oidc-client.d.ts +44 -0
- package/dist/oidc/oidc-client.d.ts.map +1 -0
- package/dist/oidc/oidc-client.js +190 -0
- package/dist/oidc/oidc-client.js.map +1 -0
- package/dist/oidc/pkce.d.ts +8 -0
- package/dist/oidc/pkce.d.ts.map +1 -0
- package/dist/oidc/pkce.js +15 -0
- package/dist/oidc/pkce.js.map +1 -0
- package/dist/session/actor.d.ts +14 -0
- package/dist/session/actor.d.ts.map +1 -0
- package/dist/session/actor.js +32 -0
- package/dist/session/actor.js.map +1 -0
- package/dist/session/in-memory-session-store.d.ts +21 -0
- package/dist/session/in-memory-session-store.d.ts.map +1 -0
- package/dist/session/in-memory-session-store.js +79 -0
- package/dist/session/in-memory-session-store.js.map +1 -0
- package/dist/session/redis-session-store.d.ts +28 -0
- package/dist/session/redis-session-store.d.ts.map +1 -0
- package/dist/session/redis-session-store.js +121 -0
- package/dist/session/redis-session-store.js.map +1 -0
- package/dist/session/session-service.d.ts +55 -0
- package/dist/session/session-service.d.ts.map +1 -0
- package/dist/session/session-service.js +224 -0
- package/dist/session/session-service.js.map +1 -0
- package/dist/session/session-store.d.ts +35 -0
- package/dist/session/session-store.d.ts.map +1 -0
- package/dist/session/session-store.js +3 -0
- package/dist/session/session-store.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionService = void 0;
|
|
4
|
+
exports.storeKeyFor = storeKeyFor;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const actor_1 = require("./actor");
|
|
8
|
+
const oidc_client_1 = require("../oidc/oidc-client");
|
|
9
|
+
const pkce_1 = require("../oidc/pkce");
|
|
10
|
+
const PENDING_AUTHORIZATION_TTL_MS = 10 * 60 * 1000;
|
|
11
|
+
const REFRESH_LEASE_TTL_MS = 15 * 1000;
|
|
12
|
+
const REFRESH_POLL_INTERVAL_MS = 50;
|
|
13
|
+
class SessionService {
|
|
14
|
+
options;
|
|
15
|
+
store;
|
|
16
|
+
client;
|
|
17
|
+
now;
|
|
18
|
+
sleep;
|
|
19
|
+
inFlightRefresh = new Map();
|
|
20
|
+
constructor(deps) {
|
|
21
|
+
this.options = deps.options;
|
|
22
|
+
this.store = deps.store;
|
|
23
|
+
this.client = deps.client;
|
|
24
|
+
this.now = deps.now ?? (() => Date.now());
|
|
25
|
+
this.sleep =
|
|
26
|
+
deps.sleep ??
|
|
27
|
+
((ms) => new Promise((resolve) => {
|
|
28
|
+
setTimeout(resolve, ms);
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
async beginAuthorization(input) {
|
|
32
|
+
const state = (0, pkce_1.randomUrlSafeToken)();
|
|
33
|
+
const nonce = (0, pkce_1.randomUrlSafeToken)();
|
|
34
|
+
const pending = {
|
|
35
|
+
codeVerifier: input.pkceVerifier,
|
|
36
|
+
nonce,
|
|
37
|
+
returnTo: input.returnTo,
|
|
38
|
+
createdAt: this.now(),
|
|
39
|
+
};
|
|
40
|
+
await this.store.putPendingAuthorization(state, pending, PENDING_AUTHORIZATION_TTL_MS);
|
|
41
|
+
const authorizationUrl = await this.client.buildAuthorizationUrl({
|
|
42
|
+
state,
|
|
43
|
+
nonce,
|
|
44
|
+
codeChallenge: input.codeChallenge,
|
|
45
|
+
});
|
|
46
|
+
return { authorizationUrl, state };
|
|
47
|
+
}
|
|
48
|
+
takePendingAuthorization(state) {
|
|
49
|
+
return this.store.takePendingAuthorization(state);
|
|
50
|
+
}
|
|
51
|
+
async establishSession(tokenSet, returnTo) {
|
|
52
|
+
const subjectId = tokenSet.accessClaims.sub;
|
|
53
|
+
if (typeof subjectId !== 'string' || subjectId === '') {
|
|
54
|
+
throw new Error('[oidc-session-nest] The access token carries no "sub" claim, so the ' +
|
|
55
|
+
'session would have no subject. Refusing to establish it.');
|
|
56
|
+
}
|
|
57
|
+
const startedAt = this.now();
|
|
58
|
+
const record = {
|
|
59
|
+
version: 1,
|
|
60
|
+
subjectId,
|
|
61
|
+
activeOrgId: (0, actor_1.resolveInitialActiveOrgId)(tokenSet.accessClaims),
|
|
62
|
+
orgRoles: (0, actor_1.resolveSessionOrgRoles)(tokenSet.accessClaims),
|
|
63
|
+
accessToken: tokenSet.accessToken,
|
|
64
|
+
...(tokenSet.refreshToken === undefined
|
|
65
|
+
? {}
|
|
66
|
+
: { refreshToken: tokenSet.refreshToken }),
|
|
67
|
+
...(tokenSet.idToken === undefined ? {} : { idToken: tokenSet.idToken }),
|
|
68
|
+
accessTokenExpiresAt: tokenSet.accessTokenExpiresAt,
|
|
69
|
+
absoluteExpiresAt: startedAt + this.options.ttl.absoluteTtlSeconds * 1000,
|
|
70
|
+
claims: tokenSet.accessClaims,
|
|
71
|
+
createdAt: startedAt,
|
|
72
|
+
};
|
|
73
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
74
|
+
const cookieValue = (0, pkce_1.randomUrlSafeToken)();
|
|
75
|
+
const written = await this.store.createSession(storeKeyFor(cookieValue), record, this.idleTtlMs(record));
|
|
76
|
+
if (written) {
|
|
77
|
+
return { cookieValue, actor: (0, actor_1.toSessionActor)(record), returnTo };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error('[oidc-session-nest] Could not mint an unused session key in two attempts. ' +
|
|
81
|
+
'That is not a collision — check that the store is not returning a stale ' +
|
|
82
|
+
'answer for every key.');
|
|
83
|
+
}
|
|
84
|
+
async resolveSession(cookieValue) {
|
|
85
|
+
const key = storeKeyFor(cookieValue);
|
|
86
|
+
const record = await this.store.readSession(key, {
|
|
87
|
+
idleTtlMs: this.options.ttl.idleTtlSeconds * 1000,
|
|
88
|
+
now: this.now(),
|
|
89
|
+
});
|
|
90
|
+
if (record === null) {
|
|
91
|
+
return { session: null, expired: true };
|
|
92
|
+
}
|
|
93
|
+
const fresh = await this.ensureFreshAccessToken(key, record);
|
|
94
|
+
if (fresh === null) {
|
|
95
|
+
return { session: null, expired: true };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
session: { key, record: fresh, actor: (0, actor_1.toSessionActor)(fresh) },
|
|
99
|
+
expired: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async setActiveOrg(session, orgId) {
|
|
103
|
+
const role = session.record.orgRoles[orgId];
|
|
104
|
+
if (role === undefined) {
|
|
105
|
+
throw new Error(`[oidc-session-nest] This session has no membership in org "${orgId}". ` +
|
|
106
|
+
'Refusing to act in an org the token does not grant.');
|
|
107
|
+
}
|
|
108
|
+
const next = {
|
|
109
|
+
...session.record,
|
|
110
|
+
version: session.record.version + 1,
|
|
111
|
+
activeOrgId: orgId,
|
|
112
|
+
};
|
|
113
|
+
const written = await this.store.replaceSession(session.key, session.record.version, next, this.idleTtlMs(next));
|
|
114
|
+
return written ? (0, actor_1.toSessionActor)(next) : null;
|
|
115
|
+
}
|
|
116
|
+
async endSession(session) {
|
|
117
|
+
if (session === null) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
await this.store.deleteSession(session.key);
|
|
121
|
+
return this.client.buildEndSessionUrl(session.record.idToken);
|
|
122
|
+
}
|
|
123
|
+
storeKeyFor(cookieValue) {
|
|
124
|
+
return storeKeyFor(cookieValue);
|
|
125
|
+
}
|
|
126
|
+
idleTtlMs(record) {
|
|
127
|
+
const idle = this.options.ttl.idleTtlSeconds * 1000;
|
|
128
|
+
const untilAbsolute = record.absoluteExpiresAt - this.now();
|
|
129
|
+
return Math.max(1, Math.min(idle, untilAbsolute));
|
|
130
|
+
}
|
|
131
|
+
async ensureFreshAccessToken(key, record) {
|
|
132
|
+
const now = this.now();
|
|
133
|
+
const leadMs = this.options.ttl.refreshLeadSeconds * 1000;
|
|
134
|
+
if (record.accessTokenExpiresAt - leadMs > now) {
|
|
135
|
+
return record;
|
|
136
|
+
}
|
|
137
|
+
if (record.refreshToken === undefined) {
|
|
138
|
+
return now >= record.accessTokenExpiresAt ? null : record;
|
|
139
|
+
}
|
|
140
|
+
const existing = this.inFlightRefresh.get(key);
|
|
141
|
+
if (existing !== undefined) {
|
|
142
|
+
return existing;
|
|
143
|
+
}
|
|
144
|
+
const pending = this.refreshUnderLease(key, record).finally(() => {
|
|
145
|
+
this.inFlightRefresh.delete(key);
|
|
146
|
+
});
|
|
147
|
+
this.inFlightRefresh.set(key, pending);
|
|
148
|
+
return pending;
|
|
149
|
+
}
|
|
150
|
+
async refreshUnderLease(key, record) {
|
|
151
|
+
const acquired = await this.store.acquireRefreshLease(key, REFRESH_LEASE_TTL_MS);
|
|
152
|
+
if (!acquired) {
|
|
153
|
+
return this.awaitAnotherRefresh(record, key);
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return await this.performRefresh(key, record);
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
await this.store.releaseRefreshLease(key);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async performRefresh(key, record) {
|
|
163
|
+
const refreshToken = record.refreshToken;
|
|
164
|
+
if (refreshToken === undefined) {
|
|
165
|
+
return record;
|
|
166
|
+
}
|
|
167
|
+
let tokenSet;
|
|
168
|
+
try {
|
|
169
|
+
tokenSet = await this.client.refresh(refreshToken);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error instanceof oidc_client_1.OidcInvalidGrantError) {
|
|
173
|
+
await this.store.deleteSession(key);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
const next = {
|
|
179
|
+
...record,
|
|
180
|
+
version: record.version + 1,
|
|
181
|
+
accessToken: tokenSet.accessToken,
|
|
182
|
+
...(tokenSet.refreshToken === undefined
|
|
183
|
+
? { refreshToken }
|
|
184
|
+
: { refreshToken: tokenSet.refreshToken }),
|
|
185
|
+
...(tokenSet.idToken === undefined ? {} : { idToken: tokenSet.idToken }),
|
|
186
|
+
accessTokenExpiresAt: tokenSet.accessTokenExpiresAt,
|
|
187
|
+
orgRoles: (0, actor_1.resolveSessionOrgRoles)(tokenSet.accessClaims),
|
|
188
|
+
claims: tokenSet.accessClaims,
|
|
189
|
+
};
|
|
190
|
+
const written = await this.store.replaceSession(key, record.version, next, this.idleTtlMs(next));
|
|
191
|
+
if (!written) {
|
|
192
|
+
return this.store.readSession(key, {
|
|
193
|
+
idleTtlMs: this.options.ttl.idleTtlSeconds * 1000,
|
|
194
|
+
now: this.now(),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return next;
|
|
198
|
+
}
|
|
199
|
+
async awaitAnotherRefresh(record, key) {
|
|
200
|
+
if (this.now() < record.accessTokenExpiresAt) {
|
|
201
|
+
return record;
|
|
202
|
+
}
|
|
203
|
+
const attempts = Math.max(1, Math.ceil(this.options.ttl.refreshWaitMs / REFRESH_POLL_INTERVAL_MS));
|
|
204
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
205
|
+
await this.sleep(REFRESH_POLL_INTERVAL_MS);
|
|
206
|
+
const latest = await this.store.readSession(key, {
|
|
207
|
+
idleTtlMs: this.options.ttl.idleTtlSeconds * 1000,
|
|
208
|
+
now: this.now(),
|
|
209
|
+
});
|
|
210
|
+
if (latest === null) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
if (latest.version > record.version) {
|
|
214
|
+
return latest;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
throw new errors_1.SessionRefreshContendedError();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.SessionService = SessionService;
|
|
221
|
+
function storeKeyFor(cookieValue) {
|
|
222
|
+
return (0, node_crypto_1.createHash)('sha256').update(cookieValue).digest('base64url');
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=session-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-service.js","sourceRoot":"","sources":["../../src/session/session-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { OrgRole } from '@xemahq/platform-common';
|
|
2
|
+
export interface SessionRecord {
|
|
3
|
+
readonly version: number;
|
|
4
|
+
readonly subjectId: string;
|
|
5
|
+
readonly activeOrgId: string | null;
|
|
6
|
+
readonly orgRoles: Readonly<Record<string, OrgRole>>;
|
|
7
|
+
readonly accessToken: string;
|
|
8
|
+
readonly refreshToken?: string;
|
|
9
|
+
readonly idToken?: string;
|
|
10
|
+
readonly accessTokenExpiresAt: number;
|
|
11
|
+
readonly absoluteExpiresAt: number;
|
|
12
|
+
readonly claims: Readonly<Record<string, unknown>>;
|
|
13
|
+
readonly createdAt: number;
|
|
14
|
+
}
|
|
15
|
+
export interface PendingAuthorization {
|
|
16
|
+
readonly codeVerifier: string;
|
|
17
|
+
readonly nonce: string;
|
|
18
|
+
readonly returnTo: string | null;
|
|
19
|
+
readonly createdAt: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ReadSessionOptions {
|
|
22
|
+
readonly idleTtlMs: number;
|
|
23
|
+
readonly now: number;
|
|
24
|
+
}
|
|
25
|
+
export interface SessionStore {
|
|
26
|
+
createSession(key: string, record: SessionRecord, ttlMs: number): Promise<boolean>;
|
|
27
|
+
readSession(key: string, options: ReadSessionOptions): Promise<SessionRecord | null>;
|
|
28
|
+
replaceSession(key: string, expectedVersion: number, record: SessionRecord, ttlMs: number): Promise<boolean>;
|
|
29
|
+
deleteSession(key: string): Promise<void>;
|
|
30
|
+
acquireRefreshLease(key: string, ttlMs: number): Promise<boolean>;
|
|
31
|
+
releaseRefreshLease(key: string): Promise<void>;
|
|
32
|
+
putPendingAuthorization(state: string, pending: PendingAuthorization, ttlMs: number): Promise<void>;
|
|
33
|
+
takePendingAuthorization(state: string): Promise<PendingAuthorization | null>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAM5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAM3B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,kBAAkB;IAOjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAQD,MAAM,WAAW,YAAY;IAQ3B,aAAa,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IASpB,WAAW,CACT,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IASjC,cAAc,CACZ,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IAGpB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAQ1C,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAGlE,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,uBAAuB,CACrB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAAC;IASjB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;CAC/E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xemahq/oidc-session-nest",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Server-side OIDC session for a NestJS backend-for-frontend: the browser holds one httpOnly cookie and never a Xema token. Performs the authorization-code exchange, keeps the token set in a session store the application owns, refreshes it under a lease so concurrent requests exchange once, and hands the generated Xema clients a bearer plus the org and Project headers through an ambient request scope.",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"registry": "https://registry.npmjs.org/",
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@nestjs/common": "^10 || ^11",
|
|
16
|
+
"@nestjs/core": "^10 || ^11",
|
|
17
|
+
"@xemahq/contracts": "^0.1.0",
|
|
18
|
+
"@xemahq/oidc-guard": ">=0.6.0",
|
|
19
|
+
"@xemahq/platform-common": ">=0.28.2",
|
|
20
|
+
"@xemahq/xema-decorators": ">=0.15.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@nestjs/common": "11.1.18",
|
|
24
|
+
"@nestjs/core": "11.1.18",
|
|
25
|
+
"@nestjs/platform-express": "11.1.28",
|
|
26
|
+
"@nestjs/testing": "11.1.18",
|
|
27
|
+
"@types/jest": "^30.0.0",
|
|
28
|
+
"@types/node": "25.2.3",
|
|
29
|
+
"@xemahq/contracts": "^0.1.0",
|
|
30
|
+
"@xemahq/oidc-guard": "^0.6.0",
|
|
31
|
+
"@xemahq/platform-common": "^0.31.0",
|
|
32
|
+
"eslint": "^9.39.4",
|
|
33
|
+
"jest": "^30.3.0",
|
|
34
|
+
"reflect-metadata": "0.2.2",
|
|
35
|
+
"rxjs": "7.8.2",
|
|
36
|
+
"ts-jest": "^29.4.9",
|
|
37
|
+
"typescript": "5.9.3",
|
|
38
|
+
"@xemahq/eslint-config": "0.6.0",
|
|
39
|
+
"@xemahq/xema-decorators": "0.16.0"
|
|
40
|
+
},
|
|
41
|
+
"exports": {
|
|
42
|
+
".": {
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"xema-source": "./src/index.ts",
|
|
45
|
+
"default": "./dist/index.js"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"license": "Apache-2.0",
|
|
49
|
+
"author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
|
|
50
|
+
"homepage": "https://xema.dev",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/xema-dev/xema-kernel-sdk.git",
|
|
54
|
+
"directory": "packages/sdks/oidc-session-nest"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"clean": "rm -rf dist",
|
|
58
|
+
"build": "tsc -p tsconfig.build.json",
|
|
59
|
+
"lint": "eslint .",
|
|
60
|
+
"test": "jest --config jest.config.ts",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
62
|
+
}
|
|
63
|
+
}
|