@getstrata/bootstrap 0.2.66 → 0.4.2

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.
@@ -2,17 +2,19 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/providers/auth.ts
5
+ import { BasicAuthGuard } from "@getstrata/core/auth/basicAuthGuard";
5
6
  import {
6
7
  AuthManager,
7
8
  CompositeGuard,
8
9
  DatabaseTokenGuard,
9
10
  GuestGuard
10
11
  } from "@getstrata/core/auth/guard";
12
+ import { JwtGuard } from "@getstrata/core/auth/jwtGuard";
11
13
  import { SessionGuard } from "@getstrata/core/auth/sessionGuard";
12
14
 
13
15
  // ../../src/config/auth.ts
14
16
  var authConfig = {
15
- allowDevHeaders: (process.env.AUTH_DEV_HEADERS ?? "true") !== "false",
17
+ allowDevHeaders: process.env.AUTH_DEV_HEADERS === "true",
16
18
  tokenDefaultAbilities: ["*"]
17
19
  };
18
20
 
@@ -47,11 +49,22 @@ var authProvider = {
47
49
  name: "core.auth",
48
50
  register({ container, config }) {
49
51
  config.set("auth.allowDevHeaders", authConfig.allowDevHeaders);
50
- const guards = [new DatabaseTokenGuard(container), new SessionGuard(container)];
52
+ const apiGuard = new DatabaseTokenGuard(container);
53
+ const sessionGuard = new SessionGuard(container);
54
+ const jwtGuard = new JwtGuard;
55
+ const basicGuard = new BasicAuthGuard(container);
56
+ const guards = [apiGuard, jwtGuard, basicGuard, sessionGuard];
51
57
  if (authConfig.allowDevHeaders) {
52
58
  guards.push(new GuestGuard);
53
59
  }
54
- container.set(CORE_AUTH_TOKEN, new AuthManager(new CompositeGuard(guards)));
60
+ const auth = new AuthManager(new CompositeGuard(guards));
61
+ auth.registerGuard("api", apiGuard);
62
+ auth.registerGuard("access_token", apiGuard);
63
+ auth.registerGuard("jwt", jwtGuard);
64
+ auth.registerGuard("basic", basicGuard);
65
+ auth.registerGuard("web", sessionGuard);
66
+ auth.registerGuard("session", sessionGuard);
67
+ container.set(CORE_AUTH_TOKEN, auth);
55
68
  }
56
69
  };
57
70
  var auth_default = authProvider;
@@ -112,8 +125,12 @@ var queueConfig = {
112
125
  };
113
126
  // ../../src/bootstrap/env.ts
114
127
  import { defineEnvSchema } from "@getstrata/core/config/envSchema";
128
+ import { DEFAULT_SPA_PREFIX, FRONTEND_MODE_PATTERN } from "@getstrata/core/runtime/frontendMode";
115
129
  var appEnvSchema = defineEnvSchema({
116
- DATABASE_URL: { required: true, pattern: /^postgres(ql)?:\/\// },
130
+ DATABASE_URL: {
131
+ required: true,
132
+ pattern: /^(postgres(ql)?|mysql|sqlite):\/\//i
133
+ },
117
134
  PORT: {
118
135
  integer: true,
119
136
  minimum: 1,
@@ -141,9 +158,21 @@ var appEnvSchema = defineEnvSchema({
141
158
  pattern: /^(sync|async|redis)$/
142
159
  },
143
160
  AUTH_DEV_HEADERS: {
144
- default: "true",
161
+ default: "false",
145
162
  pattern: /^(true|false|0|1)$/
146
163
  },
164
+ DB_CONNECTION: {
165
+ default: "",
166
+ pattern: /^(pgsql|postgres|postgresql|mysql|mariadb|sqlite)?$/i
167
+ },
168
+ AUTH_DEFAULT_GUARD: {
169
+ default: "web"
170
+ },
171
+ JWT_TTL_SECONDS: {
172
+ integer: true,
173
+ minimum: 60,
174
+ default: "3600"
175
+ },
147
176
  APP_ENV: {
148
177
  default: "local"
149
178
  },
@@ -153,6 +182,13 @@ var appEnvSchema = defineEnvSchema({
153
182
  APP_URL: {
154
183
  default: "http://localhost:3000"
155
184
  },
185
+ FRONTEND_MODE: {
186
+ default: "api",
187
+ pattern: FRONTEND_MODE_PATTERN
188
+ },
189
+ SPA_PREFIX: {
190
+ default: DEFAULT_SPA_PREFIX
191
+ },
156
192
  API_PREFIX: {
157
193
  default: "/api/v1"
158
194
  },
@@ -306,7 +342,12 @@ function readDiscoverModulesState() {
306
342
  return state;
307
343
  }
308
344
  function configureModulesDirectory(modulesDir) {
309
- readDiscoverModulesState().configuredModulesDir = modulesDir;
345
+ const state = readDiscoverModulesState();
346
+ if (state.configuredModulesDir !== modulesDir) {
347
+ state.appModules.length = 0;
348
+ state.modulesReady = undefined;
349
+ }
350
+ state.configuredModulesDir = modulesDir;
310
351
  }
311
352
  function resolveModulesDirectory(options) {
312
353
  const state = readDiscoverModulesState();
@@ -470,7 +511,6 @@ var storageProvider = {
470
511
  var storage_default = storageProvider;
471
512
 
472
513
  // ../../src/bootstrap/providers/view.ts
473
- import { resolveMembershipLookup } from "@getstrata/core/auth/membershipContext";
474
514
  import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
475
515
  import { appDisplayName } from "@getstrata/core/runtime/appKeyPrefix";
476
516
  import { isViewsEnabled } from "@getstrata/core/runtime/frontendMode";
@@ -482,148 +522,6 @@ import {
482
522
  errorTemplateName,
483
523
  resolveWebLayoutData
484
524
  } from "@getstrata/core/view";
485
-
486
- // ../../src/modules/organization/repository.ts
487
- import { BaseRepository } from "@getstrata/core/database/baseRepository";
488
- import { NotFoundError } from "@getstrata/core/errors/http";
489
- import { currentTenantId } from "@getstrata/core/tenant/tenantContext";
490
-
491
- // ../../src/modules/organization/table.ts
492
- import { defineTable } from "@getstrata/core/database/table";
493
-
494
- // ../../src/domain/workhub.ts
495
- var ORGANIZATION_TABLE = "organization";
496
-
497
- // ../../src/modules/organization/table.ts
498
- var organizationTable = defineTable({
499
- name: ORGANIZATION_TABLE,
500
- primaryKey: "id",
501
- columns: ["id", "tenant_id", "name", "slug", "created_at", "updated_at", "deleted_at"],
502
- softDeletes: true,
503
- defaultOrderBy: { column: "id", direction: "ASC" }
504
- });
505
-
506
- // ../../src/modules/organization/repository.ts
507
- class OrganizationRepository extends BaseRepository {
508
- constructor() {
509
- super(organizationTable);
510
- }
511
- async findBySlug(slug) {
512
- return await this.firstOrNull({ slug });
513
- }
514
- async listForTenant(options) {
515
- return await this.findAll({
516
- limit: options.limit,
517
- offset: options.offset,
518
- where: { tenant_id: options.tenantId ?? currentTenantId() }
519
- });
520
- }
521
- async countForTenant(tenantId = currentTenantId()) {
522
- return await this.countWhere({ tenant_id: tenantId });
523
- }
524
- async findForTenantOrThrow(id, tenantId = currentTenantId()) {
525
- const organization = await this.findById(id);
526
- if (!organization || organization.tenant_id !== tenantId) {
527
- throw new NotFoundError(`SCIM group ${id} not found.`);
528
- }
529
- return organization;
530
- }
531
- }
532
- var repository_default = OrganizationRepository;
533
-
534
- // ../../src/modules/user/repository.ts
535
- import {
536
- emailLookupForQuery,
537
- protectEmail,
538
- revealEmail
539
- } from "@getstrata/core/crypto/fieldEncryption";
540
- import { revealMfaSecret } from "@getstrata/core/crypto/mfaSecret";
541
- import { BaseRepository as BaseRepository2 } from "@getstrata/core/database/baseRepository";
542
- import { currentTenantId as currentTenantId2 } from "@getstrata/core/tenant/tenantContext";
543
-
544
- // ../../src/modules/user/table.ts
545
- import { defineTable as defineTable2 } from "@getstrata/core/database/table";
546
- var userTable = defineTable2({
547
- name: "users",
548
- primaryKey: "id",
549
- columns: [
550
- "id",
551
- "name",
552
- "email",
553
- "email_lookup",
554
- "role",
555
- "tenant_id",
556
- "password_hash",
557
- "email_verified_at",
558
- "mfa_secret",
559
- "mfa_enabled",
560
- "mfa_recovery_codes",
561
- "profile_photo_path",
562
- "session_valid_after",
563
- "current_organization_id",
564
- "created_at",
565
- "updated_at"
566
- ],
567
- defaultOrderBy: { column: "id", direction: "ASC" }
568
- });
569
-
570
- // ../../src/modules/user/repository.ts
571
- class UserRepository extends BaseRepository2 {
572
- constructor() {
573
- super(userTable);
574
- }
575
- decode(record) {
576
- return {
577
- ...record,
578
- email: revealEmail(record.email),
579
- mfa_secret: revealMfaSecret(record.mfa_secret)
580
- };
581
- }
582
- async findById(id) {
583
- const record = await super.findById(id);
584
- return record ? this.decode(record) : null;
585
- }
586
- async findAll(options = {}) {
587
- const records = await super.findAll(options);
588
- return records.map((record) => this.decode(record));
589
- }
590
- async create(values) {
591
- const email = values.email;
592
- if (!email) {
593
- throw new Error("Email is required.");
594
- }
595
- const protectedEmail = protectEmail(email);
596
- const record = await super.create({
597
- ...values,
598
- tenant_id: values.tenant_id ?? currentTenantId2(),
599
- email: protectedEmail.storedEmail,
600
- email_lookup: protectedEmail.emailLookup,
601
- password_hash: values.password_hash ?? ""
602
- });
603
- return this.decode(record);
604
- }
605
- async updateByIdOrThrow(id, values, errorFactory) {
606
- const changes = { ...values };
607
- if (values.email !== undefined) {
608
- const protectedEmail = protectEmail(values.email);
609
- changes.email = protectedEmail.storedEmail;
610
- changes.email_lookup = protectedEmail.emailLookup;
611
- }
612
- const record = await super.updateByIdOrThrow(id, changes, errorFactory);
613
- return this.decode(record);
614
- }
615
- async findByEmail(email) {
616
- const records = await this.findWhere({ email_lookup: emailLookupForQuery(email) }, { limit: 1 });
617
- const record = records[0];
618
- return record ? this.decode(record) : null;
619
- }
620
- async countForTenant(tenantId = currentTenantId2()) {
621
- return await this.countWhere({ tenant_id: tenantId });
622
- }
623
- }
624
- var repository_default2 = UserRepository;
625
-
626
- // ../../src/bootstrap/providers/view.ts
627
525
  var CORE_VIEW_TOKEN = "core.view";
628
526
  var VIEW_DIRECTORY_CONFIG_KEY = "view.directory";
629
527
  var viewProvider = {
@@ -637,23 +535,11 @@ var viewProvider = {
637
535
  const engine = new EtaViewEngine(viewsDirectory, (request) => resolveWebLayoutData(container, request ?? currentRequestMeta().request));
638
536
  container.set(CORE_VIEW_TOKEN, engine);
639
537
  configureWebLayoutData({
640
- extra: async (user) => {
641
- const appName = appDisplayName();
642
- if (!user || typeof user.id !== "number") {
643
- return { appName, currentOrganization: null, organizations: [] };
644
- }
645
- try {
646
- const record = await new repository_default2().findByIdOrThrow(user.id);
647
- const memberships = await resolveMembershipLookup().listForUser(record.id);
648
- const organizationsRepo = new repository_default;
649
- const organizations = (await Promise.all(memberships.map((membership) => organizationsRepo.findById(membership.organization_id)))).filter((organization) => Boolean(organization && !organization.deleted_at));
650
- const currentId = record.current_organization_id ?? null;
651
- const currentOrganization = organizations.find((organization) => organization.id === currentId) ?? organizations[0] ?? null;
652
- return { appName, currentOrganization, organizations };
653
- } catch {
654
- return { appName, currentOrganization: null, organizations: [] };
655
- }
656
- }
538
+ extra: async () => ({
539
+ appName: appDisplayName(),
540
+ currentOrganization: null,
541
+ organizations: []
542
+ })
657
543
  });
658
544
  configureWebErrorView({
659
545
  render: async (input) => engine.render(errorTemplateName(input.status), {
@@ -2,15 +2,21 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/secretsGuard.ts
5
- var PUBLISHED_TEST_ADMIN_API_TOKEN = "workhub-admin-test-token";
6
- var PUBLISHED_TEST_MEMBER_API_TOKEN = "workhub-member-test-token";
7
- var PUBLISHED_TEST_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
5
+ import { isViewsMode, parseFrontendMode } from "@getstrata/core/runtime/frontendMode";
6
+ var PUBLISHED_TEST_ADMIN_API_TOKEN = "strata-admin-test-token";
7
+ var PUBLISHED_TEST_MEMBER_API_TOKEN = "strata-member-test-token";
8
+ var PUBLISHED_TEST_SCIM_BEARER_TOKEN = "strata-scim-test-token";
8
9
  var MIN_SESSION_SECRET_LENGTH = 32;
9
10
  var PUBLISHED_TEST_TOKENS = new Set([
10
11
  PUBLISHED_TEST_ADMIN_API_TOKEN,
11
- PUBLISHED_TEST_MEMBER_API_TOKEN
12
+ PUBLISHED_TEST_MEMBER_API_TOKEN,
13
+ "workhub-admin-test-token",
14
+ "workhub-member-test-token"
15
+ ]);
16
+ var PUBLISHED_TEST_SCIM_TOKENS = new Set([
17
+ PUBLISHED_TEST_SCIM_BEARER_TOKEN,
18
+ "workhub-scim-test-token"
12
19
  ]);
13
- var PUBLISHED_TEST_SCIM_TOKENS = new Set([PUBLISHED_TEST_SCIM_BEARER_TOKEN]);
14
20
  function isEnabled(value, defaultEnabled) {
15
21
  if (value === undefined) {
16
22
  return defaultEnabled;
@@ -34,7 +40,7 @@ function assertAuthDevHeadersDisabled(env) {
34
40
  function assertSessionSecret(env) {
35
41
  const secret = env.SESSION_SECRET?.trim() ?? "";
36
42
  if (secret.length < MIN_SESSION_SECRET_LENGTH) {
37
- throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx (32+ characters).");
43
+ throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx or hybrid (32+ characters).");
38
44
  }
39
45
  }
40
46
  function assertPublishedTestTokensRotated(env) {
@@ -96,8 +102,7 @@ function assertProductionSecrets(env = process.env) {
96
102
  assertTokenAuthProductionSecrets(env);
97
103
  }
98
104
  assertFeatureProductionSecrets(env);
99
- const frontendMode = (env.FRONTEND_MODE ?? "api").trim();
100
- if (frontendMode === "server-htmx") {
105
+ if (isViewsMode(parseFrontendMode(env.FRONTEND_MODE))) {
101
106
  assertSessionSecret(env);
102
107
  }
103
108
  }
@@ -2,6 +2,7 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/web/routing.ts
5
+ import { requestPrefersJson } from "@getstrata/core/http/contentNegotiation";
5
6
  import { withErrorHandling as withErrorHandling2 } from "@getstrata/core/http/response";
6
7
 
7
8
  // ../../src/bootstrap/http/securedRouteModelBinding.ts
@@ -33,7 +34,7 @@ import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/require
33
34
  import { createRequirePasswordConfirmMiddleware } from "@getstrata/core/http/requirePasswordConfirmMiddleware";
34
35
  import { createRequireVerifiedMiddleware } from "@getstrata/core/http/requireVerifiedMiddleware";
35
36
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
36
- import { withErrorHandling } from "@getstrata/core/http/response";
37
+ import { withErrorHandling, withJsonErrorHandling } from "@getstrata/core/http/response";
37
38
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
38
39
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
39
40
  import { createValidateSignatureMiddleware } from "@getstrata/core/http/signedUrl";
@@ -181,7 +182,7 @@ class HttpKernel {
181
182
  return withMiddleware(...middleware)(handler);
182
183
  }
183
184
  wrapApi(handler) {
184
- return this.wrap(["api", "authenticated"], handler);
185
+ return withJsonErrorHandling(this.wrap(["api", "authenticated"], handler));
185
186
  }
186
187
  wrapWeb(handler) {
187
188
  return withErrorHandling(this.wrap("web", handler));
@@ -366,7 +367,7 @@ function wrapWebThrottle(kernel, scope, handler, onThrottled) {
366
367
  const throttled = scope === "login" ? kernel.wrapLogin(handler) : kernel.wrapRegister(handler);
367
368
  return async (request) => {
368
369
  const response = await throttled(request);
369
- if (response.status === 429) {
370
+ if (response.status === 429 && !requestPrefersJson(request)) {
370
371
  return onThrottled(request);
371
372
  }
372
373
  return response;
@@ -2,11 +2,19 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/web/session.ts
5
- import { createHash, randomBytes } from "crypto";
5
+ import { createHmac, randomBytes } from "crypto";
6
6
  import { AuthManager } from "@getstrata/core/auth/guard";
7
7
  import { getBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
8
8
  import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
9
+ import { currentSqlDialect } from "@getstrata/core/database/dialect";
9
10
  import { readRequestCookie } from "@getstrata/core/http/cookies";
11
+ import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
12
+ function sqlPlaceholder(index) {
13
+ return currentSqlDialect().placeholder(index);
14
+ }
15
+ function sqlNow() {
16
+ return currentSqlDialect().nowExpression();
17
+ }
10
18
  function isSqlClient(value) {
11
19
  return typeof value.unsafe === "function";
12
20
  }
@@ -29,22 +37,34 @@ function defaultMapSessionUser(user) {
29
37
  role: user.is_admin ? "admin" : "member"
30
38
  };
31
39
  }
40
+ function sessionDisplayName(row, email) {
41
+ if (typeof row.name === "string" && row.name.trim() !== "") {
42
+ return row.name;
43
+ }
44
+ const first = typeof row.first_name === "string" ? row.first_name.trim() : "";
45
+ const last = typeof row.last_name === "string" ? row.last_name.trim() : "";
46
+ const composed = `${first} ${last}`.trim();
47
+ return composed || email;
48
+ }
49
+ function mapSessionUserRow(row) {
50
+ const email = typeof row.email === "string" ? row.email : "";
51
+ return {
52
+ id: Number(row.user_id ?? row.id),
53
+ name: sessionDisplayName(row, email),
54
+ email,
55
+ learn_subscriber: Boolean(row.learn_subscriber),
56
+ is_admin: Boolean(row.is_admin)
57
+ };
58
+ }
32
59
  async function defaultLoadSessionUser(sql, sessionId) {
33
- const rows = await sql.unsafe(`SELECT s.id, s.user_id, s.expires_at, u.name, u.email, u.learn_subscriber,
34
- COALESCE(u.is_admin, false) AS is_admin
60
+ const rows = await sql.unsafe(`SELECT s.user_id, s.expires_at, u.*
35
61
  FROM sessions s
36
62
  INNER JOIN users u ON u.id = s.user_id
37
- WHERE s.id = $1 AND s.expires_at > NOW()`, [sessionId]);
63
+ WHERE s.id = ${sqlPlaceholder(1)} AND s.expires_at > ${sqlNow()}`, [sessionId]);
38
64
  const row = rows[0];
39
65
  if (!row)
40
66
  return null;
41
- return {
42
- id: row.user_id,
43
- name: row.name,
44
- email: row.email,
45
- learn_subscriber: row.learn_subscriber,
46
- is_admin: row.is_admin
47
- };
67
+ return mapSessionUserRow(row);
48
68
  }
49
69
  function redirectWithCookie(location, setCookie, status) {
50
70
  return new Response(null, {
@@ -82,7 +102,7 @@ class CookieSessionStore {
82
102
  if (!raw)
83
103
  return null;
84
104
  const [sessionId, signature] = raw.split(".");
85
- if (!sessionId || !signature || signature !== this.sign(sessionId)) {
105
+ if (!sessionId || !signature || !timingSafeCompareString(signature, this.sign(sessionId))) {
86
106
  return null;
87
107
  }
88
108
  return sessionId;
@@ -96,18 +116,28 @@ class CookieSessionStore {
96
116
  sql() {
97
117
  return resolveSql(this.sqlSource);
98
118
  }
99
- async create(user) {
119
+ async create(user, meta = {}) {
100
120
  const id = randomBytes(32).toString("hex");
101
121
  const expires = new Date(Date.now() + this.maxAgeSeconds * 1000);
102
- await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at) VALUES ($1, $2, $3)`, [
103
- id,
104
- user.id,
105
- expires
106
- ]);
122
+ await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at, user_agent, ip_address, last_active_at)
123
+ VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, expires, meta.userAgent ?? null, meta.ipAddress ?? null]);
107
124
  return id;
108
125
  }
109
126
  async destroy(sessionId) {
110
- await this.sql().unsafe(`DELETE FROM sessions WHERE id = $1`, [sessionId]);
127
+ await this.sql().unsafe(`DELETE FROM sessions WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
128
+ }
129
+ async destroyOtherSessions(userId, keepSessionId) {
130
+ await this.sql().unsafe(`DELETE FROM sessions WHERE user_id = ${sqlPlaceholder(1)} AND id <> ${sqlPlaceholder(2)}`, [userId, keepSessionId]);
131
+ }
132
+ async listForUser(userId) {
133
+ const dialect = currentSqlDialect();
134
+ return this.sql().unsafe(`SELECT id, user_id, user_agent, ip_address, last_active_at, expires_at
135
+ FROM sessions
136
+ WHERE user_id = ${dialect.placeholder(1)} AND expires_at > ${dialect.nowExpression()}
137
+ ORDER BY last_active_at DESC${dialect.nullsLastSuffix()}, expires_at DESC`, [userId]);
138
+ }
139
+ async touch(sessionId) {
140
+ await this.sql().unsafe(`UPDATE sessions SET last_active_at = ${sqlNow()} WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
111
141
  }
112
142
  async read(request) {
113
143
  const sessionId = this.sessionIdFromRequest(request);
@@ -116,7 +146,7 @@ class CookieSessionStore {
116
146
  return this.loadSessionUser(this.sql(), sessionId);
117
147
  }
118
148
  sign(value) {
119
- return createHash("sha256").update(`${value}.${this.secret}`).digest("hex").slice(0, 32);
149
+ return createHmac("sha256", this.secret).update(value).digest("hex").slice(0, 32);
120
150
  }
121
151
  }
122
152
 
@@ -139,11 +169,14 @@ class CookieSessionGuard {
139
169
  class CookieSessionAuthManager extends AuthManager {
140
170
  store;
141
171
  constructor(store, mapUser = defaultMapSessionUser) {
142
- super(new CookieSessionGuard(store, mapUser));
172
+ const guard = new CookieSessionGuard(store, mapUser);
173
+ super(guard);
143
174
  this.store = store;
175
+ this.registerGuard("web", guard);
176
+ this.registerGuard("session", guard);
144
177
  }
145
- async signIn(user) {
146
- const sessionId = await this.store.create(user);
178
+ async signIn(user, meta = {}) {
179
+ const sessionId = await this.store.create(user, meta);
147
180
  return { sessionId, setCookie: this.store.cookieHeader(user, sessionId) };
148
181
  }
149
182
  async signOut(request) {
@@ -153,8 +186,8 @@ class CookieSessionAuthManager extends AuthManager {
153
186
  }
154
187
  return { setCookie: this.store.clearCookieHeader() };
155
188
  }
156
- async signInRedirect(user, location, status = 302) {
157
- const { setCookie } = await this.signIn(user);
189
+ async signInRedirect(user, location, status = 302, meta = {}) {
190
+ const { setCookie } = await this.signIn(user, meta);
158
191
  return redirectWithCookie(location, setCookie, status);
159
192
  }
160
193
  async signOutRedirect(request, location, status = 302) {