@weifuwujs/auth 0.9.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.
Files changed (50) hide show
  1. package/README.md +172 -0
  2. package/dist/auth.d.ts +4 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +625 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +159 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/index.d.ts +6 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +4 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/password.d.ts +21 -0
  15. package/dist/password.d.ts.map +1 -0
  16. package/dist/password.js +75 -0
  17. package/dist/password.js.map +1 -0
  18. package/dist/postgres.d.ts +22 -0
  19. package/dist/postgres.d.ts.map +1 -0
  20. package/dist/postgres.js +240 -0
  21. package/dist/postgres.js.map +1 -0
  22. package/dist/sqlite.d.ts +3 -0
  23. package/dist/sqlite.d.ts.map +1 -0
  24. package/dist/sqlite.js +2 -0
  25. package/dist/sqlite.js.map +1 -0
  26. package/dist/stores/memory.d.ts +4 -0
  27. package/dist/stores/memory.d.ts.map +1 -0
  28. package/dist/stores/memory.js +150 -0
  29. package/dist/stores/memory.js.map +1 -0
  30. package/dist/stores/sqlite.d.ts +13 -0
  31. package/dist/stores/sqlite.d.ts.map +1 -0
  32. package/dist/stores/sqlite.js +215 -0
  33. package/dist/stores/sqlite.js.map +1 -0
  34. package/dist/testing.d.ts +13 -0
  35. package/dist/testing.d.ts.map +1 -0
  36. package/dist/testing.js +123 -0
  37. package/dist/testing.js.map +1 -0
  38. package/dist/types.d.ts +190 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +53 -0
  43. package/templates/react-router-auth/app/auth.ts +13 -0
  44. package/templates/react-router-auth/app/context.ts +6 -0
  45. package/templates/react-router-auth/app/routes/account.tsx +47 -0
  46. package/templates/react-router-auth/app/routes/login.tsx +59 -0
  47. package/templates/react-router-auth/app/routes/register.tsx +47 -0
  48. package/templates/react-router-auth/app/routes/reset-password.tsx +54 -0
  49. package/templates/react-router-auth/app/routes.ts +9 -0
  50. package/templates/react-router-auth/app/server.ts +31 -0
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # 用户系统(`@weifuwujs/auth`)
2
+
3
+ 零配置、安全默认的用户系统:scrypt、服务端不透明 session、cookie 属性、登录轮换、防枚举、
4
+ 限流、Origin 校验、单次短 TTL 重置 token。不进主入口,不 import 不背依赖。
5
+
6
+ ## 五分钟闭环
7
+
8
+ ```bash
9
+ npx @weifuwujs/weifuwu init my-app
10
+ cd my-app
11
+ cp -r "$(npm root)/@weifuwujs/auth/templates/react-router-auth/." .
12
+ npx @weifuwujs/auth migrate # 生成 data/auth.db
13
+ npm run dev # http://localhost:3000/login
14
+ ```
15
+
16
+ 页面为 React Router 无 JS 表单(渐进增强):注册 / 登录 / 忘记密码 / 账户(改密、登出)。
17
+ 叠加层(`templates/react-router-auth`)内容:
18
+
19
+ | 文件 | 作用 |
20
+ | --- | --- |
21
+ | `app/auth.ts` | `createAuth()` 单例(默认 `sqliteAuthStore('data/auth.db')`) |
22
+ | `app/server.ts` | `use(auth.middleware).mount(auth)` + `use('/account', auth.require())` + 注入 `authUser` |
23
+ | `app/routes/{login,register,reset-password,account}.tsx` | 无 JS 表单页(PRG:失败 303 回页带 `?error=`) |
24
+
25
+ ## 端点
26
+
27
+ 挂载:`app.mount(auth)`(JSON);同一端点同时接受 `application/x-www-form-urlencoded`
28
+ (表单 → 303 + `Location`,页面回显错误)。
29
+
30
+ | 方法 | 路径 | 说明 |
31
+ | --- | --- | --- |
32
+ | POST | `/auth/register` | 注册(201;表单 303) |
33
+ | POST | `/auth/login` | 登录,轮换旧会话(防 session fixation) |
34
+ | POST | `/auth/logout` | 登出并清 cookie |
35
+ | GET | `/auth/me` | 当前用户(`ctx.auth.user` 每请求现查) |
36
+ | PATCH | `/auth/me` | 改邮箱(需当前密码) |
37
+ | POST | `/auth/password/change` | 改密(保留当前会话,踢其他) |
38
+ | POST | `/auth/password/forgot` | 恒 200(防枚举);未配置 hook 时 dev 打印链接、生产静默 |
39
+ | POST | `/auth/password/reset` | 单次 token;过期/复用一律无效 |
40
+
41
+ ## 配置
42
+
43
+ ```ts
44
+ // app/auth.ts
45
+ import { createAuth } from '@weifuwujs/auth'
46
+ import { sqliteAuthStore } from '@weifuwujs/auth/sqlite'
47
+
48
+ export const auth = createAuth({
49
+ store: sqliteAuthStore('data/auth.db'),
50
+ session: { cookieName: 'ww_session', maxAge: 30 * 24 * 3600 }, // secure 默认随生产环境开启
51
+ rateLimit: { max: 10, windowMs: 60_000 }, // 默认每 IP 每分钟 10 次
52
+ origin: false, // 默认同源校验;反代跨域时显式列白名单
53
+ })
54
+
55
+ // app/server.ts
56
+ const app = createApp().use(auth.middleware).mount(auth)
57
+ app.get('/orders', auth.require(), listOrders)
58
+ app.get('/billing', auth.require((user) => user.role === 'admin'), billing)
59
+ ```
60
+
61
+ - `ctx.auth = { user, login(user), logout(), verifyPassword(plain) }`;
62
+ - `auth.require()`:HTTP 未登录 → 页面 302 `/login?next=…`、API 401;角色/谓词不过 → 403;
63
+ WS 升级未登录 → 401 拒绝;
64
+ - 只有一个中间件实例:HTTP 用 `app.use(auth.middleware)`,WS 同时用 `auth.install(app)`(`use` + `wsUse`)。
65
+
66
+ ## 换 store
67
+
68
+ ```ts
69
+ import { postgresAuthStore } from '@weifuwujs/auth/postgres'
70
+ const auth = createAuth({ store: postgresAuthStore(process.env.DATABASE_URL!) })
71
+ ```
72
+
73
+ 自定义 store 用契约套件自证(memory / sqlite / postgres 跑的是同一套):
74
+
75
+ ```ts
76
+ import { authStoreContract } from '@weifuwujs/auth/testing'
77
+ authStoreContract('my-store', () => myAuthStore())
78
+ ```
79
+
80
+ `AuthStore` 可拆(`UserStore` / `SessionStore` / `ResetTokenStore`),`listUsers?()` 为可选管理能力。
81
+
82
+ ## 换 hasher(存量 bcrypt 惰性迁移)
83
+
84
+ ```ts
85
+ import { scryptHasher } from '@weifuwujs/auth'
86
+ import { createAuth } from '@weifuwujs/auth'
87
+
88
+ const bcryptHasher = {
89
+ async hash(plain: string) {
90
+ return doBcryptHash(plain) // 你的实现
91
+ },
92
+ verify(plain: string, stored: string) {
93
+ if (stored.startsWith('$2')) return doBcryptCompare(plain, stored)
94
+ return scryptHasher.verify(plain, stored)
95
+ },
96
+ needsRehash: (stored: string) => stored.startsWith('$2'),
97
+ }
98
+
99
+ createAuth({ store, passwordHasher: bcryptHasher })
100
+ ```
101
+
102
+ 登录成功后 `needsRehash` 为真时自动用 `hash()` 重算并落库;`hash/verify` 允许同步或异步。
103
+
104
+ ## hooks
105
+
106
+ ```ts
107
+ createAuth({
108
+ store,
109
+ hooks: {
110
+ onRegister: (user) => analytics.track('signup', user.id),
111
+ onLogin: (user) => audit.log('login', user.id),
112
+ onPasswordChange: (user) => mailer.notify(user.email),
113
+ },
114
+ })
115
+ ```
116
+
117
+ hooks 被 awaited;抛错只记日志,**不改变请求成败**(外发邮件、统计不得拖垮登录)。
118
+ `sendResetToken(user, url)` 单独配置:未配置时 dev 打印链接、生产静默不发(防枚举)。
119
+
120
+ ## 侧表 profile(业务字段不进 users)
121
+
122
+ ```ts
123
+ // 业务字段放自己的表/服务,用示例中间件按需加载
124
+ const loadProfile: Middleware<Context, Context & { profile?: Profile }> = async (
125
+ req, ctx, next,
126
+ ) => next(req, { ...ctx, profile: await profiles.byUserId(ctx.auth?.user?.id) })
127
+
128
+ const app = createApp()
129
+ .use(auth.middleware)
130
+ .use(loadProfile)
131
+ .mount(auth)
132
+ ```
133
+
134
+ `users` 只存身份字段;`ctx.auth.user`(`PublicUser`)永不携带业务字段与 `passwordHash`。
135
+
136
+ ## CLI(不加载用户代码)
137
+
138
+ ```bash
139
+ weifuwu-auth migrate [--store sqlite:data/auth.db|postgres://…]
140
+ weifuwu-auth user:list
141
+ weifuwu-auth user:disable ada@example.com
142
+ weifuwu-auth user:reset-password ada@example.com [--password <plain>]
143
+ ```
144
+
145
+ `user:reset-password` 未指定明文时生成 24 字节随机密码并只打印一次。
146
+
147
+ ## 测试辅助(`/testing`)
148
+
149
+ 自定义 `AuthStore` 驱动与内置驱动(memory / sqlite / postgres)共跑同一套契约:
150
+
151
+ ```ts
152
+ import { authStoreContract } from '@weifuwujs/auth/testing'
153
+
154
+ authStoreContract('my-store', () => myAuthStore())
155
+ // 第三个参数可选:{ skip: true } 或 { skip: '未实现清理' }
156
+ ```
157
+
158
+ 契约覆盖:user CRUD 与唯一 email、session 生命周期与过期清理、reset token 单次消费与过期。
159
+
160
+ ## 安全默认清单
161
+
162
+ | 项 | 默认 |
163
+ | --- | --- |
164
+ | 密码 | scrypt(`scrypt$N$r$p$salt$hash`),可插拔 + 惰性重算 |
165
+ | 会话 | 只存 token 哈希;登录轮换;改密踢其他;disable/过期即时失效 |
166
+ | cookie | `HttpOnly` + `SameSite=Lax` + `Path=/` + `Max-Age`;`Secure` 按配置 |
167
+ | 防枚举 | 错误凭据统一文案;forgot 恒 200;时序均衡(dummy hash) |
168
+ | 限流 | 登录/注册/重置默认每 IP 每分钟 10 次(429 + retry-after) |
169
+ | Origin | 默认同源;表单 POST 与 JSON 同校验 |
170
+ | 重置 | token 单次、短 TTL(默认 15 分钟)、只存哈希 |
171
+
172
+ 边界:单租户;不做 OAuth / 2FA / 邮箱验证 / 审计事件(P2),不做支付与后台模板。
package/dist/auth.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { Context } from '@weifuwujs/weifuwu';
2
+ import type { Auth, AuthOptions } from './types.ts';
3
+ export declare function createAuth<C extends Context = Context>(options: AuthOptions): Auth;
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,OAAO,EAA8B,MAAM,oBAAoB,CAAA;AAOlF,OAAO,KAAK,EACV,IAAI,EAIJ,WAAW,EAMZ,MAAM,YAAY,CAAA;AA0MnB,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAyclF"}