@h-ai/serv 0.1.0-alpha.44 → 0.1.0-alpha.46

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.
@@ -1,16 +1,17 @@
1
1
  import { HaiResult, Logger } from '@h-ai/core';
2
2
 
3
3
  /**
4
- * @h-ai/serv — Refresh Token httpOnly Cookie 传输中间件
4
+ * @h-ai/serv — Refresh Token 浏览器 / 原生传输
5
5
  *
6
- * **职责边界**:本文件 **仅** 负责 `refresh token` 的 cookie 化传输,不涉及 access token 校验。
6
+ * **职责边界**:本文件 **仅** 负责 `refresh token` 的传输,不涉及 access token 校验。
7
7
  * - access token 校验由 `serv-context.ts` 的 `buildAuthContextFactory` 统一处理
8
- * - 本文件只关心:浏览器场景下,refresh token 不应写入 JS 可读存储(防 XSS),改走 httpOnly cookie
8
+ * - 浏览器使用 httpOnly cookie,避免 refresh token 进入 JS 可读存储
9
+ * - 经过应用可信来源校验的原生客户端可改用 JSON body,交由系统安全存储持久化
9
10
  *
10
11
  * **挂载的路由**:
11
- * - login / register / loginWithOtp:拦截 oRPC 成功响应,从响应体提取 refresh token → Set-Cookie,并从响应体擦除
12
- * - logout:拦截 oRPC 成功响应,清除 Cookie(Max-Age=0)
13
- * - refresh:**专属路由**(不走 oRPC),从 Cookie 读取 refresh token → 调用 `onRefresh` → 更新 Cookie
12
+ * - login / register / loginWithOtp:浏览器写 Cookie 并擦除响应 token;受信原生请求保留响应 token
13
+ * - logout:浏览器清除 Cookie(Max-Age=0);原生客户端自行清理安全存储
14
+ * - refresh:**专属路由**(不走 oRPC),按请求来源从 Cookie 或 JSON body 读取并轮换 token
14
15
  *
15
16
  * **Cookie 规格**:`HttpOnly; SameSite=Strict; Secure`(生产)/ 无 Secure(开发)
16
17
  * Cookie Path 限制为 `{apiPrefix}/auth/refresh`,最小化浏览器自动发送范围(避免 CSRF 攻击面)。
@@ -28,9 +29,20 @@ interface RefreshTokenPair {
28
29
  readonly tokenType?: string;
29
30
  }
30
31
  /**
31
- * Refresh Token Cookie 传输配置。
32
+ * 原生客户端的 refresh token body 传输配置。
32
33
  *
33
- * 传入 `serv.createApp({ refreshCookie: ... })` 后自动管理 refresh token cookie。
34
+ * 浏览器继续使用 httpOnly cookie;只有经过应用可信来源校验的原生请求,才允许在
35
+ * JSON body 中接收和返回 refresh token。`isRequest` 必须同时校验客户端标识与 Origin,
36
+ * 不能仅信任可伪造的单个请求头。
37
+ */
38
+ interface NativeRefreshTokenTransportConfig {
39
+ /** 判断当前请求是否来自受信原生客户端。 */
40
+ readonly isRequest: (request: Request) => boolean;
41
+ }
42
+ /**
43
+ * Refresh Token 传输配置。
44
+ *
45
+ * 传入 `serv.createApp({ refreshCookie: ... })` 后自动管理浏览器 cookie,并可选启用受信原生 body 通道。
34
46
  * access token 仍然走 `Authorization: Bearer <token>`;本配置只改变 refresh token 的传输通道。
35
47
  *
36
48
  * 刷新回调来源(优先级从高到低):
@@ -51,6 +63,13 @@ interface RefreshCookieConfig {
51
63
  * - 本地 HTTP 开发设为 `false` 可让 Cookie 在 HTTP 下正常工作。
52
64
  */
53
65
  readonly secure?: boolean;
66
+ /**
67
+ * 为受信原生客户端启用 JSON body 传输。
68
+ *
69
+ * 启用后,匹配请求的登录、注册与刷新响应保留 `refreshToken`,刷新请求从
70
+ * `{ refreshToken }` body 读取凭证,且不读写浏览器 Cookie。
71
+ */
72
+ readonly nativeTokenTransport?: NativeRefreshTokenTransportConfig;
54
73
  /**
55
74
  * 自定义刷新回调(高级用法)。
56
75
  * 未提供时使用顶层 `iam.session.refresh`;两者均缺失时 `createApp` 会抛错。
@@ -121,7 +140,7 @@ type CreateServContext = (input: CreateServContextInput) => ServContext | Promis
121
140
  *
122
141
  * - 提取 `x-request-id` / `accept-language` / `x-forwarded-for` / `x-real-ip` / `user-agent`
123
142
  * - 从 `Authorization: Bearer <token>` 提取 `accessToken`
124
- * - **不会**读取 refresh token:refresh token 若启用 `refreshCookie`,只走 httpOnly cookie
143
+ * - **不会**读取 refresh token:refresh token 若启用 `refreshCookie`,只走专属刷新路由
125
144
  *
126
145
  * `session` 字段始终为 `undefined`:若需会话填充,请使用 {@link buildAuthContextFactory}
127
146
  * 或直接通过 `serv.createApp({ iam })` 让框架自动选择合适工厂。
@@ -140,7 +159,7 @@ declare function parseRequestContext(input: CreateServContextInput): ServContext
140
159
  * 4. 下游 `requireAuth` / `requirePermission` / `requireRole` 据此判断 401/403
141
160
  *
142
161
  * refresh token **不参与**这里的认证流程:它只在 access token 过期后,被 `/auth/refresh`
143
- * 从 httpOnly cookie 中读出,用于换发新的 access token。
162
+ * 从浏览器 httpOnly cookie 或受信原生 JSON body 中读出,用于换发新的 access token。
144
163
  *
145
164
  * **安全保证**:
146
165
  * - 每个请求都会重新调用 `verifyToken`(不缓存),用户被撤权后立即生效
@@ -152,4 +171,4 @@ declare function parseRequestContext(input: CreateServContextInput): ServContext
152
171
  */
153
172
  declare function buildAuthContextFactory(verifyToken: (accessToken: string) => Promise<HaiResult<ServSession>>): CreateServContext;
154
173
 
155
- export { type CreateServContext as C, type RefreshCookieConfig as R, type ServSession as S, type ServContext as a, type ServIam as b, buildAuthContextFactory as c, type CreateServContextInput as d, type RefreshTokenPair as e, parseRequestContext as p };
174
+ export { type CreateServContext as C, type NativeRefreshTokenTransportConfig as N, type RefreshCookieConfig as R, type ServSession as S, type ServContext as a, type ServIam as b, buildAuthContextFactory as c, type CreateServContextInput as d, type RefreshTokenPair as e, parseRequestContext as p };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/serv",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.44",
4
+ "version": "0.1.0-alpha.46",
5
5
  "description": "Hai Framework service runtime powered by Hono and oRPC.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -32,9 +32,9 @@
32
32
  "access": "public"
33
33
  },
34
34
  "peerDependencies": {
35
- "@h-ai/ai": "0.1.0-alpha.44",
36
- "@h-ai/iam": "0.1.0-alpha.44",
37
- "@h-ai/storage": "0.1.0-alpha.44"
35
+ "@h-ai/iam": "0.1.0-alpha.46",
36
+ "@h-ai/storage": "0.1.0-alpha.46",
37
+ "@h-ai/ai": "0.1.0-alpha.46"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@h-ai/ai": {
@@ -56,9 +56,9 @@
56
56
  "@scalar/api-reference": "^1.57.1",
57
57
  "hono": "^4.12.18",
58
58
  "zod": "^4.4.3",
59
- "@h-ai/api-contract": "0.1.0-alpha.44",
60
- "@h-ai/crypto": "0.1.0-alpha.44",
61
- "@h-ai/core": "0.1.0-alpha.44"
59
+ "@h-ai/api-contract": "0.1.0-alpha.46",
60
+ "@h-ai/core": "0.1.0-alpha.46",
61
+ "@h-ai/crypto": "0.1.0-alpha.46"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/node": "^25.9.1",
@@ -66,9 +66,9 @@
66
66
  "tsup": "^8.5.1",
67
67
  "typescript": "^6.0.3",
68
68
  "vitest": "^4.1.7",
69
- "@h-ai/ai": "0.1.0-alpha.44",
70
- "@h-ai/iam": "0.1.0-alpha.44",
71
- "@h-ai/storage": "0.1.0-alpha.44"
69
+ "@h-ai/iam": "0.1.0-alpha.46",
70
+ "@h-ai/ai": "0.1.0-alpha.46",
71
+ "@h-ai/storage": "0.1.0-alpha.46"
72
72
  },
73
73
  "scripts": {
74
74
  "build": "tsup",