@absolutejs/auth 0.27.0-beta.7 → 0.27.0-beta.9

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,9 +1,13 @@
1
- import type { KnownDeviceStore, LoginHistoryStore, RiskAction, RiskAssessment, RiskContext, RiskSignal } from './types';
1
+ import type { KnownDeviceStore, LoginHistoryStore, RiskAction, RiskAssessment, RiskContext, RiskSignal, RiskThresholds, RiskWeights, WeightedRiskAssessment } from './types';
2
2
  export type AdaptiveConfig = {
3
3
  historyLimit?: number;
4
4
  knownDeviceStore: KnownDeviceStore;
5
5
  loginHistoryStore: LoginHistoryStore;
6
6
  maxTravelKmh?: number;
7
+ offHours?: {
8
+ end: number;
9
+ start: number;
10
+ };
7
11
  rules?: Partial<Record<RiskSignal, RiskAction>>;
8
12
  velocityMaxAttempts?: number;
9
13
  velocityWindowMs?: number;
@@ -14,9 +18,17 @@ export declare const createRiskEngine: (config: AdaptiveConfig) => {
14
18
  recordAttempt: (context: RiskContext & {
15
19
  outcome: RiskAction;
16
20
  }) => Promise<void>;
21
+ scoreRisk: (context: RiskContext, options?: {
22
+ thresholds?: RiskThresholds;
23
+ weights?: RiskWeights;
24
+ }) => Promise<WeightedRiskAssessment>;
17
25
  trustDevice: (userId: string, deviceId: string, label?: string) => Promise<void>;
18
26
  };
19
27
  export declare const recordLoginAttempt: (config: AdaptiveConfig, context: RiskContext & {
20
28
  outcome: RiskAction;
21
29
  }) => Promise<void>;
30
+ export declare const scoreRisk: (config: AdaptiveConfig & {
31
+ thresholds?: RiskThresholds;
32
+ weights?: RiskWeights;
33
+ }, context: RiskContext) => Promise<WeightedRiskAssessment>;
22
34
  export declare const trustDevice: (config: AdaptiveConfig, userId: string, deviceId: string, label?: string) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export type DeviceSignals = Record<string, unknown>;
2
+ export declare const fingerprintDevice: (signals: DeviceSignals) => Promise<string>;
@@ -1,5 +1,5 @@
1
1
  export type RiskAction = 'allow' | 'deny' | 'step_up';
2
- export type RiskSignal = 'impossible_travel' | 'new_country' | 'new_device' | 'velocity';
2
+ export type RiskSignal = 'impossible_travel' | 'new_country' | 'new_device' | 'off_hours' | 'proxy' | 'velocity';
3
3
  export type GeoPoint = {
4
4
  country?: string;
5
5
  latitude?: number;
@@ -9,6 +9,8 @@ export type RiskContext = {
9
9
  deviceId: string;
10
10
  geo?: GeoPoint;
11
11
  ipAddress?: string;
12
+ isProxy?: boolean;
13
+ localHour?: number;
12
14
  now?: number;
13
15
  userId: string;
14
16
  };
@@ -20,6 +22,16 @@ export type RiskAssessment = {
20
22
  action: RiskAction;
21
23
  reasons: RiskReason[];
22
24
  };
25
+ export type RiskWeights = Partial<Record<RiskSignal, number>>;
26
+ export type RiskThresholds = {
27
+ deny: number;
28
+ stepUp: number;
29
+ };
30
+ export type WeightedRiskAssessment = {
31
+ action: RiskAction;
32
+ reasons: RiskReason[];
33
+ score: number;
34
+ };
23
35
  export type KnownDevice = {
24
36
  deviceId: string;
25
37
  firstSeenAt: number;
@@ -0,0 +1,2 @@
1
+ import type { AuditEvent } from './types';
2
+ export declare const exportAuditCsv: (events: AuditEvent[]) => string;
@@ -2,14 +2,18 @@ import type { AuditEvent, AuditSink } from './types';
2
2
  export type AuditIntegrity = {
3
3
  hash: string;
4
4
  previousHash: string;
5
+ writerId?: string;
5
6
  };
6
7
  export type AuditChainResult = {
7
8
  brokenAt?: number;
8
9
  ok: boolean;
9
10
  };
10
- export declare const createTamperEvidentSink: ({ secret, sink }: {
11
+ export declare const createTamperEvidentSink: ({ loadWriterHead, secret, seedScanLimit, sink, writerId }: {
12
+ loadWriterHead?: (writerId: string) => Promise<string | undefined> | string | undefined;
11
13
  secret?: string;
14
+ seedScanLimit?: number;
12
15
  sink: AuditSink;
16
+ writerId?: string;
13
17
  }) => AuditSink;
14
18
  export declare const hashAuditEvent: (event: AuditEvent, previousHash: string, secret?: string) => Promise<string>;
15
19
  export declare const verifyAuditChain: (events: AuditEvent[], secret?: string) => Promise<AuditChainResult>;
@@ -15,4 +15,5 @@ export type AuditEventFilter = {
15
15
  export type AuditSink = {
16
16
  append: (event: AuditEvent) => Promise<void>;
17
17
  list?: (filter?: AuditEventFilter) => Promise<AuditEvent[]>;
18
+ prune?: (before: number) => Promise<number>;
18
19
  };
@@ -15,10 +15,17 @@ export type Subject = {
15
15
  subjectId: string;
16
16
  subjectType: string;
17
17
  };
18
+ export type ObjectQuery = {
19
+ relation: string;
20
+ resourceType: string;
21
+ subjectId: string;
22
+ subjectType: string;
23
+ };
18
24
  export declare const check: (config: FgaConfig, query: CheckQuery) => Promise<boolean>;
19
25
  export declare const createFgaEngine: (config: FgaConfig) => {
20
26
  check: (query: CheckQuery) => Promise<boolean>;
21
27
  deleteWarrant: (warrant: Warrant) => Promise<void>;
28
+ listObjects: (query: ObjectQuery) => Promise<string[]>;
22
29
  listSubjects: (query: {
23
30
  relation: string;
24
31
  resourceId: string;
@@ -27,6 +34,7 @@ export declare const createFgaEngine: (config: FgaConfig) => {
27
34
  writeWarrant: (warrant: Warrant) => Promise<void>;
28
35
  };
29
36
  export declare const deleteWarrant: (config: FgaConfig, warrant: Warrant) => Promise<void>;
37
+ export declare const listObjects: (config: FgaConfig, query: ObjectQuery) => Promise<string[]>;
30
38
  export declare const listSubjects: (config: FgaConfig, query: {
31
39
  relation: string;
32
40
  resourceId: string;
@@ -0,0 +1,2 @@
1
+ import type { FgaSchema } from './types';
2
+ export declare const parseSchema: (dsl: string) => FgaSchema;
@@ -9,6 +9,7 @@ export type Warrant = {
9
9
  export type WarrantStore = {
10
10
  deleteWarrant: (warrant: Warrant) => Promise<void>;
11
11
  listForResource: (resourceType: string, resourceId: string, relation: string) => Promise<Warrant[]>;
12
+ listResourceIds: (resourceType: string) => Promise<string[]>;
12
13
  saveWarrant: (warrant: Warrant) => Promise<void>;
13
14
  };
14
15
  export type RelationRule = {
package/dist/index.d.ts CHANGED
@@ -14712,6 +14712,7 @@ export { createInMemoryLockoutStore } from './lockout/inMemoryLockoutStore';
14712
14712
  export { createNeonLockoutStore, createPostgresLockoutStore, lockoutsTable } from './lockout/postgresLockoutStore';
14713
14713
  export { createRedisLockoutStore } from './lockout/redisLockoutStore';
14714
14714
  export type { RedisLike } from './stores/redis';
14715
+ export { exportAuditCsv } from './audit/export';
14715
14716
  export { createInMemoryAuditSink } from './audit/inMemoryAuditStore';
14716
14717
  export { auditEventsTable, createNeonAuditSink, createPostgresAuditSink } from './audit/postgresAuditStore';
14717
14718
  export * from './sso/types';
@@ -14736,10 +14737,12 @@ export type { DpopResult } from './oidc/dpop';
14736
14737
  export { createInMemoryAuthorizationCodeStore, createInMemoryOAuthClientStore, createInMemoryOidcRefreshTokenStore } from './oidc/inMemoryStores';
14737
14738
  export { createNeonAuthorizationCodeStore, createNeonOAuthClientStore, createNeonOidcRefreshTokenStore, createPostgresAuthorizationCodeStore, createPostgresOAuthClientStore, createPostgresOidcRefreshTokenStore, oauthClientsTable, oauthCodesTable, oauthRefreshTokensTable } from './oidc/postgresStores';
14738
14739
  export * from './adaptive/config';
14740
+ export * from './adaptive/fingerprint';
14739
14741
  export * from './adaptive/types';
14740
14742
  export { createInMemoryKnownDeviceStore, createInMemoryLoginHistoryStore } from './adaptive/inMemoryStores';
14741
14743
  export { createNeonKnownDeviceStore, createNeonLoginHistoryStore, createPostgresKnownDeviceStore, createPostgresLoginHistoryStore, knownDevicesTable, loginHistoryTable } from './adaptive/postgresStores';
14742
14744
  export * from './fga/config';
14745
+ export * from './fga/schema';
14743
14746
  export * from './fga/types';
14744
14747
  export { createInMemoryWarrantStore, warrantKey } from './fga/inMemoryStores';
14745
14748
  export { createNeonWarrantStore, createPostgresWarrantStore, warrantsTable } from './fga/postgresStores';
package/dist/index.js CHANGED
@@ -18710,6 +18710,7 @@ var INTEGRITY_KEY = "__integrity";
18710
18710
  var GENESIS = "";
18711
18711
  var HEX_RADIX2 = 16;
18712
18712
  var HEX_PAD = 2;
18713
+ var DEFAULT_SEED_SCAN_LIMIT = 1000;
18713
18714
  var encoder = new TextEncoder;
18714
18715
  var toHex = (buffer) => [...new Uint8Array(buffer)].map((byte) => byte.toString(HEX_RADIX2).padStart(HEX_PAD, "0")).join("");
18715
18716
  var sha256Hex = async (message) => toHex(await crypto.subtle.digest("SHA-256", encoder.encode(message)));
@@ -18740,24 +18741,45 @@ var brokenAt = (index) => {
18740
18741
  return result;
18741
18742
  };
18742
18743
  var createTamperEvidentSink = ({
18744
+ loadWriterHead,
18743
18745
  secret,
18744
- sink
18746
+ seedScanLimit = DEFAULT_SEED_SCAN_LIMIT,
18747
+ sink,
18748
+ writerId
18745
18749
  }) => {
18750
+ const chainWriterId = writerId ?? crypto.randomUUID();
18751
+ const isResuming = writerId !== undefined;
18746
18752
  let lastHash;
18753
+ let seeded = false;
18747
18754
  const seed = async () => {
18748
- if (lastHash !== undefined)
18755
+ if (seeded)
18749
18756
  return;
18750
- const [recent] = await sink.list?.({ limit: 1 }) ?? [];
18751
- lastHash = recent ? readIntegrity(recent)?.hash ?? GENESIS : GENESIS;
18757
+ seeded = true;
18758
+ if (!isResuming) {
18759
+ lastHash = GENESIS;
18760
+ return;
18761
+ }
18762
+ if (loadWriterHead) {
18763
+ lastHash = await loadWriterHead(chainWriterId) ?? GENESIS;
18764
+ return;
18765
+ }
18766
+ const recent = await sink.list?.({ limit: seedScanLimit }) ?? [];
18767
+ const head = recent.find((event) => readIntegrity(event)?.writerId === chainWriterId);
18768
+ lastHash = head ? readIntegrity(head)?.hash ?? GENESIS : GENESIS;
18752
18769
  };
18753
18770
  return {
18754
18771
  list: sink.list,
18772
+ prune: sink.prune,
18755
18773
  append: async (event) => {
18756
18774
  await seed();
18757
18775
  const previousHash = lastHash ?? GENESIS;
18758
18776
  const hash = await hashAuditEvent(event, previousHash, secret);
18759
18777
  lastHash = hash;
18760
- const integrity = { hash, previousHash };
18778
+ const integrity = {
18779
+ hash,
18780
+ previousHash,
18781
+ writerId: chainWriterId
18782
+ };
18761
18783
  await sink.append({
18762
18784
  ...event,
18763
18785
  metadata: { ...event.metadata, [INTEGRITY_KEY]: integrity }
@@ -18773,17 +18795,19 @@ var hashAuditEvent = async (event, previousHash, secret) => {
18773
18795
  return secret === undefined ? sha256Hex(message) : hmacSha256Hex(secret, message);
18774
18796
  };
18775
18797
  var verifyAuditChain = async (events, secret) => {
18776
- let previousHash = GENESIS;
18798
+ const heads = new Map;
18777
18799
  for (let index = 0;index < events.length; index += 1) {
18778
18800
  const event = events[index];
18779
18801
  if (event === undefined)
18780
18802
  return brokenAt(index);
18781
18803
  const integrity = readIntegrity(event);
18804
+ const chain = integrity?.writerId ?? GENESIS;
18805
+ const previousHash = heads.get(chain) ?? GENESIS;
18782
18806
  const expected = await hashAuditEvent(event, previousHash, secret);
18783
18807
  if (integrity === undefined || integrity.previousHash !== previousHash || integrity.hash !== expected) {
18784
18808
  return brokenAt(index);
18785
18809
  }
18786
- previousHash = integrity.hash;
18810
+ heads.set(chain, integrity.hash);
18787
18811
  }
18788
18812
  const valid = { ok: true };
18789
18813
  return valid;
@@ -19061,6 +19085,19 @@ var createRedisLockoutStore = (redis, keyPrefix = DEFAULT_PREFIX) => {
19061
19085
  }
19062
19086
  };
19063
19087
  };
19088
+ // src/audit/export.ts
19089
+ var CSV_HEADER = "at,type,userId,ip,organizationId,metadata";
19090
+ var escapeCsv = (value) => /[",\n\r]/u.test(value) ? `"${value.replace(/"/gu, '""')}"` : value;
19091
+ var toRow = (event) => [
19092
+ new Date(event.at).toISOString(),
19093
+ event.type,
19094
+ event.userId ?? "",
19095
+ event.ip ?? "",
19096
+ event.organizationId ?? "",
19097
+ event.metadata ? JSON.stringify(event.metadata) : ""
19098
+ ].map(escapeCsv).join(",");
19099
+ var exportAuditCsv = (events) => [CSV_HEADER, ...events.map(toRow)].join(`
19100
+ `);
19064
19101
  // src/audit/inMemoryAuditStore.ts
19065
19102
  var createInMemoryAuditSink = () => {
19066
19103
  const events = [];
@@ -19073,6 +19110,13 @@ var createInMemoryAuditSink = () => {
19073
19110
  const ordered = [...matched].sort((left, right) => right.at - left.at);
19074
19111
  const limited = filter?.limit === undefined ? ordered : ordered.slice(0, filter.limit);
19075
19112
  return limited.map((event) => ({ ...event }));
19113
+ },
19114
+ prune: async (before) => {
19115
+ const kept = events.filter((event) => event.at >= before);
19116
+ const removed = events.length - kept.length;
19117
+ events.length = 0;
19118
+ events.push(...kept);
19119
+ return removed;
19076
19120
  }
19077
19121
  };
19078
19122
  };
@@ -19114,6 +19158,10 @@ var createPostgresAuditSink = (db) => ({
19114
19158
  list: async (filter) => {
19115
19159
  const rows = await db.select().from(auditEventsTable).where(filter?.userId ? eq(auditEventsTable.user_id, filter.userId) : undefined).orderBy(desc(auditEventsTable.at_ms)).limit(filter?.limit ?? DEFAULT_AUDIT_LIMIT);
19116
19160
  return rows.map(toEvent);
19161
+ },
19162
+ prune: async (before) => {
19163
+ const deleted = await db.delete(auditEventsTable).where(lt(auditEventsTable.at_ms, before)).returning({ id: auditEventsTable.id });
19164
+ return deleted.length;
19117
19165
  }
19118
19166
  });
19119
19167
  // src/scim/inMemoryScimTokenStore.ts
@@ -19550,8 +19598,22 @@ var DEFAULT_RULE_ACTIONS = {
19550
19598
  impossible_travel: "deny",
19551
19599
  new_country: "step_up",
19552
19600
  new_device: "step_up",
19601
+ off_hours: "allow",
19602
+ proxy: "step_up",
19553
19603
  velocity: "deny"
19554
19604
  };
19605
+ var DEFAULT_RISK_WEIGHTS = {
19606
+ impossible_travel: 80,
19607
+ new_country: 25,
19608
+ new_device: 20,
19609
+ off_hours: 10,
19610
+ proxy: 30,
19611
+ velocity: 80
19612
+ };
19613
+ var DEFAULT_OFF_HOURS_START = 0;
19614
+ var DEFAULT_OFF_HOURS_END = 6;
19615
+ var DEFAULT_DENY_SCORE = 80;
19616
+ var DEFAULT_STEP_UP_SCORE = 40;
19555
19617
  var toRadians = (degrees) => degrees * Math.PI / DEGREES_PER_HALF_TURN;
19556
19618
  var haversineKm = (start, end) => {
19557
19619
  if (start.latitude === undefined || start.longitude === undefined || end.latitude === undefined || end.longitude === undefined) {
@@ -19565,32 +19627,44 @@ var haversineKm = (start, end) => {
19565
19627
  return HALF * EARTH_RADIUS_KM * Math.asin(Math.sqrt(factor));
19566
19628
  };
19567
19629
  var mostSevere2 = (reasons) => reasons.reduce((worst, reason) => ACTION_SEVERITY2[reason.action] > ACTION_SEVERITY2[worst] ? reason.action : worst, "allow");
19568
- var assessRisk = async (config, context) => {
19630
+ var isWithinOffHours = (hour, range) => {
19631
+ const start = range?.start ?? DEFAULT_OFF_HOURS_START;
19632
+ const end = range?.end ?? DEFAULT_OFF_HOURS_END;
19633
+ if (start <= end)
19634
+ return hour >= start && hour < end;
19635
+ return hour >= start || hour < end;
19636
+ };
19637
+ var actionForScore = (score, thresholds) => {
19638
+ const deny = "deny";
19639
+ const stepUp = "step_up";
19640
+ const allow = "allow";
19641
+ if (score >= thresholds.deny)
19642
+ return deny;
19643
+ if (score >= thresholds.stepUp)
19644
+ return stepUp;
19645
+ return allow;
19646
+ };
19647
+ var detectSignals = async (config, context) => {
19569
19648
  const {
19570
19649
  historyLimit = DEFAULT_HISTORY_LIMIT,
19571
19650
  knownDeviceStore,
19572
19651
  loginHistoryStore,
19573
19652
  maxTravelKmh = DEFAULT_MAX_TRAVEL_KMH,
19574
- rules,
19653
+ offHours,
19575
19654
  velocityMaxAttempts = DEFAULT_VELOCITY_MAX_ATTEMPTS,
19576
19655
  velocityWindowMs = DEFAULT_VELOCITY_WINDOW_MS
19577
19656
  } = config;
19578
19657
  const now = context.now ?? Date.now();
19579
- const actions = {
19580
- ...DEFAULT_RULE_ACTIONS,
19581
- ...rules
19582
- };
19583
- const reasons = [];
19658
+ const fired = [];
19584
19659
  const [device, history] = await Promise.all([
19585
19660
  knownDeviceStore.findDevice(context.userId, context.deviceId),
19586
19661
  loginHistoryStore.listRecent(context.userId, historyLimit)
19587
19662
  ]);
19588
- if (device === undefined || !device.trusted) {
19589
- reasons.push({ action: actions.new_device, signal: "new_device" });
19590
- }
19663
+ if (device === undefined || !device.trusted)
19664
+ fired.push("new_device");
19591
19665
  const country = context.geo?.country;
19592
19666
  if (country !== undefined && history.length > 0 && !history.some((attempt) => attempt.country === country)) {
19593
- reasons.push({ action: actions.new_country, signal: "new_country" });
19667
+ fired.push("new_country");
19594
19668
  }
19595
19669
  const [previous] = history;
19596
19670
  const traveledKm = previous !== undefined && context.geo !== undefined ? haversineKm({
@@ -19599,20 +19673,34 @@ var assessRisk = async (config, context) => {
19599
19673
  }, context.geo) : undefined;
19600
19674
  const hours = previous === undefined ? 0 : (now - previous.timestamp) / MILLISECONDS_IN_AN_HOUR;
19601
19675
  if (traveledKm !== undefined && hours > 0 && traveledKm / hours > maxTravelKmh) {
19602
- reasons.push({
19603
- action: actions.impossible_travel,
19604
- signal: "impossible_travel"
19605
- });
19676
+ fired.push("impossible_travel");
19606
19677
  }
19607
19678
  const recentCount = history.filter((attempt) => now - attempt.timestamp <= velocityWindowMs).length;
19608
- if (recentCount >= velocityMaxAttempts) {
19609
- reasons.push({ action: actions.velocity, signal: "velocity" });
19679
+ if (recentCount >= velocityMaxAttempts)
19680
+ fired.push("velocity");
19681
+ if (context.isProxy === true)
19682
+ fired.push("proxy");
19683
+ if (context.localHour !== undefined && isWithinOffHours(context.localHour, offHours)) {
19684
+ fired.push("off_hours");
19610
19685
  }
19686
+ return fired;
19687
+ };
19688
+ var assessRisk = async (config, context) => {
19689
+ const actions = {
19690
+ ...DEFAULT_RULE_ACTIONS,
19691
+ ...config.rules
19692
+ };
19693
+ const fired = await detectSignals(config, context);
19694
+ const reasons = fired.map((signal) => ({
19695
+ action: actions[signal],
19696
+ signal
19697
+ }));
19611
19698
  return { action: mostSevere2(reasons), reasons };
19612
19699
  };
19613
19700
  var createRiskEngine = (config) => ({
19614
19701
  assessRisk: (context) => assessRisk(config, context),
19615
19702
  recordAttempt: (context) => recordLoginAttempt(config, context),
19703
+ scoreRisk: (context, options) => scoreRisk({ ...config, ...options }, context),
19616
19704
  trustDevice: (userId, deviceId, label) => trustDevice(config, userId, deviceId, label)
19617
19705
  });
19618
19706
  var recordLoginAttempt = async (config, context) => {
@@ -19638,6 +19726,22 @@ var recordLoginAttempt = async (config, context) => {
19638
19726
  userId: context.userId
19639
19727
  });
19640
19728
  };
19729
+ var scoreRisk = async (config, context) => {
19730
+ const weights = {
19731
+ ...DEFAULT_RISK_WEIGHTS,
19732
+ ...config.weights
19733
+ };
19734
+ const defaultThresholds = {
19735
+ deny: DEFAULT_DENY_SCORE,
19736
+ stepUp: DEFAULT_STEP_UP_SCORE
19737
+ };
19738
+ const thresholds = config.thresholds ?? defaultThresholds;
19739
+ const fired = await detectSignals(config, context);
19740
+ const score = fired.reduce((sum, signal) => sum + weights[signal], 0);
19741
+ const action = actionForScore(score, thresholds);
19742
+ const reasons = fired.map((signal) => ({ action, signal }));
19743
+ return { action, reasons, score };
19744
+ };
19641
19745
  var trustDevice = async (config, userId, deviceId, label) => {
19642
19746
  const now = Date.now();
19643
19747
  const existing = await config.knownDeviceStore.findDevice(userId, deviceId);
@@ -19650,6 +19754,9 @@ var trustDevice = async (config, userId, deviceId, label) => {
19650
19754
  userId
19651
19755
  });
19652
19756
  };
19757
+ // src/adaptive/fingerprint.ts
19758
+ var canonical = (signals) => JSON.stringify(signals, (_key, value) => value === null || typeof value !== "object" || Array.isArray(value) ? value : Object.fromEntries(Object.entries(value).sort((left, right) => left[0].localeCompare(right[0]))));
19759
+ var fingerprintDevice = (signals) => hashToken(canonical(signals));
19653
19760
  // src/adaptive/inMemoryStores.ts
19654
19761
  var deviceKey = (userId, deviceId) => `${userId}:${deviceId}`;
19655
19762
  var createInMemoryKnownDeviceStore = () => {
@@ -19849,16 +19956,95 @@ var expandRule = async (config, resourceType, resourceId, relation, rule, depth,
19849
19956
  var createFgaEngine = (config) => ({
19850
19957
  check: (query) => check(config, query),
19851
19958
  deleteWarrant: (warrant) => deleteWarrant(config, warrant),
19959
+ listObjects: (query) => listObjects(config, query),
19852
19960
  listSubjects: (query) => listSubjects(config, query),
19853
19961
  writeWarrant: (warrant) => writeWarrant(config, warrant)
19854
19962
  });
19855
19963
  var deleteWarrant = (config, warrant) => config.warrantStore.deleteWarrant(warrant);
19964
+ var listObjects = async (config, query) => {
19965
+ const candidates = await config.warrantStore.listResourceIds(query.resourceType);
19966
+ const allowed = await Promise.all(candidates.map((resourceId) => check(config, {
19967
+ relation: query.relation,
19968
+ resourceId,
19969
+ resourceType: query.resourceType,
19970
+ subjectId: query.subjectId,
19971
+ subjectType: query.subjectType
19972
+ })));
19973
+ return candidates.filter((_resourceId, index) => allowed[index] === true);
19974
+ };
19856
19975
  var listSubjects = async (config, query) => {
19857
19976
  const found = new Map;
19858
19977
  await expand(config, query.resourceType, query.resourceId, query.relation, config.maxDepth ?? DEFAULT_MAX_DEPTH, found, new Set);
19859
19978
  return [...found.values()];
19860
19979
  };
19861
19980
  var writeWarrant = (config, warrant) => config.warrantStore.saveWarrant(warrant);
19981
+ // src/fga/schema.ts
19982
+ var TYPE_PATTERN = /^type\s+(\w+)$/u;
19983
+ var DEFINE_PATTERN = /^define\s+(\w+)\s*:\s*(.+)$/u;
19984
+ var FROM_PATTERN = /^(\w+)\s+from\s+(\w+)$/u;
19985
+ var OR_SEPARATOR = /\s+or\s+/u;
19986
+ var parseTerm = (term) => {
19987
+ const trimmed = term.trim();
19988
+ if (trimmed.startsWith("[")) {
19989
+ const rule2 = { kind: "self" };
19990
+ return rule2;
19991
+ }
19992
+ const fromMatch = FROM_PATTERN.exec(trimmed);
19993
+ if (fromMatch) {
19994
+ const [, relation, viaRelation] = fromMatch;
19995
+ const rule2 = {
19996
+ kind: "tupleToUserset",
19997
+ relation: relation ?? "",
19998
+ viaRelation: viaRelation ?? ""
19999
+ };
20000
+ return rule2;
20001
+ }
20002
+ const rule = { kind: "computedUserset", relation: trimmed };
20003
+ return rule;
20004
+ };
20005
+ var parseExpression = (expression) => {
20006
+ const terms = expression.split(OR_SEPARATOR).map(parseTerm);
20007
+ const [first] = terms;
20008
+ if (terms.length === 1 && first)
20009
+ return first;
20010
+ const rule = { kind: "union", rules: terms };
20011
+ return rule;
20012
+ };
20013
+ var applyType = (schema, line2) => {
20014
+ const match = TYPE_PATTERN.exec(line2);
20015
+ if (!match)
20016
+ return;
20017
+ const [, name] = match;
20018
+ if (name)
20019
+ schema[name] = {};
20020
+ return name;
20021
+ };
20022
+ var applyDefine = (target, line2) => {
20023
+ if (!target)
20024
+ return;
20025
+ const match = DEFINE_PATTERN.exec(line2);
20026
+ if (!match)
20027
+ return;
20028
+ const [, relation, expression] = match;
20029
+ if (relation && expression)
20030
+ target[relation] = parseExpression(expression);
20031
+ };
20032
+ var parseSchema = (dsl) => {
20033
+ const schema = {};
20034
+ let currentType;
20035
+ for (const rawLine of dsl.split(`
20036
+ `)) {
20037
+ const line2 = rawLine.trim();
20038
+ if (line2 === "" || line2.startsWith("#") || line2 === "relations")
20039
+ continue;
20040
+ const typeName = applyType(schema, line2);
20041
+ currentType = typeName ?? currentType;
20042
+ if (typeName !== undefined)
20043
+ continue;
20044
+ applyDefine(currentType ? schema[currentType] : undefined, line2);
20045
+ }
20046
+ return schema;
20047
+ };
19862
20048
  // src/fga/inMemoryStores.ts
19863
20049
  var createInMemoryWarrantStore = () => {
19864
20050
  const warrants = new Map;
@@ -19867,6 +20053,9 @@ var createInMemoryWarrantStore = () => {
19867
20053
  warrants.delete(warrantKey(warrant));
19868
20054
  },
19869
20055
  listForResource: async (resourceType, resourceId, relation) => [...warrants.values()].filter((warrant) => warrant.resourceType === resourceType && warrant.resourceId === resourceId && warrant.relation === relation),
20056
+ listResourceIds: async (resourceType) => [
20057
+ ...new Set([...warrants.values()].filter((warrant) => warrant.resourceType === resourceType).map((warrant) => warrant.resourceId))
20058
+ ],
19870
20059
  saveWarrant: async (warrant) => {
19871
20060
  warrants.set(warrantKey(warrant), { ...warrant });
19872
20061
  }
@@ -19901,6 +20090,10 @@ var createPostgresWarrantStore = (db) => ({
19901
20090
  const rows = await db.select().from(warrantsTable).where(and(eq(warrantsTable.resource_type, resourceType), eq(warrantsTable.resource_id, resourceId), eq(warrantsTable.relation, relation)));
19902
20091
  return rows.map(toWarrant);
19903
20092
  },
20093
+ listResourceIds: async (resourceType) => {
20094
+ const rows = await db.selectDistinct({ resourceId: warrantsTable.resource_id }).from(warrantsTable).where(eq(warrantsTable.resource_type, resourceType));
20095
+ return rows.map((row) => row.resourceId);
20096
+ },
19904
20097
  saveWarrant: async (warrant) => {
19905
20098
  await db.insert(warrantsTable).values({
19906
20099
  id: warrantKey(warrant),
@@ -20728,6 +20921,7 @@ export {
20728
20921
  sessionStore,
20729
20922
  sessionRoutes,
20730
20923
  sessionCleanup,
20924
+ scoreRisk,
20731
20925
  scopeRequiredProviderOptions,
20732
20926
  scimTokensTable,
20733
20927
  scimRoutes,
@@ -20758,6 +20952,7 @@ export {
20758
20952
  pkceProviderOptions,
20759
20953
  passwordlessTokensTable,
20760
20954
  passwordlessRoutes,
20955
+ parseSchema,
20761
20956
  organizationsTable,
20762
20957
  organizationRoutes,
20763
20958
  organizationMembershipsTable,
@@ -20779,6 +20974,7 @@ export {
20779
20974
  listUserOrganizations,
20780
20975
  listSubjects,
20781
20976
  listRingSessions,
20977
+ listObjects,
20782
20978
  knownDevicesTable,
20783
20979
  jwkThumbprint,
20784
20980
  issueTokenSet,
@@ -20812,7 +21008,9 @@ export {
20812
21008
  generateSecureToken,
20813
21009
  generateEncryptionKey,
20814
21010
  generateBackupCodes,
21011
+ fingerprintDevice,
20815
21012
  extractPropFromIdentity,
21013
+ exportAuditCsv,
20816
21014
  exchangeToken,
20817
21015
  exchangeClientCredentials,
20818
21016
  evaluatePassword,
@@ -20976,5 +21174,5 @@ export {
20976
21174
  AuthIdentityConflictError
20977
21175
  };
20978
21176
 
20979
- //# debugId=580D3E8ACE9EF4E864756E2164756E21
21177
+ //# debugId=22C6F88FE2A9D7F764756E2164756E21
20980
21178
  //# sourceMappingURL=index.js.map