@north-light/sdk 0.3.486 → 0.3.487
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.
|
@@ -29,6 +29,6 @@ export interface VerifyRunNoticeOptions {
|
|
|
29
29
|
fetch?: typeof fetch;
|
|
30
30
|
}
|
|
31
31
|
/** Verify a runtime→app run notice: the bearer is a directory-signed outbound token for `runNoticeUrl`
|
|
32
|
-
* (signature, `iss`, `aud`, `exp`), scope exactly `crtr:run_notice`, `grantee` the app's own principal;
|
|
32
|
+
* (signature, `iss`, `aud`, `exp`; `verifyCallToken`), scope exactly `crtr:run_notice`, `grantee` the app's own principal;
|
|
33
33
|
* the body is `{run_id, latest_sequence}`. Throws `RunNoticeError` (answer 401) on any failure. */
|
|
34
34
|
export declare function verifyRunNotice(request: Request, options: VerifyRunNoticeOptions): Promise<RunNotice>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JwksCache,
|
|
1
|
+
import { JwksCache, verifyCallToken } from '@north-light/identity';
|
|
2
2
|
/** The scope a run-notice token carries. */
|
|
3
3
|
export const RUN_NOTICE_SCOPE = 'crtr:run_notice';
|
|
4
4
|
/** A run notice that failed verification. An app maps it to HTTP 401 and stores nothing. */
|
|
@@ -6,6 +6,7 @@ export class RunNoticeError extends Error {
|
|
|
6
6
|
status = 401;
|
|
7
7
|
constructor(message) { super(message); this.name = 'RunNoticeError'; }
|
|
8
8
|
}
|
|
9
|
+
const isAppOrUser = (value) => typeof value === 'string' && /^(app|user):[A-Za-z0-9_-]{1,64}$/.test(value);
|
|
9
10
|
const jwksUrl = (issuer) => `${issuer.replace(/\/+$/, '')}/.well-known/jwks.json`;
|
|
10
11
|
// One cache per issuer for the global fetch, and one per (issuer, fetch) pair for a caller's fetch: a cache built on
|
|
11
12
|
// one fetch must never answer for another (an unknown kid is re-fetched at most every 30 s).
|
|
@@ -33,7 +34,7 @@ function cacheFor(issuer, fetcher) {
|
|
|
33
34
|
return cache;
|
|
34
35
|
}
|
|
35
36
|
/** Verify a runtime→app run notice: the bearer is a directory-signed outbound token for `runNoticeUrl`
|
|
36
|
-
* (signature, `iss`, `aud`, `exp`), scope exactly `crtr:run_notice`, `grantee` the app's own principal;
|
|
37
|
+
* (signature, `iss`, `aud`, `exp`; `verifyCallToken`), scope exactly `crtr:run_notice`, `grantee` the app's own principal;
|
|
37
38
|
* the body is `{run_id, latest_sequence}`. Throws `RunNoticeError` (answer 401) on any failure. */
|
|
38
39
|
export async function verifyRunNotice(request, options) {
|
|
39
40
|
const match = /^Bearer ([^\s]+)$/.exec(request.headers.get('authorization') ?? '');
|
|
@@ -41,16 +42,20 @@ export async function verifyRunNotice(request, options) {
|
|
|
41
42
|
throw new RunNoticeError('The request carries no bearer token.');
|
|
42
43
|
let claims;
|
|
43
44
|
try {
|
|
44
|
-
claims = await
|
|
45
|
+
claims = await verifyCallToken({ token: match[1], jwks: options.jwks ?? cacheFor(options.issuer, options.fetch), issuer: options.issuer, audience: options.runNoticeUrl });
|
|
45
46
|
}
|
|
46
47
|
catch {
|
|
47
48
|
throw new RunNoticeError('The bearer token is not a valid directory token for this endpoint.');
|
|
48
49
|
}
|
|
49
50
|
if (claims.scope !== RUN_NOTICE_SCOPE)
|
|
50
51
|
throw new RunNoticeError('The token scope is not crtr:run_notice.');
|
|
51
|
-
|
|
52
|
+
const { grantee, initiating_app: initiatingApp, jti, act } = claims.claims;
|
|
53
|
+
// The outbound-token claims a run notice has always carried; `verifyCallToken` does not require them.
|
|
54
|
+
if (!isAppOrUser(grantee) || !isAppOrUser(initiatingApp) || typeof jti !== 'string' || jti === '' || typeof act !== 'object' || act === null || Array.isArray(act) || typeof act.sub !== 'string' || act.sub === '')
|
|
55
|
+
throw new RunNoticeError('The bearer token is not a valid directory token for this endpoint.');
|
|
56
|
+
if (grantee !== options.grantee)
|
|
52
57
|
throw new RunNoticeError('The token was minted for another app.');
|
|
53
|
-
const runtime = claims.
|
|
58
|
+
const runtime = claims.chain[0]?.sub;
|
|
54
59
|
if (typeof runtime !== 'string' || !runtime.startsWith('runtime:'))
|
|
55
60
|
throw new RunNoticeError('The token names no runtime.');
|
|
56
61
|
let body;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: northlight
|
|
3
3
|
description: "Use when building, debugging or shipping an app that signs people in with Northlight, starts runs on their Northlight runtime, or calls Northlight providers (sms, search, integrations, context, device) with @north-light/sdk."
|
|
4
4
|
metadata:
|
|
5
|
-
sdk-version: "0.3.
|
|
5
|
+
sdk-version: "0.3.487"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Building a Northlight app
|
|
@@ -11,7 +11,7 @@ An app on Northlight signs people in with their Northlight account, then starts
|
|
|
11
11
|
|
|
12
12
|
## First: check this skill matches the SDK
|
|
13
13
|
|
|
14
|
-
This skill is for SDK version `0.3.
|
|
14
|
+
This skill is for SDK version `0.3.487`. If the project has `node_modules/@north-light/sdk/package.json`, compare its `version` with that number. If they differ, run `npx @north-light/sdk skill` in the project, then read the rewritten `.agents/skills/northlight/SKILL.md` (or `.claude/skills/northlight/SKILL.md`) instead of this copy. That command needs no account and no crtr. A project that has no SDK yet starts with `npx @north-light/sdk@latest init <dir>`.
|
|
15
15
|
|
|
16
16
|
## Where things run
|
|
17
17
|
|
package/dist/version.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** This SDK's release version. Informational only: compatibility is the contract level, not this number. */
|
|
2
|
-
export declare const SDK_VERSION = "0.3.
|
|
2
|
+
export declare const SDK_VERSION = "0.3.487";
|
|
3
3
|
/** The release sent in `Crouter-Minimum-Runtime-Version` on every request. It is frozen, so a runtime from before the
|
|
4
4
|
* contract level existed (which only knows this header) refuses this SDK, while every later runtime accepts it. */
|
|
5
5
|
export declare const LEGACY_FLOOR = "0.3.423";
|
package/dist/version.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SDK_VERSION is written by scripts/release-version.mjs on every release; do not edit by hand.
|
|
2
2
|
/** This SDK's release version. Informational only: compatibility is the contract level, not this number. */
|
|
3
|
-
export const SDK_VERSION = '0.3.
|
|
3
|
+
export const SDK_VERSION = '0.3.487';
|
|
4
4
|
// LEGACY_FLOOR is written once by `scripts/contract.mjs release`, at the first release that carries the contract level
|
|
5
5
|
// (the `0.0.0` placeholder is replaced there and never again); do not edit by hand.
|
|
6
6
|
/** The release sent in `Crouter-Minimum-Runtime-Version` on every request. It is frozen, so a runtime from before the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.487",
|
|
4
4
|
"crouterContract": "1.11",
|
|
5
5
|
"description": "Typed Node and browser client for running crouter agents through the crtrd /v1 API.",
|
|
6
6
|
"type": "module",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"prepublishOnly": "node -e \"if (process.env.npm_config_tag !== 'next') { console.error('@north-light/sdk publishes only with --tag next; scripts/promote-sdk.mjs is the only mover of latest.'); process.exit(1); }\""
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@north-light/api": "0.3.
|
|
48
|
-
"@north-light/identity": "0.3.
|
|
47
|
+
"@north-light/api": "0.3.487",
|
|
48
|
+
"@north-light/identity": "0.3.487",
|
|
49
49
|
"jose": "^6.2.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|