@apoa/core 0.2.1 → 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/README.md +34 -23
- package/dist/index.cjs +144 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +143 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,35 +11,35 @@ npm install @apoa/core
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
14
|
+
import { APOA, generateKeyPair } from '@apoa/core';
|
|
15
15
|
|
|
16
|
-
// Generate keys and create a client
|
|
17
16
|
const keys = await generateKeyPair();
|
|
18
|
-
const
|
|
17
|
+
const apoa = new APOA({ privateKey: keys.privateKey });
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
principal: { id: "did:apoa:you" },
|
|
19
|
+
const token = await apoa.tokens.createGrant({
|
|
20
|
+
principal: "did:apoa:you",
|
|
23
21
|
agent: { id: "did:apoa:your-agent", name: "HomeBot Pro" },
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
accessMode: "browser",
|
|
29
|
-
browserConfig: {
|
|
30
|
-
allowedUrls: ["https://portal.nationwidemortgage.com/*"],
|
|
31
|
-
credentialVaultRef: "1password://vault/mortgage-portal",
|
|
32
|
-
},
|
|
33
|
-
}],
|
|
34
|
-
rules: [{ id: "no-signing", description: "Never sign anything", enforcement: "hard" }],
|
|
35
|
-
expires: "2026-09-01",
|
|
22
|
+
service: "nationwidemortgage.com",
|
|
23
|
+
scopes: ["rate_lock:read", "documents:read"],
|
|
24
|
+
constraints: { signing: false },
|
|
25
|
+
expiresIn: "30d",
|
|
36
26
|
});
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
const valid = await apoa.tokens.validate(token.raw, { publicKey: keys.publicKey });
|
|
29
|
+
console.log(valid.valid); // true
|
|
30
|
+
|
|
31
|
+
const result = await apoa.authorizations.check(
|
|
32
|
+
token,
|
|
33
|
+
"nationwidemortgage.com",
|
|
34
|
+
"rate_lock:read"
|
|
35
|
+
);
|
|
40
36
|
// { authorized: true, checks: { revoked: false, scopeAllowed: true, ... } }
|
|
41
37
|
|
|
42
|
-
const denied = await
|
|
38
|
+
const denied = await apoa.authorizations.check(
|
|
39
|
+
token,
|
|
40
|
+
"nationwidemortgage.com",
|
|
41
|
+
"documents:sign"
|
|
42
|
+
);
|
|
43
43
|
// { authorized: false, reason: "scope 'documents:sign' not in authorized scopes" }
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -58,7 +58,18 @@ const denied = await client.authorize(token, "nationwidemortgage.com", "document
|
|
|
58
58
|
## Two Usage Styles
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
61
|
-
// Style 1:
|
|
61
|
+
// Style 1: Application facade (recommended for apps)
|
|
62
|
+
const apoa = new APOA({ privateKey: keys.privateKey });
|
|
63
|
+
const token = await apoa.tokens.createGrant({
|
|
64
|
+
principal: "did:apoa:you",
|
|
65
|
+
agent: "did:apoa:agent",
|
|
66
|
+
service: "service.com",
|
|
67
|
+
scopes: ["action:read"],
|
|
68
|
+
expiresIn: "30d",
|
|
69
|
+
});
|
|
70
|
+
await apoa.authorizations.check(token, "service.com", "action:read");
|
|
71
|
+
|
|
72
|
+
// Style 2: Protocol client
|
|
62
73
|
const client = createClient({
|
|
63
74
|
revocationStore: new MemoryRevocationStore(),
|
|
64
75
|
auditStore: new MemoryAuditStore(),
|
|
@@ -66,7 +77,7 @@ const client = createClient({
|
|
|
66
77
|
});
|
|
67
78
|
await client.authorize(token, "service.com", "action:read");
|
|
68
79
|
|
|
69
|
-
// Style
|
|
80
|
+
// Style 3: Standalone imports (for scripts, tests, and adapters)
|
|
70
81
|
import { checkScope, authorize, createToken } from '@apoa/core';
|
|
71
82
|
checkScope(token, "service.com", "action:read");
|
|
72
83
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
APOA: () => APOA,
|
|
33
34
|
APOAError: () => APOAError,
|
|
34
35
|
AttenuationViolationError: () => AttenuationViolationError,
|
|
35
36
|
ChainVerificationError: () => ChainVerificationError,
|
|
@@ -1328,7 +1329,9 @@ function createClient(options) {
|
|
|
1328
1329
|
const defaultSigningOptions = options?.defaultSigningOptions;
|
|
1329
1330
|
function mergeSigningOptions(opts) {
|
|
1330
1331
|
if (!opts && !defaultSigningOptions?.privateKey) {
|
|
1331
|
-
throw new Error(
|
|
1332
|
+
throw new Error(
|
|
1333
|
+
"APOA needs a private key to create tokens. Pass `privateKey` to `new APOA({ privateKey })`, configure `createClient({ defaultSigningOptions: { privateKey } })`, or pass signing options to `createToken(...)`."
|
|
1334
|
+
);
|
|
1332
1335
|
}
|
|
1333
1336
|
return {
|
|
1334
1337
|
...defaultSigningOptions,
|
|
@@ -1389,8 +1392,148 @@ function createClient(options) {
|
|
|
1389
1392
|
}
|
|
1390
1393
|
};
|
|
1391
1394
|
}
|
|
1395
|
+
|
|
1396
|
+
// src/apoa.ts
|
|
1397
|
+
var APOA = class {
|
|
1398
|
+
client;
|
|
1399
|
+
tokens;
|
|
1400
|
+
authorizations;
|
|
1401
|
+
constructor(options = {}) {
|
|
1402
|
+
const { privateKey, algorithm, kid, ...clientOptions } = options;
|
|
1403
|
+
this.client = createClient({
|
|
1404
|
+
...clientOptions,
|
|
1405
|
+
defaultSigningOptions: privateKey ? { privateKey, algorithm, kid } : void 0
|
|
1406
|
+
});
|
|
1407
|
+
this.tokens = {
|
|
1408
|
+
create: (definition, signingOptions) => this.client.createToken(definition, signingOptions),
|
|
1409
|
+
createGrant: async (input, signingOptions) => this.client.createToken(normalizeGrantInput(input), signingOptions),
|
|
1410
|
+
validate: (token, validationOptions) => this.client.validateToken(token, validationOptions),
|
|
1411
|
+
parse: (input, format) => this.client.parseDefinition(input, format)
|
|
1412
|
+
};
|
|
1413
|
+
this.authorizations = {
|
|
1414
|
+
check: (token, service, action, authorizeOptions) => this.client.authorize(token, service, action, authorizeOptions)
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
async generateKeyPair(algorithm) {
|
|
1418
|
+
return this.client.generateKeyPair(algorithm);
|
|
1419
|
+
}
|
|
1420
|
+
};
|
|
1421
|
+
function normalizeGrantInput(input) {
|
|
1422
|
+
const errors = [];
|
|
1423
|
+
if (!input || typeof input !== "object") {
|
|
1424
|
+
throw invalidGrantInput(["input must be an object"]);
|
|
1425
|
+
}
|
|
1426
|
+
const principal = normalizePrincipal(input.principal, errors);
|
|
1427
|
+
const agent = normalizeAgent(input.agent, errors);
|
|
1428
|
+
const services = normalizeServices(input, errors);
|
|
1429
|
+
const expires = normalizeExpires(input, errors);
|
|
1430
|
+
if (errors.length > 0 || !principal || !agent || services.length === 0 || !expires) {
|
|
1431
|
+
throw invalidGrantInput(errors);
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
principal,
|
|
1435
|
+
agent,
|
|
1436
|
+
services,
|
|
1437
|
+
expires,
|
|
1438
|
+
...input.rules ? { rules: input.rules } : {},
|
|
1439
|
+
...input.revocable !== void 0 ? { revocable: input.revocable } : {},
|
|
1440
|
+
...input.delegatable !== void 0 ? { delegatable: input.delegatable } : {},
|
|
1441
|
+
...input.maxDelegationDepth !== void 0 ? { maxDelegationDepth: input.maxDelegationDepth } : {},
|
|
1442
|
+
...input.metadata ? { metadata: input.metadata } : {},
|
|
1443
|
+
...input.agentProvider ? { agentProvider: input.agentProvider } : {},
|
|
1444
|
+
...input.legal ? { legal: input.legal } : {}
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
function normalizePrincipal(principal, errors) {
|
|
1448
|
+
if (typeof principal === "string" && principal.trim()) {
|
|
1449
|
+
return { id: principal.trim() };
|
|
1450
|
+
}
|
|
1451
|
+
if (principal && typeof principal === "object" && principal.id) {
|
|
1452
|
+
return principal;
|
|
1453
|
+
}
|
|
1454
|
+
errors.push("principal is required; pass a DID string or { id }");
|
|
1455
|
+
return void 0;
|
|
1456
|
+
}
|
|
1457
|
+
function normalizeAgent(agent, errors) {
|
|
1458
|
+
if (typeof agent === "string" && agent.trim()) {
|
|
1459
|
+
return { id: agent.trim() };
|
|
1460
|
+
}
|
|
1461
|
+
if (agent && typeof agent === "object" && agent.id) {
|
|
1462
|
+
return agent;
|
|
1463
|
+
}
|
|
1464
|
+
errors.push("agent is required; pass a DID string or { id }");
|
|
1465
|
+
return void 0;
|
|
1466
|
+
}
|
|
1467
|
+
function normalizeServices(input, errors) {
|
|
1468
|
+
if (input.services) {
|
|
1469
|
+
if (!Array.isArray(input.services) || input.services.length === 0) {
|
|
1470
|
+
errors.push("services must be a non-empty array when provided");
|
|
1471
|
+
return [];
|
|
1472
|
+
}
|
|
1473
|
+
return input.services;
|
|
1474
|
+
}
|
|
1475
|
+
if (!input.service) {
|
|
1476
|
+
errors.push("service is required unless services is provided");
|
|
1477
|
+
return [];
|
|
1478
|
+
}
|
|
1479
|
+
if (!input.scopes || !Array.isArray(input.scopes) || input.scopes.length === 0) {
|
|
1480
|
+
errors.push("scopes must be a non-empty array unless services is provided");
|
|
1481
|
+
return [];
|
|
1482
|
+
}
|
|
1483
|
+
return [{
|
|
1484
|
+
service: input.service,
|
|
1485
|
+
scopes: input.scopes,
|
|
1486
|
+
...input.constraints ? { constraints: input.constraints } : {},
|
|
1487
|
+
...input.accessMode ? { accessMode: input.accessMode } : {},
|
|
1488
|
+
...input.browserConfig ? { browserConfig: input.browserConfig } : {},
|
|
1489
|
+
...input.apiConfig ? { apiConfig: input.apiConfig } : {}
|
|
1490
|
+
}];
|
|
1491
|
+
}
|
|
1492
|
+
function normalizeExpires(input, errors) {
|
|
1493
|
+
if (input.expires && input.expiresIn) {
|
|
1494
|
+
errors.push("pass either expires or expiresIn, not both");
|
|
1495
|
+
return void 0;
|
|
1496
|
+
}
|
|
1497
|
+
if (input.expires) {
|
|
1498
|
+
return input.expires;
|
|
1499
|
+
}
|
|
1500
|
+
if (input.expiresIn) {
|
|
1501
|
+
return parseDurationFromNow(input.expiresIn);
|
|
1502
|
+
}
|
|
1503
|
+
errors.push("expires or expiresIn is required");
|
|
1504
|
+
return void 0;
|
|
1505
|
+
}
|
|
1506
|
+
function parseDurationFromNow(duration) {
|
|
1507
|
+
const match = /^(\d+)([smhd])$/.exec(duration);
|
|
1508
|
+
if (!match) {
|
|
1509
|
+
throw invalidGrantInput([
|
|
1510
|
+
`expiresIn must use a clear duration like '15m', '2h', or '30d'`
|
|
1511
|
+
]);
|
|
1512
|
+
}
|
|
1513
|
+
const amount = Number(match[1]);
|
|
1514
|
+
if (!Number.isSafeInteger(amount) || amount <= 0) {
|
|
1515
|
+
throw invalidGrantInput(["expiresIn duration must be a positive integer"]);
|
|
1516
|
+
}
|
|
1517
|
+
const unitMs = {
|
|
1518
|
+
s: 1e3,
|
|
1519
|
+
m: 60 * 1e3,
|
|
1520
|
+
h: 60 * 60 * 1e3,
|
|
1521
|
+
d: 24 * 60 * 60 * 1e3
|
|
1522
|
+
};
|
|
1523
|
+
return new Date(Date.now() + amount * unitMs[match[2]]);
|
|
1524
|
+
}
|
|
1525
|
+
function invalidGrantInput(errors) {
|
|
1526
|
+
return new Error(
|
|
1527
|
+
[
|
|
1528
|
+
"Invalid APOA grant input.",
|
|
1529
|
+
...errors.map((error) => `- ${error}`),
|
|
1530
|
+
"Minimal shape: { principal, agent, service, scopes, expiresIn }"
|
|
1531
|
+
].join("\n")
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1392
1534
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1393
1535
|
0 && (module.exports = {
|
|
1536
|
+
APOA,
|
|
1394
1537
|
APOAError,
|
|
1395
1538
|
AttenuationViolationError,
|
|
1396
1539
|
ChainVerificationError,
|