@rakomi/node 0.0.0 → 0.1.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/LICENSE +21 -0
- package/README.md +57 -1
- package/SECURITY.md +206 -0
- package/dist/agents.d.ts +90 -0
- package/dist/agents.js +203 -0
- package/dist/anonymous.d.ts +50 -0
- package/dist/anonymous.js +105 -0
- package/dist/ciba.d.ts +97 -0
- package/dist/ciba.js +282 -0
- package/dist/client.d.ts +93 -0
- package/dist/client.js +202 -0
- package/dist/credentials.d.ts +87 -0
- package/dist/credentials.js +104 -0
- package/dist/device.d.ts +76 -0
- package/dist/device.js +244 -0
- package/dist/doctor.d.ts +11 -0
- package/dist/doctor.js +135 -0
- package/dist/dpop-session.d.ts +90 -0
- package/dist/dpop-session.js +127 -0
- package/dist/dpop.d.ts +24 -0
- package/dist/dpop.js +51 -0
- package/dist/env-detect.d.ts +11 -0
- package/dist/env-detect.js +26 -0
- package/dist/errors.d.ts +307 -0
- package/dist/errors.js +385 -0
- package/dist/eudi.d.ts +23 -0
- package/dist/eudi.js +27 -0
- package/dist/flags.d.ts +50 -0
- package/dist/flags.js +173 -0
- package/dist/guards.d.ts +16 -0
- package/dist/guards.js +104 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +18 -0
- package/dist/internal/canonical-url.d.ts +13 -0
- package/dist/internal/canonical-url.js +52 -0
- package/dist/internal/shared-constants.d.ts +3 -0
- package/dist/internal/shared-constants.js +3 -0
- package/dist/jwks-cache.d.ts +31 -0
- package/dist/jwks-cache.js +135 -0
- package/dist/link.d.ts +73 -0
- package/dist/link.js +262 -0
- package/dist/middleware.d.ts +21 -0
- package/dist/middleware.js +84 -0
- package/dist/oauth.d.ts +46 -0
- package/dist/oauth.js +457 -0
- package/dist/rbac.d.ts +12 -0
- package/dist/rbac.js +20 -0
- package/dist/token-exchange.d.ts +65 -0
- package/dist/token-exchange.js +163 -0
- package/dist/types.d.ts +436 -0
- package/dist/types.js +1 -0
- package/dist/verify-publisher-webhook.d.ts +25 -0
- package/dist/verify-publisher-webhook.js +47 -0
- package/dist/verify-token.d.ts +3 -0
- package/dist/verify-token.js +148 -0
- package/dist/verify-webhook.d.ts +7 -0
- package/dist/verify-webhook.js +101 -0
- package/package.json +61 -5
- package/sbom.cdx.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CRE8EVE Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
1
|
# @rakomi/node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Server-side Node.js SDK for [Rakomi](https://rakomi.com) — EU-native auth-as-a-service.
|
|
4
|
+
|
|
5
|
+
**Intended purpose & user:** for backend developers verifying Rakomi access tokens and webhook
|
|
6
|
+
signatures in a trusted Node.js runtime.
|
|
7
|
+
|
|
8
|
+
- **1 runtime dependency** — only [`jose`](https://github.com/panva/jose) for JWT/JWKS operations.
|
|
9
|
+
- **ESM-first** — ships as ES modules; CJS supported via Node.js `require(esm)`.
|
|
10
|
+
- **Result pattern** — `verifyToken()` / `verifyWebhook()` never throw; they return
|
|
11
|
+
`{ ok: true, data }` or `{ ok: false, error }`.
|
|
12
|
+
- **Type-safe** — full TypeScript types with generic payload support.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @rakomi/node
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires **Node.js 22+**.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { RakomiClient } from '@rakomi/node';
|
|
26
|
+
|
|
27
|
+
const rakomi = new RakomiClient({
|
|
28
|
+
apiKey: 'akm_live_xxx', // akm_test_* in development
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const result = await rakomi.verifyToken(token);
|
|
32
|
+
if (result.ok) {
|
|
33
|
+
console.log('User ID:', result.data.userId);
|
|
34
|
+
} else {
|
|
35
|
+
console.error('Error:', result.error.code);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
API keys must start with `akm_live_` (production) or `akm_test_` (testing).
|
|
40
|
+
|
|
41
|
+
## Secure defaults
|
|
42
|
+
|
|
43
|
+
Token signatures are verified with a fixed asymmetric algorithm (never read from the token header),
|
|
44
|
+
the signing-key set is fetched only from the pinned issuer host, and issuer/audience/expiry are
|
|
45
|
+
enforced. See the [Secure defaults](https://docs.rakomi.dev/sdk/secure-defaults/) guide.
|
|
46
|
+
|
|
47
|
+
## Documentation
|
|
48
|
+
|
|
49
|
+
- [SDK reference](https://docs.rakomi.dev/sdk/) · [Quickstart](https://docs.rakomi.dev/getting-started/quickstart-sdk/)
|
|
50
|
+
- [Error codes](https://docs.rakomi.dev/sdk/errors/)
|
|
51
|
+
|
|
52
|
+
## Security & support
|
|
53
|
+
|
|
54
|
+
- Vulnerability reporting and the coordinated-disclosure policy: [`SECURITY.md`](./SECURITY.md).
|
|
55
|
+
- Dated support windows (CRA Art. 13(8)): [SDK Support & Lifecycle](https://rakomi.com/sdk-support).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
See [`LICENSE`](./LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
This policy covers the four JS-family SDK packages: `@rakomi/node`, `@rakomi/sdk-core`,
|
|
6
|
+
`@rakomi/react`, and `@rakomi/react-native`.
|
|
7
|
+
|
|
8
|
+
While these packages remain pre-1.0 (`0.x`), they carry **no stability or support guarantee**
|
|
9
|
+
(SemVer 2.0.0 §4); the latest `0.x` line receives security updates on a best-effort basis.
|
|
10
|
+
|
|
11
|
+
From version **1.0** onward, Rakomi maintains the current (N) and previous (N-1) MAJOR in parallel, with N-1 receiving
|
|
12
|
+
security-only fixes. The CRA support period for each MAJOR is determined in accordance with
|
|
13
|
+
**CRA Art. 13(8)** — at least five years, or the product's expected use time where shorter. The authoritative, machine-readable support windows are published at
|
|
14
|
+
[`https://rakomi.com/.well-known/sdk-support.json`](https://rakomi.com/.well-known/sdk-support.json)
|
|
15
|
+
and rendered for humans on the [SDK Support & Lifecycle page](https://rakomi.com/sdk-support). This
|
|
16
|
+
document points at that single source rather than re-typing dated rows.
|
|
17
|
+
|
|
18
|
+
Vulnerabilities in peer dependencies (e.g., React) are out of Rakomi's direct scope, but Rakomi will update minimum peer dependency versions when a peer dependency has a known critical CVE affecting SDK users.
|
|
19
|
+
|
|
20
|
+
## Reporting a Vulnerability
|
|
21
|
+
|
|
22
|
+
**Please do NOT open a public GitHub issue for security vulnerabilities.**
|
|
23
|
+
|
|
24
|
+
We support two reporting channels:
|
|
25
|
+
|
|
26
|
+
1. **Email:** security@rakomi.com (preferred for initial contact)
|
|
27
|
+
2. **GitHub Private Vulnerability Reporting:** [Submit via GitHub Security](https://github.com/rakomidev/rakomi-js/security/advisories/new) — each report automatically receives a GHSA tracking identifier (GHSA-xxxx-xxxx-xxxx).
|
|
28
|
+
|
|
29
|
+
For encrypted communication, a PGP public key is available at:
|
|
30
|
+
`https://rakomi.com/rakomi-security-pgp-key.asc`
|
|
31
|
+
|
|
32
|
+
Include the key fingerprint from this file (section 14) for out-of-band verification when sending encrypted reports.
|
|
33
|
+
|
|
34
|
+
## Response Targets
|
|
35
|
+
|
|
36
|
+
We strive to meet these response targets. Actual response times may vary based on issue complexity and team availability.
|
|
37
|
+
|
|
38
|
+
| Severity | First Response | Fix Target | Disclosure |
|
|
39
|
+
|----------|---------------|------------|------------|
|
|
40
|
+
| Critical (actively exploited) | without undue delay (target: 24h) | 72h patch/mitigation | Designated EU authority notified within 24h (CRA Art. 14) |
|
|
41
|
+
| High | without undue delay (target: 48h) | 14 days | Coordinated after fix |
|
|
42
|
+
| Medium | 5 business days | 90 days | Coordinated after fix |
|
|
43
|
+
| Low | 5 business days | Next release cycle | Changelog note |
|
|
44
|
+
|
|
45
|
+
Severity is assessed using industry-standard vulnerability scoring criteria.
|
|
46
|
+
|
|
47
|
+
Rakomi is maintained by a small team. During periods of reduced availability, the auto-reply from security@rakomi.com will confirm receipt and provide the PGP key. For actively exploited vulnerabilities, we will respond as quickly as humanly possible.
|
|
48
|
+
|
|
49
|
+
## Coordinated Vulnerability Disclosure (CVD) Policy
|
|
50
|
+
|
|
51
|
+
We follow a coordinated disclosure model with a **90-day embargo** period from the date of acknowledgment. During this time:
|
|
52
|
+
|
|
53
|
+
- Rakomi will work to develop and release a fix
|
|
54
|
+
- We will keep the reporter updated on progress at least every 7 business days for Critical/High severity issues
|
|
55
|
+
- We will notify the reporter when a fix is released
|
|
56
|
+
- After 90 days, we will publish a security advisory regardless of fix status
|
|
57
|
+
|
|
58
|
+
We may request an extension if the fix requires significant infrastructure changes, and we will coordinate with the reporter before any deadline extension.
|
|
59
|
+
|
|
60
|
+
## Safe Harbor
|
|
61
|
+
|
|
62
|
+
We will not pursue legal action against researchers who follow this disclosure policy and act in good faith. We consider security research conducted in accordance with this policy to be:
|
|
63
|
+
- Conducted lawfully and in good faith under applicable EU law
|
|
64
|
+
- Not subject to legal action by CRE8EVE Sp. z o.o.
|
|
65
|
+
|
|
66
|
+
Safe harbor **does not extend to**:
|
|
67
|
+
- Accessing or modifying other users' data
|
|
68
|
+
- Performing denial of service attacks
|
|
69
|
+
- Social engineering employees or users
|
|
70
|
+
- Exfiltrating data beyond what is necessary to demonstrate the vulnerability
|
|
71
|
+
- Any activity that violates applicable law
|
|
72
|
+
|
|
73
|
+
## Scope
|
|
74
|
+
|
|
75
|
+
**In scope:**
|
|
76
|
+
- `@rakomi/node`, `@rakomi/sdk-core`, `@rakomi/react`, and `@rakomi/react-native` SDK source code and published npm packages
|
|
77
|
+
- Security properties of API interactions initiated by the SDKs (request signing, token verification, credential handling)
|
|
78
|
+
- Authentication flow logic within the SDKs
|
|
79
|
+
|
|
80
|
+
**Out of scope:**
|
|
81
|
+
- Social engineering attacks against Rakomi employees or users
|
|
82
|
+
- Denial of service attacks
|
|
83
|
+
- Physical security
|
|
84
|
+
- Vulnerabilities in third-party services used by Rakomi's backend
|
|
85
|
+
- Vulnerabilities in peer dependencies (e.g., React) — reported to the relevant maintainer, but Rakomi will update minimum peer dependency versions when a peer dependency has a known critical CVE affecting SDK users
|
|
86
|
+
|
|
87
|
+
This policy applies to the official `@rakomi/node`, `@rakomi/sdk-core`, `@rakomi/react`, and `@rakomi/react-native` packages distributed via npmjs.com. Forks and derivatives are maintained by their respective authors. Customers in regulated sectors (healthcare, finance) may have additional notification obligations beyond this general policy — contact security@rakomi.com for sector-specific compliance documentation.
|
|
88
|
+
|
|
89
|
+
## EU Authority Reporting
|
|
90
|
+
|
|
91
|
+
It is our **policy** to report actively exploited vulnerabilities and severe security incidents
|
|
92
|
+
having an impact on the security of our products to the relevant EU authority in accordance with
|
|
93
|
+
**CRA Art. 14**, on the statutory timeline: an early warning, followed by a fuller notification, and
|
|
94
|
+
a final report.
|
|
95
|
+
|
|
96
|
+
We report to the national coordinator CSIRT designated for our Member State of main establishment
|
|
97
|
+
(Poland), which is our live reporting channel today; onboarding to the EU single reporting platform
|
|
98
|
+
is in progress, and that platform is the documented onward path as it becomes available to
|
|
99
|
+
manufacturers.
|
|
100
|
+
|
|
101
|
+
## Security Update Notifications
|
|
102
|
+
|
|
103
|
+
Consumers of Rakomi SDKs can receive security update notifications through:
|
|
104
|
+
- **GitHub Security Advisories** on this repository (subscribe via GitHub "Watch" → "Security alerts")
|
|
105
|
+
- **npm audit:** `npm audit` or `pnpm audit` will flag known vulnerabilities in installed versions
|
|
106
|
+
|
|
107
|
+
In accordance with CRA Art. 14(8), after becoming aware of an actively exploited vulnerability or a severe incident having an impact on the security of our products, we will inform impacted users (and, where appropriate, all users) — together with any available risk-mitigation or corrective measures — through the above channels.
|
|
108
|
+
|
|
109
|
+
## Manufacturer Identification
|
|
110
|
+
|
|
111
|
+
**Legal entity:** CRE8EVE Sp. z o.o.
|
|
112
|
+
**Registered address:** Tulipanowa 4, 72-003 Dobra, Poland (EU)
|
|
113
|
+
**Contact:** security@rakomi.com (role-based — no personal mailbox or phone is published)
|
|
114
|
+
**Products covered:** `@rakomi/node`, `@rakomi/sdk-core`, `@rakomi/react`, `@rakomi/react-native` (published on npmjs.com)
|
|
115
|
+
|
|
116
|
+
The manufacturer is itself EU-established (Poland), so no CRA Art. 18 Authorised Representative is required (Art. 18 applies to manufacturers established outside the Union). CRA conformity assessment (Art. 32): these are Class I important products (Annex III). The internal-control procedure (Annex VIII, Module A — manufacturer self-assessment, no notified-body involvement) is available for a Class I product **only where harmonised standards, common specifications, or a European cybersecurity certification scheme at assurance level at least 'substantial' are applied in full** (Art. 32(2)); otherwise a third-party route — EU-type examination plus conformity to type (modules B+C), or full quality assurance (module H) — is required. The applicable route will be confirmed against the harmonised standards in force at the CRA application date. The EU Declaration of Conformity and CE marking attach at the CRA application date (Dec 2027) and are not yet issued. See the full manufacturer record at https://docs.rakomi.dev/compliance/manufacturer/.
|
|
117
|
+
|
|
118
|
+
## Export Control / Cryptography Notice
|
|
119
|
+
|
|
120
|
+
The four `@rakomi/*` SDK packages incorporate and invoke cryptography — they verify asymmetric
|
|
121
|
+
digital signatures (JWT/token verification), compare key material in constant time, and rely on the
|
|
122
|
+
host platform's TLS for transport security. They are distributed as **publicly available, mass-market
|
|
123
|
+
software** with cryptographic functionality the end user cannot readily modify.
|
|
124
|
+
|
|
125
|
+
- **EU — Regulation (EU) 2021/821 (Dual-Use):** the SDKs qualify for the **mass-market** treatment
|
|
126
|
+
under the Cryptography Note (Note 3) to Category 5, Part 2 of Annex I — generally available to the
|
|
127
|
+
public, sold without restriction, and not designed for the user to alter the cryptographic
|
|
128
|
+
functionality. No export authorisation is required for their distribution within or from the EU, and —
|
|
129
|
+
unlike the US path — the EU decontrol is **self-executing**, with no notification or filing step.
|
|
130
|
+
- **US — Export Administration Regulations (EAR):** the cryptographic functionality is classifiable
|
|
131
|
+
under **ECCN 5D002**. As **publicly available** open-source software the source code is **not
|
|
132
|
+
subject to the EAR** (15 CFR §734.7(a)), and the corresponding object code is distributed under the
|
|
133
|
+
mass-market provisions. The one-time email notification of the public source-code URL to the U.S.
|
|
134
|
+
BIS and NSA is filed at first public release (15 CFR §742.15(b)).
|
|
135
|
+
|
|
136
|
+
This notice is provided for transparency and is **not legal advice**. Downstream redistributors are
|
|
137
|
+
responsible for their own export, import, and use obligations in their jurisdiction.
|
|
138
|
+
|
|
139
|
+
## Post-Market Surveillance
|
|
140
|
+
|
|
141
|
+
Rakomi monitors SDK health after release through:
|
|
142
|
+
- Automated dependency vulnerability scanning (npm audit, Dependabot)
|
|
143
|
+
- Runtime error patterns derived from API logs (SDK version reported in User-Agent header)
|
|
144
|
+
- Periodic security review of SDK code per the internal security review process
|
|
145
|
+
|
|
146
|
+
This constitutes the "effective and regular tests and reviews of the security of the product with digital elements" required under CRA Annex I, Part II, point (3). The coordinated-vulnerability-disclosure policy required under CRA Annex I, Part II, point (5) is set out in the "Coordinated Vulnerability Disclosure (CVD) Policy" section above.
|
|
147
|
+
|
|
148
|
+
## No Bounty Program
|
|
149
|
+
|
|
150
|
+
Rakomi does not currently operate a paid bug bounty program. We deeply appreciate responsible disclosure and will acknowledge researchers in security advisories (with their consent).
|
|
151
|
+
|
|
152
|
+
## Reference: security.txt
|
|
153
|
+
|
|
154
|
+
This policy is referenced in our machine-readable security contact file (RFC 9116):
|
|
155
|
+
`/.well-known/security.txt` — deployed at `https://rakomi.com/.well-known/security.txt`
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## What to Include in Your Report
|
|
160
|
+
|
|
161
|
+
*(ISO/IEC 29147:2018 §6.5)*
|
|
162
|
+
|
|
163
|
+
To help us triage efficiently, please include:
|
|
164
|
+
|
|
165
|
+
1. **Affected package** name and version (e.g., @rakomi/node 0.2.0)
|
|
166
|
+
2. **Reproduction steps** — a minimal, reproducible example
|
|
167
|
+
3. **Impact assessment** — what an attacker could achieve
|
|
168
|
+
4. **Proof of concept** — if available (do not use real user data)
|
|
169
|
+
5. **Reporter contact** — so we can keep you updated
|
|
170
|
+
|
|
171
|
+
Reports that do not include reproduction steps or fall outside the defined scope may be closed without a tracking ID.
|
|
172
|
+
|
|
173
|
+
## Report Tracking
|
|
174
|
+
|
|
175
|
+
*(ISO/IEC 29147:2018 §6.6)*
|
|
176
|
+
|
|
177
|
+
Each report receives a unique tracking identifier upon acknowledgment. For reports submitted via GitHub Private Vulnerability Reporting, the GHSA identifier (e.g., GHSA-xxxx-xxxx-xxxx) serves as the tracking ID. For email reports, we will direct you to also submit via GitHub PVR for formal tracking.
|
|
178
|
+
|
|
179
|
+
## Status Updates
|
|
180
|
+
|
|
181
|
+
*(ISO/IEC 29147:2018 §6.4)*
|
|
182
|
+
|
|
183
|
+
We provide status updates at least every **7 business days** for Critical/High severity issues, and upon resolution for Medium/Low severity issues.
|
|
184
|
+
|
|
185
|
+
## CVE Assignment
|
|
186
|
+
|
|
187
|
+
Confirmed vulnerabilities with sufficient impact will receive CVE identifiers via GitHub's CNA (CVE Numbering Authority) program.
|
|
188
|
+
|
|
189
|
+
## Reporter Data Privacy
|
|
190
|
+
|
|
191
|
+
*(GDPR Art. 6(1)(f) + Art. 13/14)*
|
|
192
|
+
|
|
193
|
+
Reporter personal data (name, email) is processed under GDPR Art. 6(1)(f) legitimate interest for vulnerability coordination. This data is:
|
|
194
|
+
- Retained for the duration of the vulnerability lifecycle plus 2 years
|
|
195
|
+
- Not shared with third parties except as required for CVE assignment or regulatory reporting (e.g., ENISA, national CSIRT)
|
|
196
|
+
- Accessible to the reporter upon request (GDPR Art. 15)
|
|
197
|
+
- Deletable upon request after vulnerability closure (GDPR Art. 17, where not overridden by regulatory retention obligations)
|
|
198
|
+
|
|
199
|
+
To exercise your GDPR rights, contact security@rakomi.com.
|
|
200
|
+
|
|
201
|
+
## PGP Key Fingerprint
|
|
202
|
+
|
|
203
|
+
The PGP public key for encrypted communication is available at:
|
|
204
|
+
`https://rakomi.com/rakomi-security-pgp-key.asc`
|
|
205
|
+
|
|
206
|
+
Verify the key fingerprint through an independent channel (e.g., LinkedIn, Twitter/X, or a direct phone call) before sending sensitive information.
|
package/dist/agents.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node SDK surface for end-user agent management.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the two `/v1/users/me/agents*` endpoints:
|
|
5
|
+
* - `GET /v1/users/me/agents` → `list({ userToken })`
|
|
6
|
+
* - `DELETE /v1/users/me/agents/{agentClientId}` → `revoke({ userToken, agentClientId })`
|
|
7
|
+
*
|
|
8
|
+
* These endpoints require an end-user JWT (NOT an API key), so each helper
|
|
9
|
+
* takes a `{ userToken }` option. The SDK client (constructed with an API key)
|
|
10
|
+
* only carries the `baseUrl`; the user token flows per-call.
|
|
11
|
+
*
|
|
12
|
+
* Tenant-admin agent management is intentionally out of scope: the SDK is
|
|
13
|
+
* consumed BY agents and end users, not by tenant admins managing agents.
|
|
14
|
+
*/
|
|
15
|
+
import type { VerifyResult } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* End-user-side agent row returned by `GET /v1/users/me/agents`.
|
|
18
|
+
*/
|
|
19
|
+
export type UserAgentResponse = {
|
|
20
|
+
agent_client_id: string;
|
|
21
|
+
agent_name: string;
|
|
22
|
+
agent_logo_url?: string;
|
|
23
|
+
agent_class?: string;
|
|
24
|
+
last_action_at?: string;
|
|
25
|
+
action_count: number;
|
|
26
|
+
revoked_at?: string;
|
|
27
|
+
agent_revoked_at?: string;
|
|
28
|
+
};
|
|
29
|
+
export interface AgentsClientContext {
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
fetchImpl?: typeof fetch;
|
|
32
|
+
}
|
|
33
|
+
export interface AgentsCallOptions {
|
|
34
|
+
/** End-user JWT (Bearer). REQUIRED — these endpoints do NOT accept API keys. */
|
|
35
|
+
userToken: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ListUserAgentsResponse {
|
|
38
|
+
data: UserAgentResponse[];
|
|
39
|
+
}
|
|
40
|
+
export interface RevokeUserAgentOptions extends AgentsCallOptions {
|
|
41
|
+
/** The agent's public `client_id` (matches `oauth_clients.client_id`). */
|
|
42
|
+
agentClientId: string;
|
|
43
|
+
}
|
|
44
|
+
export interface RevokeUserAgentResponse {
|
|
45
|
+
agent_client_id: string;
|
|
46
|
+
revoked_at: string;
|
|
47
|
+
reason: string;
|
|
48
|
+
}
|
|
49
|
+
export declare class AgentsNetworkError extends Error {
|
|
50
|
+
readonly code = "agents/network_error";
|
|
51
|
+
constructor(message: string);
|
|
52
|
+
}
|
|
53
|
+
export declare class AgentNotFoundError extends Error {
|
|
54
|
+
readonly code = "agents/not_found";
|
|
55
|
+
constructor(message?: string);
|
|
56
|
+
}
|
|
57
|
+
export declare class AgentsUnauthorizedError extends Error {
|
|
58
|
+
readonly code = "agents/unauthorized";
|
|
59
|
+
constructor(message?: string);
|
|
60
|
+
}
|
|
61
|
+
export declare class AgentsRateLimitedError extends Error {
|
|
62
|
+
readonly code = "agents/rate_limited";
|
|
63
|
+
readonly retryAfterSeconds?: number;
|
|
64
|
+
constructor(retryAfterSeconds?: number);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* User-scoped agents resource. Attached to `RakomiClient#users.me.agents`.
|
|
68
|
+
*
|
|
69
|
+
* All methods require an end-user JWT passed via `{ userToken }`. The underlying
|
|
70
|
+
* `RakomiClient` API key is NOT sent on these calls — the API rejects API-key
|
|
71
|
+
* auth on user-scoped routes.
|
|
72
|
+
*
|
|
73
|
+
* SSRF hardening: every `fetch` uses `redirect: 'error'`.
|
|
74
|
+
*/
|
|
75
|
+
export declare class AgentsClient {
|
|
76
|
+
private readonly baseUrl;
|
|
77
|
+
private readonly fetchImpl;
|
|
78
|
+
constructor(ctx: AgentsClientContext);
|
|
79
|
+
/**
|
|
80
|
+
* GET /v1/users/me/agents — list every agent that has ever acted on the
|
|
81
|
+
* authenticated user's behalf, with per-user + per-tenant revocation status.
|
|
82
|
+
*/
|
|
83
|
+
list(options: AgentsCallOptions): Promise<VerifyResult<ListUserAgentsResponse>>;
|
|
84
|
+
/**
|
|
85
|
+
* DELETE /v1/users/me/agents/{agentClientId} — revoke an agent from acting
|
|
86
|
+
* on this user's behalf (GDPR Art. 7(3)). Idempotent — re-revoking returns
|
|
87
|
+
* 200 with the existing row and fires no second audit/webhook.
|
|
88
|
+
*/
|
|
89
|
+
revoke(options: RevokeUserAgentOptions): Promise<VerifyResult<RevokeUserAgentResponse>>;
|
|
90
|
+
}
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node SDK surface for end-user agent management.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the two `/v1/users/me/agents*` endpoints:
|
|
5
|
+
* - `GET /v1/users/me/agents` → `list({ userToken })`
|
|
6
|
+
* - `DELETE /v1/users/me/agents/{agentClientId}` → `revoke({ userToken, agentClientId })`
|
|
7
|
+
*
|
|
8
|
+
* These endpoints require an end-user JWT (NOT an API key), so each helper
|
|
9
|
+
* takes a `{ userToken }` option. The SDK client (constructed with an API key)
|
|
10
|
+
* only carries the `baseUrl`; the user token flows per-call.
|
|
11
|
+
*
|
|
12
|
+
* Tenant-admin agent management is intentionally out of scope: the SDK is
|
|
13
|
+
* consumed BY agents and end users, not by tenant admins managing agents.
|
|
14
|
+
*/
|
|
15
|
+
export class AgentsNetworkError extends Error {
|
|
16
|
+
code = 'agents/network_error';
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'AgentsNetworkError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class AgentNotFoundError extends Error {
|
|
23
|
+
code = 'agents/not_found';
|
|
24
|
+
constructor(message = 'Agent not found in this tenant') {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'AgentNotFoundError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class AgentsUnauthorizedError extends Error {
|
|
30
|
+
code = 'agents/unauthorized';
|
|
31
|
+
constructor(message = 'Missing or invalid user token') {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = 'AgentsUnauthorizedError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class AgentsRateLimitedError extends Error {
|
|
37
|
+
code = 'agents/rate_limited';
|
|
38
|
+
retryAfterSeconds;
|
|
39
|
+
constructor(retryAfterSeconds) {
|
|
40
|
+
super('Rate limit exceeded for /v1/users/me/agents');
|
|
41
|
+
this.name = 'AgentsRateLimitedError';
|
|
42
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function safeJson(res) {
|
|
46
|
+
try {
|
|
47
|
+
return (await res.json());
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function parseRetryAfter(res) {
|
|
54
|
+
const v = res.headers.get('retry-after');
|
|
55
|
+
if (!v)
|
|
56
|
+
return undefined;
|
|
57
|
+
const n = Number(v);
|
|
58
|
+
if (Number.isFinite(n) && n > 0)
|
|
59
|
+
return Math.round(n);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function networkError(message) {
|
|
63
|
+
return {
|
|
64
|
+
code: 'agents/network_error',
|
|
65
|
+
message,
|
|
66
|
+
suggestion: 'Verify Rakomi base URL is reachable and that DNS / TLS is healthy.',
|
|
67
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function notFoundError() {
|
|
71
|
+
return {
|
|
72
|
+
code: 'agents/not_found',
|
|
73
|
+
message: 'Agent not found in this tenant',
|
|
74
|
+
suggestion: 'Verify the agentClientId matches an agent that has acted on this user before.',
|
|
75
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function unauthorizedError() {
|
|
79
|
+
return {
|
|
80
|
+
code: 'agents/unauthorized',
|
|
81
|
+
message: 'Missing or invalid user token',
|
|
82
|
+
suggestion: 'Pass a valid end-user JWT in `userToken`. API keys are NOT accepted on /v1/users/me routes.',
|
|
83
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function rateLimitedError(retryAfter) {
|
|
87
|
+
return {
|
|
88
|
+
code: 'agents/rate_limited',
|
|
89
|
+
message: 'Rate limit exceeded for /v1/users/me/agents',
|
|
90
|
+
suggestion: retryAfter !== undefined
|
|
91
|
+
? `Wait ${retryAfter}s and retry.`
|
|
92
|
+
: 'Slow down and retry after a short back-off.',
|
|
93
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function genericError(status, body) {
|
|
97
|
+
return {
|
|
98
|
+
code: body?.code ?? `agents/http_${status}`,
|
|
99
|
+
message: body?.message ?? `HTTP ${status}`,
|
|
100
|
+
suggestion: 'Inspect the response body and retry if appropriate.',
|
|
101
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* User-scoped agents resource. Attached to `RakomiClient#users.me.agents`.
|
|
106
|
+
*
|
|
107
|
+
* All methods require an end-user JWT passed via `{ userToken }`. The underlying
|
|
108
|
+
* `RakomiClient` API key is NOT sent on these calls — the API rejects API-key
|
|
109
|
+
* auth on user-scoped routes.
|
|
110
|
+
*
|
|
111
|
+
* SSRF hardening: every `fetch` uses `redirect: 'error'`.
|
|
112
|
+
*/
|
|
113
|
+
export class AgentsClient {
|
|
114
|
+
baseUrl;
|
|
115
|
+
fetchImpl;
|
|
116
|
+
constructor(ctx) {
|
|
117
|
+
this.baseUrl = ctx.baseUrl;
|
|
118
|
+
this.fetchImpl = ctx.fetchImpl ?? fetch;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* GET /v1/users/me/agents — list every agent that has ever acted on the
|
|
122
|
+
* authenticated user's behalf, with per-user + per-tenant revocation status.
|
|
123
|
+
*/
|
|
124
|
+
async list(options) {
|
|
125
|
+
let res;
|
|
126
|
+
try {
|
|
127
|
+
res = await this.fetchImpl(`${this.baseUrl}/v1/users/me/agents`, {
|
|
128
|
+
method: 'GET',
|
|
129
|
+
redirect: 'error',
|
|
130
|
+
headers: {
|
|
131
|
+
Authorization: `Bearer ${options.userToken}`,
|
|
132
|
+
Accept: 'application/json',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return { ok: false, error: networkError(err?.message ?? 'Network error') };
|
|
138
|
+
}
|
|
139
|
+
if (res.status === 200) {
|
|
140
|
+
const body = await safeJson(res);
|
|
141
|
+
if (!body || !Array.isArray(body.data)) {
|
|
142
|
+
return { ok: false, error: networkError('Malformed response body — expected { data: UserAgentResponse[] }') };
|
|
143
|
+
}
|
|
144
|
+
return { ok: true, data: body };
|
|
145
|
+
}
|
|
146
|
+
if (res.status === 401)
|
|
147
|
+
return { ok: false, error: unauthorizedError() };
|
|
148
|
+
if (res.status === 429)
|
|
149
|
+
return { ok: false, error: rateLimitedError(parseRetryAfter(res)) };
|
|
150
|
+
const body = await safeJson(res);
|
|
151
|
+
return { ok: false, error: genericError(res.status, body) };
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* DELETE /v1/users/me/agents/{agentClientId} — revoke an agent from acting
|
|
155
|
+
* on this user's behalf (GDPR Art. 7(3)). Idempotent — re-revoking returns
|
|
156
|
+
* 200 with the existing row and fires no second audit/webhook.
|
|
157
|
+
*/
|
|
158
|
+
async revoke(options) {
|
|
159
|
+
if (!options.agentClientId) {
|
|
160
|
+
return {
|
|
161
|
+
ok: false,
|
|
162
|
+
error: {
|
|
163
|
+
code: 'agents/invalid_request',
|
|
164
|
+
message: 'agentClientId is required',
|
|
165
|
+
suggestion: 'Pass the agent\'s public client_id (matches oauth_clients.client_id).',
|
|
166
|
+
docs_url: 'https://docs.rakomi.dev/guides/ai-agents',
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
let res;
|
|
171
|
+
try {
|
|
172
|
+
res = await this.fetchImpl(`${this.baseUrl}/v1/users/me/agents/${encodeURIComponent(options.agentClientId)}`, {
|
|
173
|
+
method: 'DELETE',
|
|
174
|
+
redirect: 'error',
|
|
175
|
+
headers: {
|
|
176
|
+
Authorization: `Bearer ${options.userToken}`,
|
|
177
|
+
Accept: 'application/json',
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
return { ok: false, error: networkError(err?.message ?? 'Network error') };
|
|
183
|
+
}
|
|
184
|
+
if (res.status === 200) {
|
|
185
|
+
const body = await safeJson(res);
|
|
186
|
+
if (!body ||
|
|
187
|
+
typeof body.agent_client_id !== 'string' ||
|
|
188
|
+
typeof body.revoked_at !== 'string' ||
|
|
189
|
+
typeof body.reason !== 'string') {
|
|
190
|
+
return { ok: false, error: networkError('Malformed response body — expected { agent_client_id, revoked_at, reason }') };
|
|
191
|
+
}
|
|
192
|
+
return { ok: true, data: body };
|
|
193
|
+
}
|
|
194
|
+
if (res.status === 401)
|
|
195
|
+
return { ok: false, error: unauthorizedError() };
|
|
196
|
+
if (res.status === 404)
|
|
197
|
+
return { ok: false, error: notFoundError() };
|
|
198
|
+
if (res.status === 429)
|
|
199
|
+
return { ok: false, error: rateLimitedError(parseRetryAfter(res)) };
|
|
200
|
+
const body = await safeJson(res);
|
|
201
|
+
return { ok: false, error: genericError(res.status, body) };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node SDK surface for anonymous sign-ins.
|
|
3
|
+
*
|
|
4
|
+
* Pure server-side: no browser globals (window/document/navigator/localStorage).
|
|
5
|
+
* Browser apps should use the React hook `useAnonymousSignin` from `@rakomi/react`
|
|
6
|
+
* which calls this under the hood via the user's backend, OR hit `/v1/auth/anonymous`
|
|
7
|
+
* directly from the browser with the tenant's public API key.
|
|
8
|
+
*
|
|
9
|
+
* Returns a Result shape consistent with the rest of the SDK: NEVER throws on
|
|
10
|
+
* expected API errors (403/429/402/401), throws ONLY on programmer errors.
|
|
11
|
+
*/
|
|
12
|
+
import { RakomiError } from './errors.js';
|
|
13
|
+
import { AnonymousSessionExpiredError } from './errors.js';
|
|
14
|
+
import type { VerifyResult } from './types.js';
|
|
15
|
+
export interface AnonymousSigninOptions {
|
|
16
|
+
/** Optional tenant-supplied public metadata (≤1 KB JSON, same rules as). */
|
|
17
|
+
publicMetadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface AnonymousSigninResult {
|
|
20
|
+
accessToken: string;
|
|
21
|
+
refreshToken: string;
|
|
22
|
+
expiresIn: number;
|
|
23
|
+
user: {
|
|
24
|
+
id: string;
|
|
25
|
+
isAnonymous: true;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface AnonymousSigninCallContext {
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
fetchImpl?: typeof fetch;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* POST /v1/auth/anonymous. Returns Result — never throws on 4xx.
|
|
36
|
+
*/
|
|
37
|
+
export declare function anonymousSignin(ctx: AnonymousSigninCallContext, options?: AnonymousSigninOptions): Promise<VerifyResult<AnonymousSigninResult>>;
|
|
38
|
+
/**
|
|
39
|
+
* Decode an access token's `is_anonymous` claim WITHOUT verifying the signature.
|
|
40
|
+
* Used only to gate the `AnonymousSessionExpiredError` path — callers should still
|
|
41
|
+
* verify any trust-sensitive claim via `client.verifyToken()`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isAnonymousTokenHeuristic(accessToken: string): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Classify a refresh failure: if the prior token was anonymous and the refresh
|
|
46
|
+
* returned 401, throw `AnonymousSessionExpiredError`. Otherwise the caller
|
|
47
|
+
* decides its own UX routing.
|
|
48
|
+
*/
|
|
49
|
+
export declare function maybeThrowAnonymousExpired(priorAccessToken: string | null, refreshStatus: number): void;
|
|
50
|
+
export { AnonymousSessionExpiredError, RakomiError };
|