@segmentstream-cloud/app 0.0.0-stage → 0.2.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 +201 -0
- package/NOTICE +2 -0
- package/README.md +190 -2
- package/dist/app.d.ts +33 -0
- package/dist/app.js +35 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +41 -0
- package/dist/context.d.ts +10 -0
- package/dist/context.js +1 -0
- package/dist/cookies.d.ts +47 -0
- package/dist/cookies.js +142 -0
- package/dist/data.d.ts +138 -0
- package/dist/data.js +225 -0
- package/dist/handlers.d.ts +8 -0
- package/dist/handlers.js +133 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +16 -0
- package/dist/parameters.d.ts +55 -0
- package/dist/parameters.js +118 -0
- package/dist/pkce.d.ts +8 -0
- package/dist/pkce.js +40 -0
- package/dist/proxy.d.ts +13 -0
- package/dist/proxy.js +91 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +50 -0
- package/dist/sessions.d.ts +27 -0
- package/dist/sessions.js +72 -0
- package/dist/sign-in.d.ts +28 -0
- package/dist/sign-in.js +56 -0
- package/dist/tokens.d.ts +26 -0
- package/dist/tokens.js +70 -0
- package/package.json +55 -4
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createRemoteJWKSet, errors, jwtVerify } from "jose";
|
|
2
|
+
const CLOCK_TOLERANCE_SECONDS = 5;
|
|
3
|
+
const remoteKeySets = new Map();
|
|
4
|
+
/** One remote JWKS per SegmentStream origin, shared by every app instance in the process. */
|
|
5
|
+
export function remoteKeySet(baseUrl) {
|
|
6
|
+
const existing = remoteKeySets.get(baseUrl);
|
|
7
|
+
if (existing !== undefined) {
|
|
8
|
+
return existing;
|
|
9
|
+
}
|
|
10
|
+
const keySet = createRemoteJWKSet(new URL(`${baseUrl}/api/v1/app-sessions/jwks`), {
|
|
11
|
+
timeoutDuration: 5_000,
|
|
12
|
+
});
|
|
13
|
+
remoteKeySets.set(baseUrl, keySet);
|
|
14
|
+
return keySet;
|
|
15
|
+
}
|
|
16
|
+
export function createTokenVerifier(config, keySet) {
|
|
17
|
+
return async function verifyToken(token) {
|
|
18
|
+
let payload;
|
|
19
|
+
try {
|
|
20
|
+
const verified = await jwtVerify(token, keySet, {
|
|
21
|
+
algorithms: ["ES256"],
|
|
22
|
+
issuer: config.issuer,
|
|
23
|
+
clockTolerance: CLOCK_TOLERANCE_SECONDS,
|
|
24
|
+
requiredClaims: ["sub", "exp", "iat", "org", "wsp"],
|
|
25
|
+
});
|
|
26
|
+
payload = verified.payload;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return classifyVerificationFailure(error);
|
|
30
|
+
}
|
|
31
|
+
const user = appUserFromClaims(config, payload);
|
|
32
|
+
return user === null ? { status: "invalid" } : { status: "valid", user, token };
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function classifyVerificationFailure(error) {
|
|
36
|
+
if (error instanceof errors.JWTExpired) {
|
|
37
|
+
return { status: "expired" };
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof errors.JWKSTimeout) {
|
|
40
|
+
return { status: "unavailable" };
|
|
41
|
+
}
|
|
42
|
+
if (error instanceof errors.JOSEError) {
|
|
43
|
+
return { status: "invalid" };
|
|
44
|
+
}
|
|
45
|
+
// Network failures while fetching the key set are not token defects.
|
|
46
|
+
return { status: "unavailable" };
|
|
47
|
+
}
|
|
48
|
+
function appUserFromClaims(config, payload) {
|
|
49
|
+
const { sub, org, wsp, email, name } = payload;
|
|
50
|
+
if (typeof sub !== "string" ||
|
|
51
|
+
sub === "" ||
|
|
52
|
+
org !== config.organizationId ||
|
|
53
|
+
wsp !== config.workspaceId) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (!hasAppAudience(payload.aud)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
id: sub,
|
|
61
|
+
...(typeof email === "string" ? { email } : {}),
|
|
62
|
+
...(typeof name === "string" ? { name } : {}),
|
|
63
|
+
organizationId: org,
|
|
64
|
+
workspaceId: wsp,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function hasAppAudience(audience) {
|
|
68
|
+
const audiences = typeof audience === "string" ? [audience] : (audience ?? []);
|
|
69
|
+
return audiences.some((candidate) => candidate.startsWith("app:"));
|
|
70
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/app",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Sign-in, sessions, and warehouse data access for SegmentStream workspace apps built with Next.js.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "SegmentStream",
|
|
7
|
+
"homepage": "https://segmentstream.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/segmentstream/segmentstream.git",
|
|
11
|
+
"directory": "packages/app-sdk"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"segmentstream",
|
|
15
|
+
"nextjs",
|
|
16
|
+
"authentication"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"NOTICE"
|
|
30
|
+
],
|
|
31
|
+
"sideEffects": false,
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"lint": "eslint src tests --config ../../eslint.config.mjs",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"prepack": "npm run build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"jose": "^6.2.12"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"next": ">=16.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^24",
|
|
50
|
+
"next": "16.3.8",
|
|
51
|
+
"typescript": "^5.9",
|
|
52
|
+
"vitest": "^4.1.11"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|