@ateam-ai-mobile/core 0.1.9 → 0.1.11
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.mts +14 -5
- package/dist/index.d.ts +14 -5
- package/dist/index.js +23 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/authSession.ts +1 -20
- package/src/config.ts +14 -4
- package/src/index.ts +1 -0
- package/src/jwt.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ateam-ai-mobile/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Headless ADAS SDK core — auth bridge, API client, chat/job streaming, MCP calls, theme tokens. No UI, no Expo dependencies.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/authSession.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* so behaviour is identical (the R0.5 authRefresh baseline is the oracle).
|
|
23
23
|
*/
|
|
24
24
|
import { getCoreApiUrl, type RefreshOutcome } from './config';
|
|
25
|
+
import { decodeJwtPayload } from './jwt';
|
|
25
26
|
|
|
26
27
|
export interface SessionUser {
|
|
27
28
|
actorId: string;
|
|
@@ -71,26 +72,6 @@ export interface AuthSessionDeps {
|
|
|
71
72
|
onToken?: (token: string, user: SessionUser | null) => void | Promise<void>;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
// ── JWT projection (verbatim from the app) ──────────────────────────────────
|
|
75
|
-
|
|
76
|
-
function decodeJwtPayload(token: string): Record<string, any> | null {
|
|
77
|
-
try {
|
|
78
|
-
const segs = token.split('.');
|
|
79
|
-
if (segs.length !== 3) return null;
|
|
80
|
-
// base64url → base64
|
|
81
|
-
const b64 = segs[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
82
|
-
// Pad to a multiple of 4
|
|
83
|
-
const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
|
|
84
|
-
const json = typeof atob === 'function'
|
|
85
|
-
? atob(padded)
|
|
86
|
-
// Hermes always has atob; fallback for older runtimes
|
|
87
|
-
: Buffer.from(padded, 'base64').toString('utf8');
|
|
88
|
-
return JSON.parse(json);
|
|
89
|
-
} catch {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
75
|
export function userFromJwt(token: string): SessionUser | null {
|
|
95
76
|
const claims = decodeJwtPayload(token);
|
|
96
77
|
if (!claims?.sub) return null;
|
package/src/config.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { AttachmentCache } from './storage/types';
|
|
12
|
+
import { tenantFromToken } from './jwt';
|
|
12
13
|
|
|
13
14
|
/** Result of a host-driven token refresh. Mirrors the semantics the API
|
|
14
15
|
* client depends on: only 'auth_failed' logs the user out; 'transient'
|
|
@@ -62,10 +63,9 @@ export interface AdasConfig {
|
|
|
62
63
|
* the user belongs to). */
|
|
63
64
|
consumerAuthUrl?: UrlSource;
|
|
64
65
|
tokenProvider: TokenProvider;
|
|
65
|
-
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* tenant switch). */
|
|
66
|
+
/** The host's tenant PICK — used only while signed out (e.g. the login
|
|
67
|
+
* screen's chosen workspace). A signed-in session's tenant is always the
|
|
68
|
+
* token's `tenant` claim, the one the backend acts on (getTenant()). */
|
|
69
69
|
tenant?: string | (() => string);
|
|
70
70
|
/** Current end user as known to the host. actorId is used as the chat
|
|
71
71
|
* reply context; without it, replies route to 'unknown'. */
|
|
@@ -132,7 +132,17 @@ export function getConsumerAuthApiUrl(): string {
|
|
|
132
132
|
return resolveUrl(url);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/** The tenant the app is scoped to. Signed in: the live token's `tenant` claim,
|
|
136
|
+
* the same tenant every API call acts on, so a plugin URL, the voice session
|
|
137
|
+
* and the header can never name a different workspace than the backend does
|
|
138
|
+
* (2026-09-29: a stored pick still said 'ada' while the token said
|
|
139
|
+
* ateam-mcp-test, and a plugin page 401'd). Signed out: the host's pick, or ''
|
|
140
|
+
* when there is none — never a default tenant. */
|
|
135
141
|
export function getTenant(): string {
|
|
142
|
+
const token = getConfig().tokenProvider.getToken();
|
|
143
|
+
// Signed in, the token is the ONLY answer: a token without a claim names no
|
|
144
|
+
// tenant (''), never the host's stored pick and never a default.
|
|
145
|
+
if (token) return tenantFromToken(token) || '';
|
|
136
146
|
const t = getConfig().tenant;
|
|
137
147
|
return (typeof t === 'function' ? t() : t) || '';
|
|
138
148
|
}
|
package/src/index.ts
CHANGED
|
@@ -50,6 +50,7 @@ export type { Session, SessionEnv } from './session';
|
|
|
50
50
|
// SDK-owned session state machine (R6) — JWT lifecycle, refresh table, restore,
|
|
51
51
|
// logout orchestration. The host provides secure storage + a logout teardown hook.
|
|
52
52
|
export { createAuthSession, userFromJwt } from './authSession';
|
|
53
|
+
export { tenantFromToken } from './jwt';
|
|
53
54
|
export type {
|
|
54
55
|
AuthSession,
|
|
55
56
|
AuthSessionState,
|
package/src/jwt.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// JWT projection shared by the session state machine and the tenant getter.
|
|
2
|
+
// Decode only: the token was issued and is verified by the platform; the client
|
|
3
|
+
// reads its claims to know WHO and WHERE, never to trust them for access.
|
|
4
|
+
|
|
5
|
+
export function decodeJwtPayload(token: string): Record<string, any> | null {
|
|
6
|
+
try {
|
|
7
|
+
const segs = token.split('.');
|
|
8
|
+
if (segs.length !== 3) return null;
|
|
9
|
+
// base64url → base64
|
|
10
|
+
const b64 = segs[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
11
|
+
// Pad to a multiple of 4
|
|
12
|
+
const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
|
|
13
|
+
const json = typeof atob === 'function'
|
|
14
|
+
? atob(padded)
|
|
15
|
+
// Hermes always has atob; fallback for older runtimes
|
|
16
|
+
: Buffer.from(padded, 'base64').toString('utf8');
|
|
17
|
+
return JSON.parse(json);
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The tenant a signed-in session is scoped to: the token's `tenant` claim, the
|
|
24
|
+
* same value the backend reads. Null for no token or a token without one. */
|
|
25
|
+
export function tenantFromToken(token: string | null | undefined): string | null {
|
|
26
|
+
if (!token) return null;
|
|
27
|
+
const t = decodeJwtPayload(token)?.tenant;
|
|
28
|
+
return typeof t === 'string' && t ? t : null;
|
|
29
|
+
}
|