@apoa/core 0.2.0 → 0.2.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.
package/dist/index.d.cts CHANGED
@@ -235,6 +235,35 @@ interface APOAClientOptions {
235
235
  keyResolver?: KeyResolver;
236
236
  defaultSigningOptions?: Partial<SigningOptions>;
237
237
  }
238
+ /** Options for the application-facing APOA facade. */
239
+ interface APOAOptions extends Omit<APOAClientOptions, 'defaultSigningOptions'> {
240
+ privateKey?: CryptoKey;
241
+ algorithm?: 'EdDSA' | 'ES256';
242
+ kid?: string;
243
+ }
244
+ /** Duration string for convenience expiry fields, e.g. `15m`, `2h`, `30d`. */
245
+ type DurationString = `${number}${'s' | 'm' | 'h' | 'd'}`;
246
+ /** One-service grant input accepted by the application-facing facade. */
247
+ interface SimpleGrantInput {
248
+ principal: string | Principal;
249
+ agent: string | Agent;
250
+ service?: string;
251
+ scopes?: string[];
252
+ services?: ServiceAuthorization[];
253
+ constraints?: ConstraintMap;
254
+ rules?: Rule[];
255
+ expires?: Date | string;
256
+ expiresIn?: DurationString;
257
+ revocable?: boolean;
258
+ delegatable?: boolean;
259
+ maxDelegationDepth?: number;
260
+ metadata?: TokenMetadata;
261
+ accessMode?: AccessMode;
262
+ browserConfig?: BrowserSessionConfig;
263
+ apiConfig?: APIAccessConfig;
264
+ agentProvider?: AgentProvider;
265
+ legal?: LegalFramework;
266
+ }
238
267
  /** The configured APOA client. */
239
268
  interface APOAClient {
240
269
  createToken(definition: APOADefinition, options?: SigningOptions): Promise<APOAToken>;
@@ -520,6 +549,24 @@ declare function delegate(parentToken: APOAToken, childDef: DelegationDefinition
520
549
  */
521
550
  declare function verifyChain(chain: DelegationChain, store?: RevocationStore): Promise<ChainVerificationResult>;
522
551
 
552
+ type DefinitionLike = {
553
+ parentToken?: unknown;
554
+ delegationChain?: unknown;
555
+ };
556
+ type TokenLike = {
557
+ parentToken?: unknown;
558
+ definition?: DefinitionLike;
559
+ };
560
+ /**
561
+ * Return ancestor token IDs referenced by a token-like object.
562
+ *
563
+ * Canonical SDK tokens use `parentToken` for the direct parent. Some transport
564
+ * adapters also carry `definition.delegationChain` snapshots or message
565
+ * metadata with ancestor IDs. This helper normalizes those forms so revocation
566
+ * checks can consistently include every known ancestor.
567
+ */
568
+ declare function getDelegationAncestorIds(input: APOAToken | TokenLike | DefinitionLike): string[];
569
+
523
570
  /** A JSON Web Key as defined by RFC 7517. */
524
571
  interface JWK {
525
572
  kty: string;
@@ -578,4 +625,25 @@ declare function createJWKSResolver(url: string, options?: JWKSResolverOptions):
578
625
  */
579
626
  declare function createClient(options?: APOAClientOptions): APOAClient;
580
627
 
581
- export { type APIAccessConfig, type APOAClient, type APOAClientOptions, type APOADefinition, APOAError, type APOAToken, type AccessMode, type Agent, type AgentProvider, AttenuationViolationError, type AuditDetailValue, type AuditEntry, type AuditQueryOptions, type AuditStore, type AuthorizationResult, type AuthorizeOptions, type BrowserSessionConfig, ChainVerificationError, type ChainVerificationResult, type ConstraintMap, type ConstraintValue, DefinitionValidationError, type DelegationChain, type DelegationDefinition, type JWK, type JWKS, type JWKSResolverOptions, type KeyResolver, type LegalFramework, MemoryAuditStore, MemoryRevocationStore, MetadataValidationError, type MetadataValue, type OnRuleViolation, type Principal, type PublicKeyToJWKOptions, RevocationError, type RevocationOptions, type RevocationRecord, type RevocationStore, type Rule, RuleEnforcementError, type RuleViolation, type ScopeCheckResult, ScopeViolationError, type ServiceAuthorization, type SigningOptions, TokenExpiredError, type TokenMetadata, type ValidationOptions, type ValidationResult, authorize, buildJWKS, cascadeRevoke, checkConstraint, checkScope, createClient, createJWKSResolver, createToken, decodeHeader, delegate, generateKeyPair, getAuditTrail, getAuditTrailByService, isBeforeNotBefore, isExpired, isRevoked, logAction, matchScope, parseDefinition, parseScope, publicKeyToJWK, revoke, sign, signToken, validateToken, verify, verifyAttenuation, verifyChain, verifySignature };
628
+ /**
629
+ * Application-facing APOA facade.
630
+ *
631
+ * This keeps the protocol-level APIs intact while giving app developers a
632
+ * smaller first path: configure once, then use namespaced resources.
633
+ */
634
+ declare class APOA {
635
+ private readonly client;
636
+ readonly tokens: {
637
+ create: (definition: APOADefinition, options?: SigningOptions) => Promise<APOAToken>;
638
+ createGrant: (input: SimpleGrantInput, options?: SigningOptions) => Promise<APOAToken>;
639
+ validate: (token: string | APOAToken, options?: Omit<ValidationOptions, 'revocationStore'>) => Promise<ValidationResult>;
640
+ parse: (input: string, format?: 'yaml' | 'json') => APOADefinition;
641
+ };
642
+ readonly authorizations: {
643
+ check: (token: APOAToken, service: string, action: string, options?: Omit<AuthorizeOptions, 'revocationStore' | 'auditStore'>) => Promise<AuthorizationResult>;
644
+ };
645
+ constructor(options?: APOAOptions);
646
+ generateKeyPair(algorithm?: 'EdDSA' | 'ES256'): Promise<CryptoKeyPair>;
647
+ }
648
+
649
+ export { type APIAccessConfig, APOA, type APOAClient, type APOAClientOptions, type APOADefinition, APOAError, type APOAOptions, type APOAToken, type AccessMode, type Agent, type AgentProvider, AttenuationViolationError, type AuditDetailValue, type AuditEntry, type AuditQueryOptions, type AuditStore, type AuthorizationResult, type AuthorizeOptions, type BrowserSessionConfig, ChainVerificationError, type ChainVerificationResult, type ConstraintMap, type ConstraintValue, DefinitionValidationError, type DelegationChain, type DelegationDefinition, type DurationString, type JWK, type JWKS, type JWKSResolverOptions, type KeyResolver, type LegalFramework, MemoryAuditStore, MemoryRevocationStore, MetadataValidationError, type MetadataValue, type OnRuleViolation, type Principal, type PublicKeyToJWKOptions, RevocationError, type RevocationOptions, type RevocationRecord, type RevocationStore, type Rule, RuleEnforcementError, type RuleViolation, type ScopeCheckResult, ScopeViolationError, type ServiceAuthorization, type SigningOptions, type SimpleGrantInput, TokenExpiredError, type TokenMetadata, type ValidationOptions, type ValidationResult, authorize, buildJWKS, cascadeRevoke, checkConstraint, checkScope, createClient, createJWKSResolver, createToken, decodeHeader, delegate, generateKeyPair, getAuditTrail, getAuditTrailByService, getDelegationAncestorIds, isBeforeNotBefore, isExpired, isRevoked, logAction, matchScope, parseDefinition, parseScope, publicKeyToJWK, revoke, sign, signToken, validateToken, verify, verifyAttenuation, verifyChain, verifySignature };
package/dist/index.d.ts CHANGED
@@ -235,6 +235,35 @@ interface APOAClientOptions {
235
235
  keyResolver?: KeyResolver;
236
236
  defaultSigningOptions?: Partial<SigningOptions>;
237
237
  }
238
+ /** Options for the application-facing APOA facade. */
239
+ interface APOAOptions extends Omit<APOAClientOptions, 'defaultSigningOptions'> {
240
+ privateKey?: CryptoKey;
241
+ algorithm?: 'EdDSA' | 'ES256';
242
+ kid?: string;
243
+ }
244
+ /** Duration string for convenience expiry fields, e.g. `15m`, `2h`, `30d`. */
245
+ type DurationString = `${number}${'s' | 'm' | 'h' | 'd'}`;
246
+ /** One-service grant input accepted by the application-facing facade. */
247
+ interface SimpleGrantInput {
248
+ principal: string | Principal;
249
+ agent: string | Agent;
250
+ service?: string;
251
+ scopes?: string[];
252
+ services?: ServiceAuthorization[];
253
+ constraints?: ConstraintMap;
254
+ rules?: Rule[];
255
+ expires?: Date | string;
256
+ expiresIn?: DurationString;
257
+ revocable?: boolean;
258
+ delegatable?: boolean;
259
+ maxDelegationDepth?: number;
260
+ metadata?: TokenMetadata;
261
+ accessMode?: AccessMode;
262
+ browserConfig?: BrowserSessionConfig;
263
+ apiConfig?: APIAccessConfig;
264
+ agentProvider?: AgentProvider;
265
+ legal?: LegalFramework;
266
+ }
238
267
  /** The configured APOA client. */
239
268
  interface APOAClient {
240
269
  createToken(definition: APOADefinition, options?: SigningOptions): Promise<APOAToken>;
@@ -520,6 +549,24 @@ declare function delegate(parentToken: APOAToken, childDef: DelegationDefinition
520
549
  */
521
550
  declare function verifyChain(chain: DelegationChain, store?: RevocationStore): Promise<ChainVerificationResult>;
522
551
 
552
+ type DefinitionLike = {
553
+ parentToken?: unknown;
554
+ delegationChain?: unknown;
555
+ };
556
+ type TokenLike = {
557
+ parentToken?: unknown;
558
+ definition?: DefinitionLike;
559
+ };
560
+ /**
561
+ * Return ancestor token IDs referenced by a token-like object.
562
+ *
563
+ * Canonical SDK tokens use `parentToken` for the direct parent. Some transport
564
+ * adapters also carry `definition.delegationChain` snapshots or message
565
+ * metadata with ancestor IDs. This helper normalizes those forms so revocation
566
+ * checks can consistently include every known ancestor.
567
+ */
568
+ declare function getDelegationAncestorIds(input: APOAToken | TokenLike | DefinitionLike): string[];
569
+
523
570
  /** A JSON Web Key as defined by RFC 7517. */
524
571
  interface JWK {
525
572
  kty: string;
@@ -578,4 +625,25 @@ declare function createJWKSResolver(url: string, options?: JWKSResolverOptions):
578
625
  */
579
626
  declare function createClient(options?: APOAClientOptions): APOAClient;
580
627
 
581
- export { type APIAccessConfig, type APOAClient, type APOAClientOptions, type APOADefinition, APOAError, type APOAToken, type AccessMode, type Agent, type AgentProvider, AttenuationViolationError, type AuditDetailValue, type AuditEntry, type AuditQueryOptions, type AuditStore, type AuthorizationResult, type AuthorizeOptions, type BrowserSessionConfig, ChainVerificationError, type ChainVerificationResult, type ConstraintMap, type ConstraintValue, DefinitionValidationError, type DelegationChain, type DelegationDefinition, type JWK, type JWKS, type JWKSResolverOptions, type KeyResolver, type LegalFramework, MemoryAuditStore, MemoryRevocationStore, MetadataValidationError, type MetadataValue, type OnRuleViolation, type Principal, type PublicKeyToJWKOptions, RevocationError, type RevocationOptions, type RevocationRecord, type RevocationStore, type Rule, RuleEnforcementError, type RuleViolation, type ScopeCheckResult, ScopeViolationError, type ServiceAuthorization, type SigningOptions, TokenExpiredError, type TokenMetadata, type ValidationOptions, type ValidationResult, authorize, buildJWKS, cascadeRevoke, checkConstraint, checkScope, createClient, createJWKSResolver, createToken, decodeHeader, delegate, generateKeyPair, getAuditTrail, getAuditTrailByService, isBeforeNotBefore, isExpired, isRevoked, logAction, matchScope, parseDefinition, parseScope, publicKeyToJWK, revoke, sign, signToken, validateToken, verify, verifyAttenuation, verifyChain, verifySignature };
628
+ /**
629
+ * Application-facing APOA facade.
630
+ *
631
+ * This keeps the protocol-level APIs intact while giving app developers a
632
+ * smaller first path: configure once, then use namespaced resources.
633
+ */
634
+ declare class APOA {
635
+ private readonly client;
636
+ readonly tokens: {
637
+ create: (definition: APOADefinition, options?: SigningOptions) => Promise<APOAToken>;
638
+ createGrant: (input: SimpleGrantInput, options?: SigningOptions) => Promise<APOAToken>;
639
+ validate: (token: string | APOAToken, options?: Omit<ValidationOptions, 'revocationStore'>) => Promise<ValidationResult>;
640
+ parse: (input: string, format?: 'yaml' | 'json') => APOADefinition;
641
+ };
642
+ readonly authorizations: {
643
+ check: (token: APOAToken, service: string, action: string, options?: Omit<AuthorizeOptions, 'revocationStore' | 'auditStore'>) => Promise<AuthorizationResult>;
644
+ };
645
+ constructor(options?: APOAOptions);
646
+ generateKeyPair(algorithm?: 'EdDSA' | 'ES256'): Promise<CryptoKeyPair>;
647
+ }
648
+
649
+ export { type APIAccessConfig, APOA, type APOAClient, type APOAClientOptions, type APOADefinition, APOAError, type APOAOptions, type APOAToken, type AccessMode, type Agent, type AgentProvider, AttenuationViolationError, type AuditDetailValue, type AuditEntry, type AuditQueryOptions, type AuditStore, type AuthorizationResult, type AuthorizeOptions, type BrowserSessionConfig, ChainVerificationError, type ChainVerificationResult, type ConstraintMap, type ConstraintValue, DefinitionValidationError, type DelegationChain, type DelegationDefinition, type DurationString, type JWK, type JWKS, type JWKSResolverOptions, type KeyResolver, type LegalFramework, MemoryAuditStore, MemoryRevocationStore, MetadataValidationError, type MetadataValue, type OnRuleViolation, type Principal, type PublicKeyToJWKOptions, RevocationError, type RevocationOptions, type RevocationRecord, type RevocationStore, type Rule, RuleEnforcementError, type RuleViolation, type ScopeCheckResult, ScopeViolationError, type ServiceAuthorization, type SigningOptions, type SimpleGrantInput, TokenExpiredError, type TokenMetadata, type ValidationOptions, type ValidationResult, authorize, buildJWKS, cascadeRevoke, checkConstraint, checkScope, createClient, createJWKSResolver, createToken, decodeHeader, delegate, generateKeyPair, getAuditTrail, getAuditTrailByService, getDelegationAncestorIds, isBeforeNotBefore, isExpired, isRevoked, logAction, matchScope, parseDefinition, parseScope, publicKeyToJWK, revoke, sign, signToken, validateToken, verify, verifyAttenuation, verifyChain, verifySignature };
package/dist/index.js CHANGED
@@ -1137,6 +1137,37 @@ function checkAttenuation(parent, child, index, errors) {
1137
1137
  }
1138
1138
  }
1139
1139
 
1140
+ // src/delegation/ancestors.ts
1141
+ function getDelegationAncestorIds(input) {
1142
+ const ids = [];
1143
+ const seen = /* @__PURE__ */ new Set();
1144
+ const push = (value) => {
1145
+ if (typeof value !== "string" || value.length === 0 || seen.has(value)) {
1146
+ return;
1147
+ }
1148
+ seen.add(value);
1149
+ ids.push(value);
1150
+ };
1151
+ const token = input;
1152
+ const definition = hasDefinition(input) ? token.definition : input;
1153
+ push(token.parentToken);
1154
+ push(definition?.parentToken);
1155
+ const chain = definition?.delegationChain;
1156
+ if (Array.isArray(chain)) {
1157
+ for (const link of chain) {
1158
+ if (typeof link === "string") {
1159
+ push(link);
1160
+ } else if (link && typeof link === "object") {
1161
+ push(link.parentTokenId);
1162
+ }
1163
+ }
1164
+ }
1165
+ return ids;
1166
+ }
1167
+ function hasDefinition(input) {
1168
+ return Boolean(input && typeof input === "object" && "definition" in input);
1169
+ }
1170
+
1140
1171
  // src/jwks/index.ts
1141
1172
  import * as jose3 from "jose";
1142
1173
  async function publicKeyToJWK(publicKey, options) {
@@ -1221,7 +1252,9 @@ function createClient(options) {
1221
1252
  const defaultSigningOptions = options?.defaultSigningOptions;
1222
1253
  function mergeSigningOptions(opts) {
1223
1254
  if (!opts && !defaultSigningOptions?.privateKey) {
1224
- throw new Error("No signing options provided and no defaultSigningOptions.privateKey configured");
1255
+ throw new Error(
1256
+ "APOA needs a private key to create tokens. Pass `privateKey` to `new APOA({ privateKey })`, configure `createClient({ defaultSigningOptions: { privateKey } })`, or pass signing options to `createToken(...)`."
1257
+ );
1225
1258
  }
1226
1259
  return {
1227
1260
  ...defaultSigningOptions,
@@ -1282,7 +1315,147 @@ function createClient(options) {
1282
1315
  }
1283
1316
  };
1284
1317
  }
1318
+
1319
+ // src/apoa.ts
1320
+ var APOA = class {
1321
+ client;
1322
+ tokens;
1323
+ authorizations;
1324
+ constructor(options = {}) {
1325
+ const { privateKey, algorithm, kid, ...clientOptions } = options;
1326
+ this.client = createClient({
1327
+ ...clientOptions,
1328
+ defaultSigningOptions: privateKey ? { privateKey, algorithm, kid } : void 0
1329
+ });
1330
+ this.tokens = {
1331
+ create: (definition, signingOptions) => this.client.createToken(definition, signingOptions),
1332
+ createGrant: async (input, signingOptions) => this.client.createToken(normalizeGrantInput(input), signingOptions),
1333
+ validate: (token, validationOptions) => this.client.validateToken(token, validationOptions),
1334
+ parse: (input, format) => this.client.parseDefinition(input, format)
1335
+ };
1336
+ this.authorizations = {
1337
+ check: (token, service, action, authorizeOptions) => this.client.authorize(token, service, action, authorizeOptions)
1338
+ };
1339
+ }
1340
+ async generateKeyPair(algorithm) {
1341
+ return this.client.generateKeyPair(algorithm);
1342
+ }
1343
+ };
1344
+ function normalizeGrantInput(input) {
1345
+ const errors = [];
1346
+ if (!input || typeof input !== "object") {
1347
+ throw invalidGrantInput(["input must be an object"]);
1348
+ }
1349
+ const principal = normalizePrincipal(input.principal, errors);
1350
+ const agent = normalizeAgent(input.agent, errors);
1351
+ const services = normalizeServices(input, errors);
1352
+ const expires = normalizeExpires(input, errors);
1353
+ if (errors.length > 0 || !principal || !agent || services.length === 0 || !expires) {
1354
+ throw invalidGrantInput(errors);
1355
+ }
1356
+ return {
1357
+ principal,
1358
+ agent,
1359
+ services,
1360
+ expires,
1361
+ ...input.rules ? { rules: input.rules } : {},
1362
+ ...input.revocable !== void 0 ? { revocable: input.revocable } : {},
1363
+ ...input.delegatable !== void 0 ? { delegatable: input.delegatable } : {},
1364
+ ...input.maxDelegationDepth !== void 0 ? { maxDelegationDepth: input.maxDelegationDepth } : {},
1365
+ ...input.metadata ? { metadata: input.metadata } : {},
1366
+ ...input.agentProvider ? { agentProvider: input.agentProvider } : {},
1367
+ ...input.legal ? { legal: input.legal } : {}
1368
+ };
1369
+ }
1370
+ function normalizePrincipal(principal, errors) {
1371
+ if (typeof principal === "string" && principal.trim()) {
1372
+ return { id: principal.trim() };
1373
+ }
1374
+ if (principal && typeof principal === "object" && principal.id) {
1375
+ return principal;
1376
+ }
1377
+ errors.push("principal is required; pass a DID string or { id }");
1378
+ return void 0;
1379
+ }
1380
+ function normalizeAgent(agent, errors) {
1381
+ if (typeof agent === "string" && agent.trim()) {
1382
+ return { id: agent.trim() };
1383
+ }
1384
+ if (agent && typeof agent === "object" && agent.id) {
1385
+ return agent;
1386
+ }
1387
+ errors.push("agent is required; pass a DID string or { id }");
1388
+ return void 0;
1389
+ }
1390
+ function normalizeServices(input, errors) {
1391
+ if (input.services) {
1392
+ if (!Array.isArray(input.services) || input.services.length === 0) {
1393
+ errors.push("services must be a non-empty array when provided");
1394
+ return [];
1395
+ }
1396
+ return input.services;
1397
+ }
1398
+ if (!input.service) {
1399
+ errors.push("service is required unless services is provided");
1400
+ return [];
1401
+ }
1402
+ if (!input.scopes || !Array.isArray(input.scopes) || input.scopes.length === 0) {
1403
+ errors.push("scopes must be a non-empty array unless services is provided");
1404
+ return [];
1405
+ }
1406
+ return [{
1407
+ service: input.service,
1408
+ scopes: input.scopes,
1409
+ ...input.constraints ? { constraints: input.constraints } : {},
1410
+ ...input.accessMode ? { accessMode: input.accessMode } : {},
1411
+ ...input.browserConfig ? { browserConfig: input.browserConfig } : {},
1412
+ ...input.apiConfig ? { apiConfig: input.apiConfig } : {}
1413
+ }];
1414
+ }
1415
+ function normalizeExpires(input, errors) {
1416
+ if (input.expires && input.expiresIn) {
1417
+ errors.push("pass either expires or expiresIn, not both");
1418
+ return void 0;
1419
+ }
1420
+ if (input.expires) {
1421
+ return input.expires;
1422
+ }
1423
+ if (input.expiresIn) {
1424
+ return parseDurationFromNow(input.expiresIn);
1425
+ }
1426
+ errors.push("expires or expiresIn is required");
1427
+ return void 0;
1428
+ }
1429
+ function parseDurationFromNow(duration) {
1430
+ const match = /^(\d+)([smhd])$/.exec(duration);
1431
+ if (!match) {
1432
+ throw invalidGrantInput([
1433
+ `expiresIn must use a clear duration like '15m', '2h', or '30d'`
1434
+ ]);
1435
+ }
1436
+ const amount = Number(match[1]);
1437
+ if (!Number.isSafeInteger(amount) || amount <= 0) {
1438
+ throw invalidGrantInput(["expiresIn duration must be a positive integer"]);
1439
+ }
1440
+ const unitMs = {
1441
+ s: 1e3,
1442
+ m: 60 * 1e3,
1443
+ h: 60 * 60 * 1e3,
1444
+ d: 24 * 60 * 60 * 1e3
1445
+ };
1446
+ return new Date(Date.now() + amount * unitMs[match[2]]);
1447
+ }
1448
+ function invalidGrantInput(errors) {
1449
+ return new Error(
1450
+ [
1451
+ "Invalid APOA grant input.",
1452
+ ...errors.map((error) => `- ${error}`),
1453
+ "Minimal shape: { principal, agent, service, scopes, expiresIn }"
1454
+ ].join("\n")
1455
+ );
1456
+ }
1285
1457
  export {
1458
+ APOA,
1286
1459
  APOAError,
1287
1460
  AttenuationViolationError,
1288
1461
  ChainVerificationError,
@@ -1307,6 +1480,7 @@ export {
1307
1480
  generateKeyPair2 as generateKeyPair,
1308
1481
  getAuditTrail,
1309
1482
  getAuditTrailByService,
1483
+ getDelegationAncestorIds,
1310
1484
  isBeforeNotBefore,
1311
1485
  isExpired,
1312
1486
  isRevoked,