@absolutejs/auth 0.36.0 → 0.38.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/dist/index.js CHANGED
@@ -1,4 +1,19 @@
1
1
  // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
2
17
  var __require = import.meta.require;
3
18
 
4
19
  // node_modules/citra/dist/index.js
@@ -8300,10 +8315,26 @@ var GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
8300
8315
  var LIST_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:ListResponse";
8301
8316
  var ERROR_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:Error";
8302
8317
  var SPC_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig";
8318
+ var RESOURCE_TYPE_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:ResourceType";
8303
8319
  var SCIM_CONTENT_TYPE = "application/scim+json";
8304
8320
  var FILTER_MAX_RESULTS = 200;
8305
8321
  var FILTER_PATTERN = /^\s*(\w[\w.]*)\s+eq\s+"([^"]*)"\s*$/u;
8306
- var toUserResource = (user, location) => {
8322
+ var mergeCustomSchemas = (resource, custom, map) => {
8323
+ if (custom === undefined || map === undefined)
8324
+ return;
8325
+ const extra = map.toScim(custom);
8326
+ for (const [key, value] of Object.entries(extra)) {
8327
+ resource[key] = value;
8328
+ }
8329
+ const extraSchemas = (map.schemas ?? []).map((schema) => schema.id);
8330
+ if (extraSchemas.length === 0)
8331
+ return;
8332
+ const { schemas } = resource;
8333
+ if (Array.isArray(schemas)) {
8334
+ resource.schemas = [...schemas, ...extraSchemas];
8335
+ }
8336
+ };
8337
+ var toUserResource = (user, location, map) => {
8307
8338
  const resource = {
8308
8339
  active: user.active,
8309
8340
  id: user.id,
@@ -8324,6 +8355,7 @@ var toUserResource = (user, location) => {
8324
8355
  if (user.email !== undefined) {
8325
8356
  resource.emails = [{ primary: true, value: user.email }];
8326
8357
  }
8358
+ mergeCustomSchemas(resource, user.custom, map);
8327
8359
  return resource;
8328
8360
  };
8329
8361
  var stringField = (source, field) => {
@@ -8338,7 +8370,7 @@ var primaryEmail = (emails) => {
8338
8370
  const flagged = emails.find((entry) => typeof entry === "object" && entry !== null && Reflect.get(entry, "primary") === true);
8339
8371
  return stringField(flagged ?? emails[0], "value");
8340
8372
  };
8341
- var parseUserInput = (body) => {
8373
+ var parseUserInput = (body, map) => {
8342
8374
  if (typeof body !== "object" || body === null)
8343
8375
  return;
8344
8376
  const userName = stringField(body, "userName");
@@ -8346,8 +8378,14 @@ var parseUserInput = (body) => {
8346
8378
  return;
8347
8379
  const active = Reflect.get(body, "active");
8348
8380
  const name = Reflect.get(body, "name");
8381
+ const bodyRecord = {};
8382
+ for (const key of Object.keys(body)) {
8383
+ bodyRecord[key] = Reflect.get(body, key);
8384
+ }
8385
+ const custom = map === undefined ? undefined : map.fromScim(bodyRecord);
8349
8386
  return {
8350
8387
  active: typeof active === "boolean" ? active : true,
8388
+ custom,
8351
8389
  displayName: stringField(body, "displayName"),
8352
8390
  email: primaryEmail(Reflect.get(body, "emails")),
8353
8391
  externalId: stringField(body, "externalId"),
@@ -8408,6 +8446,7 @@ var applyOperation = (target, operation) => {
8408
8446
  var applyPatch = (user, body) => {
8409
8447
  const next = {
8410
8448
  active: user.active,
8449
+ custom: user.custom,
8411
8450
  displayName: user.displayName,
8412
8451
  email: user.email,
8413
8452
  externalId: user.externalId,
@@ -8453,6 +8492,15 @@ var scimJson = (resource, httpStatus) => new Response(JSON.stringify(resource),
8453
8492
  status: httpStatus
8454
8493
  });
8455
8494
  var serviceProviderConfig = (location) => ({
8495
+ authenticationSchemes: [
8496
+ {
8497
+ description: "Authentication scheme using the OAuth Bearer Token Standard",
8498
+ name: "OAuth Bearer Token",
8499
+ primary: true,
8500
+ specUri: "https://www.rfc-editor.org/info/rfc6750",
8501
+ type: "oauthbearertoken"
8502
+ }
8503
+ ],
8456
8504
  bulk: { maxOperations: 0, maxPayloadSize: 0, supported: false },
8457
8505
  changePassword: { supported: false },
8458
8506
  etag: { supported: false },
@@ -8462,6 +8510,111 @@ var serviceProviderConfig = (location) => ({
8462
8510
  schemas: [SPC_SCHEMA],
8463
8511
  sort: { supported: false }
8464
8512
  });
8513
+ var CORE_USER_ATTRIBUTES = [
8514
+ { multiValued: false, name: "userName", required: true, type: "string" },
8515
+ { multiValued: false, name: "active", required: false, type: "boolean" },
8516
+ { multiValued: false, name: "displayName", required: false, type: "string" },
8517
+ { multiValued: false, name: "externalId", required: false, type: "string" },
8518
+ {
8519
+ multiValued: false,
8520
+ name: "name",
8521
+ required: false,
8522
+ subAttributes: [
8523
+ { multiValued: false, name: "givenName", required: false, type: "string" },
8524
+ { multiValued: false, name: "familyName", required: false, type: "string" }
8525
+ ],
8526
+ type: "complex"
8527
+ },
8528
+ {
8529
+ multiValued: true,
8530
+ name: "emails",
8531
+ required: false,
8532
+ subAttributes: [
8533
+ { multiValued: false, name: "value", required: false, type: "string" },
8534
+ { multiValued: false, name: "primary", required: false, type: "boolean" }
8535
+ ],
8536
+ type: "complex"
8537
+ }
8538
+ ];
8539
+ var CORE_GROUP_ATTRIBUTES = [
8540
+ { multiValued: false, name: "displayName", required: true, type: "string" },
8541
+ { multiValued: false, name: "externalId", required: false, type: "string" },
8542
+ {
8543
+ multiValued: true,
8544
+ name: "members",
8545
+ required: false,
8546
+ subAttributes: [
8547
+ { multiValued: false, name: "value", required: false, type: "string" },
8548
+ { multiValued: false, name: "display", required: false, type: "string" }
8549
+ ],
8550
+ type: "complex"
8551
+ }
8552
+ ];
8553
+ var CORE_USER_SCHEMA = {
8554
+ attributes: CORE_USER_ATTRIBUTES,
8555
+ description: "User Account",
8556
+ id: USER_SCHEMA,
8557
+ name: "User"
8558
+ };
8559
+ var CORE_GROUP_SCHEMA = {
8560
+ attributes: CORE_GROUP_ATTRIBUTES,
8561
+ description: "Group",
8562
+ id: GROUP_SCHEMA,
8563
+ name: "Group"
8564
+ };
8565
+ var schemaResource = (schema, location) => ({
8566
+ attributes: schema.attributes,
8567
+ description: schema.description ?? schema.name,
8568
+ id: schema.id,
8569
+ meta: { location, resourceType: "Schema" },
8570
+ name: schema.name
8571
+ });
8572
+ var schemaList = (location, extras = []) => {
8573
+ const all = [CORE_USER_SCHEMA, CORE_GROUP_SCHEMA, ...extras];
8574
+ const resources = all.map((schema) => schemaResource(schema, `${location}/${schema.id}`));
8575
+ return listResponse(resources);
8576
+ };
8577
+ var schemaOne = (location, id, extras = []) => {
8578
+ const all = [CORE_USER_SCHEMA, CORE_GROUP_SCHEMA, ...extras];
8579
+ const found = all.find((schema) => schema.id === id);
8580
+ if (found === undefined)
8581
+ return;
8582
+ return schemaResource(found, location);
8583
+ };
8584
+ var resourceTypeUser = (location, usersEndpoint, extensionSchemaIds) => ({
8585
+ description: "User Account",
8586
+ endpoint: usersEndpoint,
8587
+ id: "User",
8588
+ meta: { location, resourceType: "ResourceType" },
8589
+ name: "User",
8590
+ schema: USER_SCHEMA,
8591
+ schemaExtensions: extensionSchemaIds.map((schema) => ({
8592
+ required: false,
8593
+ schema
8594
+ })),
8595
+ schemas: [RESOURCE_TYPE_SCHEMA]
8596
+ });
8597
+ var resourceTypeGroup = (location, groupsEndpoint) => ({
8598
+ description: "Group",
8599
+ endpoint: groupsEndpoint,
8600
+ id: "Group",
8601
+ meta: { location, resourceType: "ResourceType" },
8602
+ name: "Group",
8603
+ schema: GROUP_SCHEMA,
8604
+ schemas: [RESOURCE_TYPE_SCHEMA]
8605
+ });
8606
+ var resourceTypeList = (location, usersEndpoint, groupsEndpoint, extras = []) => listResponse([
8607
+ resourceTypeUser(`${location}/User`, usersEndpoint, extras.map((schema) => schema.id)),
8608
+ resourceTypeGroup(`${location}/Group`, groupsEndpoint)
8609
+ ]);
8610
+ var resourceTypeOne = (location, id, usersEndpoint, groupsEndpoint, extras = []) => {
8611
+ if (id === "User") {
8612
+ return resourceTypeUser(location, usersEndpoint, extras.map((schema) => schema.id));
8613
+ }
8614
+ if (id === "Group")
8615
+ return resourceTypeGroup(location, groupsEndpoint);
8616
+ return;
8617
+ };
8465
8618
  var REMOVE_MEMBER_PATTERN = /^members\[\s*value\s+eq\s+"([^"]*)"\s*\]$/iu;
8466
8619
  var parseMembers = (value) => {
8467
8620
  if (!Array.isArray(value))
@@ -8562,6 +8715,7 @@ var SCIM_CONTENT_TYPE2 = "application/scim+json";
8562
8715
  var unauthorized = () => scimError(SCIM_UNAUTHORIZED, "Invalid or missing SCIM bearer token");
8563
8716
  var notImplemented = () => scimError(SCIM_NOT_IMPLEMENTED, "Group provisioning is not configured");
8564
8717
  var scimRoutes = ({
8718
+ customAttributes,
8565
8719
  getScimGroup,
8566
8720
  getScimUser,
8567
8721
  listScimGroups,
@@ -8580,8 +8734,17 @@ var scimRoutes = ({
8580
8734
  const groupsRoute = `${scimRoute}/Groups`;
8581
8735
  const groupRoute = `${scimRoute}/Groups/:id`;
8582
8736
  const spcRoute = `${scimRoute}/ServiceProviderConfig`;
8737
+ const schemasRoute = `${scimRoute}/Schemas`;
8738
+ const schemaRoute = `${scimRoute}/Schemas/:id`;
8739
+ const resourceTypesRoute = `${scimRoute}/ResourceTypes`;
8740
+ const resourceTypeRoute = `${scimRoute}/ResourceTypes/:id`;
8741
+ const extensionSchemas = customAttributes?.schemas ?? [];
8583
8742
  const userLocation = (requestUrl, id) => `${new URL(requestUrl).origin}${scimRoute}/Users/${id}`;
8584
8743
  const groupLocation = (requestUrl, id) => `${new URL(requestUrl).origin}${scimRoute}/Groups/${id}`;
8744
+ const schemasLocation = (requestUrl) => `${new URL(requestUrl).origin}${scimRoute}/Schemas`;
8745
+ const resourceTypesLocation = (requestUrl) => `${new URL(requestUrl).origin}${scimRoute}/ResourceTypes`;
8746
+ const usersEndpoint = `${scimRoute}/Users`;
8747
+ const groupsEndpoint = `${scimRoute}/Groups`;
8585
8748
  return new Elysia29().onParse(({ request }, contentType) => contentType === SCIM_CONTENT_TYPE2 ? request.json() : undefined).get(spcRoute, async ({ headers, request }) => {
8586
8749
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8587
8750
  if (organizationId === undefined)
@@ -8591,12 +8754,12 @@ var scimRoutes = ({
8591
8754
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8592
8755
  if (organizationId === undefined)
8593
8756
  return unauthorized();
8594
- const input = parseUserInput(body);
8757
+ const input = parseUserInput(body, customAttributes);
8595
8758
  if (input === undefined) {
8596
8759
  return scimError(SCIM_BAD_REQUEST, "Request body is not a valid SCIM User", "invalidValue");
8597
8760
  }
8598
8761
  const user = await onScimUserCreate({ input, organizationId });
8599
- return scimJson(toUserResource(user, userLocation(request.url, user.id)), SCIM_CREATED);
8762
+ return scimJson(toUserResource(user, userLocation(request.url, user.id), customAttributes), SCIM_CREATED);
8600
8763
  }).get(usersRoute, async ({ headers, query, request }) => {
8601
8764
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8602
8765
  if (organizationId === undefined)
@@ -8605,7 +8768,7 @@ var scimRoutes = ({
8605
8768
  filter: parseFilter(query.filter),
8606
8769
  organizationId
8607
8770
  });
8608
- const resources = users.map((user) => toUserResource(user, userLocation(request.url, user.id)));
8771
+ const resources = users.map((user) => toUserResource(user, userLocation(request.url, user.id), customAttributes));
8609
8772
  return scimJson(listResponse(resources), SCIM_OK);
8610
8773
  }, { query: t26.Object({ filter: t26.Optional(t26.String()) }) }).get(userRoute, async ({ headers, params: { id }, request }) => {
8611
8774
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
@@ -8615,12 +8778,12 @@ var scimRoutes = ({
8615
8778
  if (user === undefined) {
8616
8779
  return scimError(SCIM_NOT_FOUND, "User not found");
8617
8780
  }
8618
- return scimJson(toUserResource(user, userLocation(request.url, id)), SCIM_OK);
8781
+ return scimJson(toUserResource(user, userLocation(request.url, id), customAttributes), SCIM_OK);
8619
8782
  }, { params: t26.Object({ id: t26.String() }) }).put(userRoute, async ({ body, headers, params: { id }, request }) => {
8620
8783
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8621
8784
  if (organizationId === undefined)
8622
8785
  return unauthorized();
8623
- const input = parseUserInput(body);
8786
+ const input = parseUserInput(body, customAttributes);
8624
8787
  if (input === undefined) {
8625
8788
  return scimError(SCIM_BAD_REQUEST, "Request body is not a valid SCIM User", "invalidValue");
8626
8789
  }
@@ -8632,7 +8795,7 @@ var scimRoutes = ({
8632
8795
  if (user === undefined) {
8633
8796
  return scimError(SCIM_NOT_FOUND, "User not found");
8634
8797
  }
8635
- return scimJson(toUserResource(user, userLocation(request.url, id)), SCIM_OK);
8798
+ return scimJson(toUserResource(user, userLocation(request.url, id), customAttributes), SCIM_OK);
8636
8799
  }, { params: t26.Object({ id: t26.String() }) }).patch(userRoute, async ({ body, headers, params: { id }, request }) => {
8637
8800
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8638
8801
  if (organizationId === undefined)
@@ -8649,7 +8812,7 @@ var scimRoutes = ({
8649
8812
  if (user === undefined) {
8650
8813
  return scimError(SCIM_NOT_FOUND, "User not found");
8651
8814
  }
8652
- return scimJson(toUserResource(user, userLocation(request.url, id)), SCIM_OK);
8815
+ return scimJson(toUserResource(user, userLocation(request.url, id), customAttributes), SCIM_OK);
8653
8816
  }, { params: t26.Object({ id: t26.String() }) }).delete(userRoute, async ({ headers, params: { id } }) => {
8654
8817
  const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8655
8818
  if (organizationId === undefined)
@@ -8741,6 +8904,34 @@ var scimRoutes = ({
8741
8904
  return notImplemented();
8742
8905
  await onScimGroupDelete({ id, organizationId });
8743
8906
  return new Response(null, { status: SCIM_NO_CONTENT });
8907
+ }, { params: t26.Object({ id: t26.String() }) }).get(schemasRoute, async ({ headers, request }) => {
8908
+ const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8909
+ if (organizationId === undefined)
8910
+ return unauthorized();
8911
+ return scimJson(schemaList(schemasLocation(request.url), extensionSchemas), SCIM_OK);
8912
+ }).get(schemaRoute, async ({ headers, params: { id }, request }) => {
8913
+ const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8914
+ if (organizationId === undefined)
8915
+ return unauthorized();
8916
+ const schema = schemaOne(`${schemasLocation(request.url)}/${id}`, id, extensionSchemas);
8917
+ if (schema === undefined) {
8918
+ return scimError(SCIM_NOT_FOUND, "Schema not found");
8919
+ }
8920
+ return scimJson(schema, SCIM_OK);
8921
+ }, { params: t26.Object({ id: t26.String() }) }).get(resourceTypesRoute, async ({ headers, request }) => {
8922
+ const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8923
+ if (organizationId === undefined)
8924
+ return unauthorized();
8925
+ return scimJson(resourceTypeList(resourceTypesLocation(request.url), usersEndpoint, groupsEndpoint, extensionSchemas), SCIM_OK);
8926
+ }).get(resourceTypeRoute, async ({ headers, params: { id }, request }) => {
8927
+ const organizationId = await resolveScimOrganization(scimTokenStore, headers.authorization);
8928
+ if (organizationId === undefined)
8929
+ return unauthorized();
8930
+ const resourceType = resourceTypeOne(`${resourceTypesLocation(request.url)}/${id}`, id, usersEndpoint, groupsEndpoint, extensionSchemas);
8931
+ if (resourceType === undefined) {
8932
+ return scimError(SCIM_NOT_FOUND, "ResourceType not found");
8933
+ }
8934
+ return scimJson(resourceType, SCIM_OK);
8744
8935
  }, { params: t26.Object({ id: t26.String() }) });
8745
8936
  };
8746
8937
 
@@ -20771,6 +20962,132 @@ var switchActiveSession = ({
20771
20962
  };
20772
20963
  // src/tenancy.ts
20773
20964
  var hasOrganizationScope = (value) => typeof value.organizationId === "string" && value.organizationId.length > 0;
20965
+ // src/credentials/backgroundOps.ts
20966
+ var HIBP_BREACHED_ACCOUNT_URL = "https://haveibeenpwned.com/api/v3/breachedaccount/";
20967
+ var HIBP_USER_AGENT = "@absolutejs/auth breach scanner";
20968
+ var DEFAULT_PAUSE_MS = 1700;
20969
+ var HIBP_NOT_FOUND = 404;
20970
+ var HIBP_RATE_LIMITED = 429;
20971
+ var MS_PER_DAY = 86400000;
20972
+ var MS_PER_SECOND3 = 1000;
20973
+ var sleep = (delayMs) => new Promise((resolve) => {
20974
+ setTimeout(resolve, delayMs);
20975
+ });
20976
+ var isBreachRecord = (entry) => {
20977
+ if (typeof entry !== "object" || entry === null)
20978
+ return false;
20979
+ if (!("name" in entry))
20980
+ return false;
20981
+ const candidate = entry;
20982
+ return typeof candidate.name === "string";
20983
+ };
20984
+ var isBreachRecordArray = (value) => Array.isArray(value) && value.every(isBreachRecord);
20985
+ var checkEmailBreaches = async (email, apiKey, truncate) => {
20986
+ const url = `${HIBP_BREACHED_ACCOUNT_URL}${encodeURIComponent(email)}?truncateResponse=${truncate ? "true" : "false"}`;
20987
+ const response = await fetch(url, {
20988
+ headers: {
20989
+ "hibp-api-key": apiKey,
20990
+ "user-agent": HIBP_USER_AGENT
20991
+ }
20992
+ });
20993
+ if (response.status === HIBP_NOT_FOUND)
20994
+ return [];
20995
+ if (response.status === HIBP_RATE_LIMITED) {
20996
+ const retryAfter = Number(response.headers.get("retry-after") ?? "0");
20997
+ if (retryAfter > 0)
20998
+ await sleep(retryAfter * MS_PER_SECOND3);
20999
+ return [];
21000
+ }
21001
+ if (!response.ok)
21002
+ return [];
21003
+ const body = await response.json();
21004
+ if (!isBreachRecordArray(body))
21005
+ return [];
21006
+ return body;
21007
+ };
21008
+ var scanEmail = async (email, apiKey, truncate, onBreachFound) => {
21009
+ const breaches = await checkEmailBreaches(email, apiKey, truncate);
21010
+ if (breaches.length === 0)
21011
+ return false;
21012
+ await onBreachFound({ breaches, email });
21013
+ return true;
21014
+ };
21015
+ var scanPage = async (options) => {
21016
+ let scanned = 0;
21017
+ let breached = 0;
21018
+ for (const email of options.emails) {
21019
+ scanned += 1;
21020
+ const hit = await scanEmail(email, options.apiKey, options.truncate, options.onBreachFound);
21021
+ if (hit)
21022
+ breached += 1;
21023
+ await sleep(options.pauseMs);
21024
+ }
21025
+ return { breached, scanned };
21026
+ };
21027
+ var runEmailBreachScan = async (input) => {
21028
+ const pauseMs = input.pauseMs ?? DEFAULT_PAUSE_MS;
21029
+ const truncate = input.truncateResponse ?? true;
21030
+ let scanned = 0;
21031
+ let breached = 0;
21032
+ let cursor;
21033
+ do {
21034
+ const page = await input.iterateEmails(cursor);
21035
+ const tally = await scanPage({
21036
+ apiKey: input.hibpApiKey,
21037
+ emails: page.emails,
21038
+ onBreachFound: input.onBreachFound,
21039
+ pauseMs,
21040
+ truncate
21041
+ });
21042
+ scanned += tally.scanned;
21043
+ breached += tally.breached;
21044
+ cursor = page.nextCursor;
21045
+ } while (cursor !== undefined);
21046
+ const result = { breached, scanned };
21047
+ return result;
21048
+ };
21049
+ var pruneCandidate = async (candidate, cutoff, dryRun, onDelete) => {
21050
+ const reference = candidate.lastLoginAt ?? candidate.createdAt;
21051
+ if (reference === undefined || reference === null)
21052
+ return false;
21053
+ if (reference >= cutoff)
21054
+ return false;
21055
+ if (!dryRun)
21056
+ await onDelete(candidate.userId);
21057
+ return true;
21058
+ };
21059
+ var prunePage = async (options) => {
21060
+ const pruned = [];
21061
+ for (const candidate of options.candidates) {
21062
+ const removed = await pruneCandidate(candidate, options.cutoff, options.dryRun, options.onDelete);
21063
+ if (removed)
21064
+ pruned.push(candidate.userId);
21065
+ }
21066
+ return pruned;
21067
+ };
21068
+ var pruneInactiveUsers = async (input) => {
21069
+ const now = input.now?.() ?? Date.now();
21070
+ const thresholdMs = input.olderThanDays * MS_PER_DAY;
21071
+ const cutoff = now - thresholdMs;
21072
+ const dryRun = input.dryRun ?? false;
21073
+ const prunedUserIds = [];
21074
+ let scanned = 0;
21075
+ let cursor;
21076
+ do {
21077
+ const page = await input.iterateUsers(cursor);
21078
+ scanned += page.users.length;
21079
+ const removed = await prunePage({
21080
+ candidates: page.users,
21081
+ cutoff,
21082
+ dryRun,
21083
+ onDelete: input.onDelete
21084
+ });
21085
+ prunedUserIds.push(...removed);
21086
+ cursor = page.nextCursor;
21087
+ } while (cursor !== undefined);
21088
+ const result = { dryRun, prunedUserIds, scanned };
21089
+ return result;
21090
+ };
20774
21091
  // src/credentials/emailValidation.ts
20775
21092
  import { resolveMx } from "dns/promises";
20776
21093
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
@@ -21574,6 +21891,15 @@ var createPostgresAuditSink = (db) => ({
21574
21891
  return deleted.length;
21575
21892
  }
21576
21893
  });
21894
+ // src/scim/extensions.ts
21895
+ var defineScimAttributeMap = (map) => map;
21896
+ var diffScimGroupMembers = (current, next) => {
21897
+ const currentValues = new Set(current.map((member) => member.value));
21898
+ const nextValues = new Set(next.map((member) => member.value));
21899
+ const added = next.filter((member) => !currentValues.has(member.value));
21900
+ const removed = current.filter((member) => !nextValues.has(member.value));
21901
+ return { added, removed };
21902
+ };
21577
21903
  // src/scim/inMemoryScimTokenStore.ts
21578
21904
  var createInMemoryScimTokenStore = () => {
21579
21905
  const tokens = new Map;
@@ -24373,6 +24699,7 @@ export {
24373
24699
  samlServiceProvidersTable,
24374
24700
  samlIdpRoutes,
24375
24701
  runMigrations,
24702
+ runEmailBreachScan,
24376
24703
  rotateVaultKey,
24377
24704
  rotateMfaEncryptionKey,
24378
24705
  rolesTable,
@@ -24400,6 +24727,7 @@ export {
24400
24727
  readUserInfoBearer,
24401
24728
  readSessionRing,
24402
24729
  pushAuthorizationRequest,
24730
+ pruneInactiveUsers,
24403
24731
  providers,
24404
24732
  providerOptions,
24405
24733
  protectRoutePlugin,
@@ -24498,10 +24826,12 @@ export {
24498
24826
  endImpersonation,
24499
24827
  encryptTotpSecret,
24500
24828
  encryptSecret,
24829
+ diffScimGroupMembers,
24501
24830
  denyDeviceAuthorization,
24502
24831
  denyBackchannelAuth,
24503
24832
  deleteWarrant,
24504
24833
  deleteRegisteredClient,
24834
+ defineScimAttributeMap,
24505
24835
  defineProvidersConfiguration,
24506
24836
  defineAuthSettings,
24507
24837
  defineAuthHtmxConfig,
@@ -24703,5 +25033,5 @@ export {
24703
25033
  AuthIdentityConflictError
24704
25034
  };
24705
25035
 
24706
- //# debugId=1069DAAF739CAF9F64756E2164756E21
25036
+ //# debugId=5A0B79F67182545764756E2164756E21
24707
25037
  //# sourceMappingURL=index.js.map