@oslokommune/auth-bff 1.5.1 → 1.6.0-beta1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"oidc-routes.d.mts","sourceRoot":"","sources":["../../src/middleware/oidc-routes.mjs"],"names":[],"mappings":"AAEA,qDASC"}
1
+ {"version":3,"file":"oidc-routes.d.mts","sourceRoot":"","sources":["../../src/middleware/oidc-routes.mjs"],"names":[],"mappings":"AAEA,qDAUC"}
@@ -5,5 +5,6 @@ export function oidcRoutes(oidcMiddleware) {
5
5
  router.get('/auth/callback', oidcMiddleware.callback);
6
6
  router.get('/auth/logout', oidcMiddleware.logout);
7
7
  router.get('/auth/user', oidcMiddleware.user);
8
+ router.get('/auth/front-channel-logout', oidcMiddleware.frontChannelLogout);
8
9
  return router;
9
10
  }
@@ -11,6 +11,7 @@ export class OidcMiddleware {
11
11
  get callback(): (req: any, res: any, next: any) => Promise<void>;
12
12
  get user(): (req: any, res: any, next: any) => Promise<any>;
13
13
  get logout(): (req: any, res: any) => void;
14
+ get frontChannelLogout(): (req: any, res: any) => Promise<void>;
14
15
  #private;
15
16
  }
16
17
  //# sourceMappingURL=oidc.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"oidc.d.mts","sourceRoot":"","sources":["../../src/middleware/oidc.mjs"],"names":[],"mappings":"AAGA;IAeE,oDAIC;IAdD;;;;OAIG;IACH,sBAGC;IAqDD,yBACU,QAAG,EAAE,QAAG,EAAE,SAAI,UAUvB;IAED,cACU,QAAG,EAAE,QAAG,UAsBjB;IAED,iBACgB,QAAG,EAAE,QAAG,EAAE,SAAI,mBAmC7B;IAED,aACgB,QAAG,EAAE,QAAG,EAAE,SAAI,kBAmB7B;IAED,eACU,QAAG,EAAE,QAAG,UAYjB;;CACF"}
1
+ {"version":3,"file":"oidc.d.mts","sourceRoot":"","sources":["../../src/middleware/oidc.mjs"],"names":[],"mappings":"AAIA;IAeE,oDAIC;IAdD;;;;OAIG;IACH,sBAGC;IAsDD,yBACU,QAAG,EAAE,QAAG,EAAE,SAAI,UAWvB;IAED,cACU,QAAG,EAAE,QAAG,UAsBjB;IAED,iBACgB,QAAG,EAAE,QAAG,EAAE,SAAI,mBAqC7B;IAED,aACgB,QAAG,EAAE,QAAG,EAAE,SAAI,kBAmB7B;IAED,eACU,QAAG,EAAE,QAAG,UAQjB;IAED,2BACgB,QAAG,EAAE,QAAG,mBAcvB;;CACF"}
@@ -21,6 +21,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
21
21
  var _OidcMiddleware_instances, _OidcMiddleware_clientManager, _OidcMiddleware_config, _OidcMiddleware_refreshPromises, _OidcMiddleware_refreshTokenSet, _OidcMiddleware_getFreshTokenSet;
22
22
  import { generators, TokenSet } from "openid-client";
23
23
  import { OidcClientManager } from "../client.mjs";
24
+ import { redact } from "../utils.js";
24
25
  export class OidcMiddleware {
25
26
  /**
26
27
  * @private
@@ -56,6 +57,7 @@ export class OidcMiddleware {
56
57
  next();
57
58
  }
58
59
  else {
60
+ console.warn(`401: No valid tokenSet in session sid=${redact(req.session.id)}`);
59
61
  res.sendStatus(401);
60
62
  }
61
63
  }).catch(next);
@@ -92,7 +94,9 @@ export class OidcMiddleware {
92
94
  code_verifier: codeVerifier,
93
95
  state: stateKey
94
96
  });
95
- req.session.tokenSet = new TokenSet(tokenSet);
97
+ req.session.tokenSet = tokenSet;
98
+ const parsedTokenSet = new TokenSet(tokenSet);
99
+ req.session["idp-sid"] = parsedTokenSet.claims().sid;
96
100
  delete req.session.codeVerifier;
97
101
  delete req.session.stateKey;
98
102
  delete req.session.stateValue;
@@ -132,7 +136,7 @@ export class OidcMiddleware {
132
136
  return res.send(claims);
133
137
  }
134
138
  catch (e) {
135
- console.error(`Error in /user sid=${(_a = req.session) === null || _a === void 0 ? void 0 : _a.id}`, e, req.session);
139
+ console.error(`Error in /user sid=${redact((_a = req.session) === null || _a === void 0 ? void 0 : _a.id)}`, e);
136
140
  next(e);
137
141
  }
138
142
  });
@@ -140,7 +144,6 @@ export class OidcMiddleware {
140
144
  get logout() {
141
145
  return (req, res) => {
142
146
  const tokenSet = req.session.tokenSet && new TokenSet(req.session.tokenSet);
143
- //TODO: støtt frontchannel SLO
144
147
  req.session.destroy(() => {
145
148
  res.redirect(__classPrivateFieldGet(this, _OidcMiddleware_clientManager, "f").client.endSessionUrl({
146
149
  id_token_hint: tokenSet === null || tokenSet === void 0 ? void 0 : tokenSet.id_token,
@@ -148,6 +151,22 @@ export class OidcMiddleware {
148
151
  });
149
152
  };
150
153
  }
154
+ get frontChannelLogout() {
155
+ return (req, res) => __awaiter(this, void 0, void 0, function* () {
156
+ var _a;
157
+ const { iss, sid } = req.query;
158
+ console.log(`Front channel logout: params iss=${iss}, sid=${redact(sid)}`);
159
+ if (sid) {
160
+ try {
161
+ yield ((_a = req.destroySessionByIdTokenSid) === null || _a === void 0 ? void 0 : _a.call(req, sid));
162
+ }
163
+ catch (e) {
164
+ console.error("Failed to destroy session", e);
165
+ }
166
+ }
167
+ res.sendStatus(200);
168
+ });
169
+ }
151
170
  }
152
171
  _OidcMiddleware_clientManager = new WeakMap(), _OidcMiddleware_config = new WeakMap(), _OidcMiddleware_refreshPromises = new WeakMap(), _OidcMiddleware_instances = new WeakSet(), _OidcMiddleware_refreshTokenSet = function _OidcMiddleware_refreshTokenSet(req, tokenSet) {
153
172
  return __awaiter(this, void 0, void 0, function* () {
@@ -156,19 +175,19 @@ _OidcMiddleware_clientManager = new WeakMap(), _OidcMiddleware_config = new Weak
156
175
  const sessionId = req.session.id;
157
176
  const refreshToken = tokenSet.refresh_token;
158
177
  const doRefresh = () => __awaiter(this, void 0, void 0, function* () {
159
- console.log(`Token refresh starting. sid=${sessionId}`);
178
+ console.log(`Token refresh starting. sid=${redact(sessionId)}`);
160
179
  try {
161
180
  const refreshedTokenSet = yield __classPrivateFieldGet(this, _OidcMiddleware_clientManager, "f").client.refresh(refreshToken);
162
- console.log(`Token refresh OK. sid=${sessionId}`);
181
+ console.log(`Token refresh OK. sid=${redact(sessionId)}`);
163
182
  return refreshedTokenSet;
164
183
  }
165
184
  catch (err) {
166
- console.log(`Token refresh failed. sid=${sessionId}`, err);
185
+ console.log(`Token refresh failed. sid=${redact(sessionId)}`, err);
167
186
  return null;
168
187
  }
169
188
  });
170
189
  const refreshPromise = (_a = (_b = __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f"))[refreshToken]) !== null && _a !== void 0 ? _a : (_b[refreshToken] = doRefresh().finally(() => {
171
- console.log(`Token refresh finished. Cleaning up. sid=${sessionId}`);
190
+ console.log(`Token refresh finished. Cleaning up. sid=${redact(sessionId)}`);
172
191
  setTimeout(() => {
173
192
  delete __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f")[refreshToken];
174
193
  }, 10000);
@@ -186,10 +205,11 @@ _OidcMiddleware_clientManager = new WeakMap(), _OidcMiddleware_config = new Weak
186
205
  return __awaiter(this, void 0, void 0, function* () {
187
206
  const tokenSet = req.session.tokenSet && new TokenSet(req.session.tokenSet);
188
207
  if (!tokenSet) {
189
- console.log("No tokenSet found in session");
208
+ console.log(`No tokenSet found in session sid=${redact(req.session.id)}`);
190
209
  return;
191
210
  }
192
211
  if (tokenSet.expired()) {
212
+ console.log(`TokenSet expired sid=${redact(req.session.id)}`);
193
213
  const newTokenSet = yield __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_refreshTokenSet).call(this, req, tokenSet);
194
214
  return newTokenSet && new TokenSet(newTokenSet);
195
215
  }
@@ -1,2 +1,2 @@
1
- export function sessions(config: any): any;
1
+ export function sessions(config: any): any[];
2
2
  //# sourceMappingURL=sessions.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessions.d.mts","sourceRoot":"","sources":["../../src/middleware/sessions.mjs"],"names":[],"mappings":"AAQA,2CAyBC"}
1
+ {"version":3,"file":"sessions.d.mts","sourceRoot":"","sources":["../../src/middleware/sessions.mjs"],"names":[],"mappings":"AAiDA,6CAgCC"}
@@ -1,17 +1,63 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import session from "express-session";
2
11
  import dynamoDbStore from "connect-dynamodb";
12
+ import { DeleteItemCommand, DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
13
+ import { redact } from "../utils.js";
3
14
  function dynamoDbSessionStore(config = {}) {
15
+ const client = new DynamoDBClient({});
4
16
  const DynamoDbStore = dynamoDbStore({ session });
5
- return new DynamoDbStore(config);
17
+ const sessionStoreConfig = Object.assign(Object.assign({}, config), { client, specialKeys: [
18
+ { name: "idp-sid", type: "S" }
19
+ ], skipThrowMissingSpecialKeys: true });
20
+ const sessionStore = new DynamoDbStore(sessionStoreConfig);
21
+ sessionStore.destroyByIdTokenSid = (idTokenSid) => __awaiter(this, void 0, void 0, function* () {
22
+ console.log(`Front channel logout: deleting session(s) with idp-sid=${redact(idTokenSid)}`);
23
+ const query = new QueryCommand({
24
+ TableName: config.table,
25
+ IndexName: "idp-sid-index",
26
+ ExpressionAttributeValues: { ":sid": { S: idTokenSid } },
27
+ ExpressionAttributeNames: { "#k": "idp-sid" },
28
+ KeyConditionExpression: "#k = :sid",
29
+ ProjectionExpression: "id"
30
+ });
31
+ const res = yield client.send(query);
32
+ yield Promise.all(res.Items.map((item) => {
33
+ var _a;
34
+ console.log(`Front channel logout: deleting session ${redact((_a = item.id) === null || _a === void 0 ? void 0 : _a.S, 10)}`);
35
+ return client.send(new DeleteItemCommand({
36
+ TableName: config.table,
37
+ Key: { id: item.id }
38
+ }));
39
+ }));
40
+ console.log(`Front channel logout: completed. ${res.Count} session(s) deleted`);
41
+ });
42
+ return sessionStore;
43
+ }
44
+ function memorySessionStore(config = {}) {
45
+ const sessionStore = new session.MemoryStore(config);
46
+ sessionStore.destroyByIdTokenSid = (idTokenSid) => {
47
+ // dummy.
48
+ console.log(`Pretending to destroyByIdTokenSid. idp-sid=${redact(idTokenSid)}`);
49
+ };
50
+ return sessionStore;
6
51
  }
7
52
  export function sessions(config) {
8
- var _a;
53
+ var _a, _b;
9
54
  let sessionStore;
10
55
  if (config.sessionStoreType === 'memory') {
11
- sessionStore = undefined;
56
+ const sessionStoreOptions = (_a = config.sessionStoreOptions) !== null && _a !== void 0 ? _a : {};
57
+ sessionStore = memorySessionStore(sessionStoreOptions);
12
58
  }
13
59
  else if (config.sessionStoreType === 'dynamodb') {
14
- const sessionStoreOptions = (_a = config.sessionStoreOptions) !== null && _a !== void 0 ? _a : {};
60
+ const sessionStoreOptions = (_b = config.sessionStoreOptions) !== null && _b !== void 0 ? _b : {};
15
61
  sessionStore = dynamoDbSessionStore(sessionStoreOptions);
16
62
  }
17
63
  else if (config.sessionStoreType) {
@@ -20,16 +66,22 @@ export function sessions(config) {
20
66
  else {
21
67
  throw Error('missing sessionStoreType');
22
68
  }
23
- return session({
24
- secret: config.sessionSecret,
25
- store: sessionStore,
26
- resave: false,
27
- saveUninitialized: false,
28
- cookie: config.cookie || {
29
- httpOnly: true,
30
- path: config.cookiePath,
31
- secure: config.cookieSecure,
32
- sameSite: config.cookieSameSite
33
- },
34
- });
69
+ return [
70
+ session({
71
+ secret: config.sessionSecret,
72
+ store: sessionStore,
73
+ resave: false,
74
+ saveUninitialized: false,
75
+ cookie: config.cookie || {
76
+ httpOnly: true,
77
+ path: config.cookiePath,
78
+ secure: config.cookieSecure,
79
+ sameSite: config.cookieSameSite
80
+ },
81
+ }),
82
+ (req) => {
83
+ // make this function available to request handlers
84
+ req.destroySessionByIdTokenSid = sessionStore === null || sessionStore === void 0 ? void 0 : sessionStore.destroyByIdTokenSid;
85
+ }
86
+ ];
35
87
  }
@@ -0,0 +1,2 @@
1
+ export function redact(string: any, length?: number): string;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAAA,6DAEC"}
package/dist/utils.js ADDED
@@ -0,0 +1,3 @@
1
+ export function redact(string, length = 5) {
2
+ return (string === null || string === void 0 ? void 0 : string.substring(0, length)) + '***';
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/auth-bff",
3
- "version": "1.5.1",
3
+ "version": "1.6.0-beta1",
4
4
  "repository": "https://github.com/oslokommune/auth-bff.git",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,7 +8,8 @@
8
8
  "scripts": {
9
9
  "build": "tsc",
10
10
  "run": "node ./dist/server.mjs",
11
- "build-and-publish": "tsc && npm publish"
11
+ "build-and-publish": "tsc && npm publish",
12
+ "build-and-publish-prerelease": "tsc && npm publish --tag prerelease"
12
13
  },
13
14
  "exports": {
14
15
  "./vite-plugin": "./dist/vite-plugin.mjs",