index.js
5.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* `@drama/auth` 公共面 —— 身份认证域唯一对外导出口(任务文档 §3 契约)。
*
* 设计纪律(架构 §9 红线 + §1 边界):
* - 包内不读 `process.env`、不在模块顶层建任何连接(db 由 `createUserRepo(db)` 注入,
* 密钥/JWKS/OAuth client 全由注入的 Port 在 composition root 持有)。
* - 绝不自研密码学:JWT 签发/验证走 `tokenSigner`,OAuth 验签走 `oauth`,
* 密码哈希走 `hasher`。本包只编排这些 Port + 把会话/凭据/验证码状态落库。
* - 唯一对外错误类型是 `AuthError`(结构化 `code`/`retryable`)。
*
* ----- 注入式 Port 协议(实现在 composition root,本包只消费契约)-----
*
* @typedef {Object} TokenSigner 薄封装 jose。
* @property {(claims: object, opts: { ttlMs: number }) => string|Promise<string>} sign
* @property {(token: string) => object|Promise<object>} verify 验签 + 过期校验,失败抛错。
*
* @typedef {Object} Hasher 薄封装 argon2 / bcrypt。
* @property {(plain: string) => string|Promise<string>} hash
* @property {(plain: string, hash: string) => boolean|Promise<boolean>} verify
*
* @typedef {Object} OauthExchangeResult
* @property {string} provider_user_id
* @property {string} email
* @property {boolean} email_verified
*
* @typedef {Object} OauthPort 薄封装各家 SDK(含验签)。
* @property {(args: { provider: 'google'|'apple', code: string, oauth_flow: 'access_token'|'authorization_code', redirect_uri?: string }) => OauthExchangeResult|Promise<OauthExchangeResult>} exchange
*
* 来自 @drama/email 的投递端口(验证码投递)。
* @typedef {Object} EmailPort
* @property {(args: { to: string, code: string, purpose: string, locale?: string }) => void|Promise<void>} sendCode
*
* ----- 领域 DTO(与 @drama/contracts 的 User/Session 对齐)-----
*
* @typedef {Object} User
* @property {string} id
* @property {string} email
* @property {string} [password_hash]
* @property {Array<{ provider: string, provider_user_id: string }>} [oauth_providers]
* @property {string} [locale]
* @property {Date} [email_verified_at]
*
* @typedef {Object} Session
* @property {string} sid
* @property {string} user_id
* @property {string} refresh_hash
* @property {string} family
* @property {Date} expires_at UTC
* @property {boolean} [used]
* @property {boolean} [revoked]
*
* @typedef {Object} CodeRecord
* @property {string} email
* @property {string} purpose
* @property {string} code_hash
* @property {Date} expires_at UTC
* @property {number} attempts
*
* ----- UserRepo 形态(createUserRepo(db) 返回,本包内消费)-----
*
* @typedef {Object} UserRepo
* @property {(email: string) => Promise<User|null>} findByEmail
* @property {(id: string) => Promise<User|null>} findById
* @property {(args: { email: string, password_hash?: string, oauth_providers?: Array<{provider:string,provider_user_id:string}>, locale?: string, email_verified_at?: Date }) => Promise<User>} create
* @property {(args: { user_id: string, provider: string, provider_user_id: string }) => Promise<User>} linkOauth
* @property {(args: { user_id: string, password_hash: string }) => Promise<void>} updatePasswordHash
* @property {(args: { user_id: string, sid: string, refresh_hash: string, family: string, expires_at: Date }) => Promise<void>} saveSession
* @property {(args: { sid: string }) => Promise<Session|null>} findSession
* @property {(args: { sid: string }) => Promise<void>} markRefreshUsed
* @property {(args: { family: string }) => Promise<void>} revokeFamily
* @property {(args: { user_id: string }) => Promise<void>} revokeUserSessions
* @property {(args: { key: string, ip?: string, at: Date }) => Promise<void>} recordAttempt
* @property {(args: { key: string, ip?: string, windowMs: number }) => Promise<number>} countRecentAttempts
* @property {(args: { email: string, purpose: string, code_hash: string, expires_at: Date }) => Promise<void>} saveCode
* @property {(args: { email: string, purpose: string }) => Promise<CodeRecord|null>} consumeCode
* @property {(args: { email: string, purpose: string, attempts: number }) => Promise<void>} [bumpCodeAttempts]
*
* ----- Auth 形态(createAuth(...) 返回,全部返回 Promise)-----
*
* @typedef {Object} AuthTokens
* @property {User} user
* @property {string} access_token
* @property {string} refresh_token
*
* @typedef {Object} Auth
* @property {(args: { email: string, password: string, locale?: string, email_verified_at?: Date }) => Promise<AuthTokens>} register
* @property {(args: { email: string, password: string, ip?: string, device_id?: string }) => Promise<AuthTokens>} login
* @property {(args: { provider: 'google'|'apple', code: string, redirect_uri?: string, device_id?: string }) => Promise<AuthTokens & { is_new_user: boolean }>} oauthLogin
* @property {(args: { refresh_token: string }) => Promise<{ user: User|{id:string}, access_token: string, refresh_token: string }>} refresh
* @property {(args: { email: string, code: string, purpose: 'register'|'login'|'reset' }) => Promise<{ ok: true }>} verifyCode
* @property {(args: { email: string, purpose: 'register'|'login'|'reset', locale?: string, ip?: string }) => Promise<{ ok: true }>} sendCode
* @property {(args: { email: string, locale?: string, ip?: string }) => Promise<{ ok: true }>} requestPasswordReset
* @property {(args: { email: string, password: string }) => Promise<{ ok: true }>} resetPassword
* @property {(args: { user_id: string }) => Promise<User|null>} getUser
* @property {(token: string) => Promise<object>} verifyAccessToken
* @property {(args: { refresh_token: string }) => Promise<{ ok: true }>} logout
* @property {(args: { user_id: string }) => Promise<{ ok: true }>} revokeAllSessions
*/
export { createAuth } from './auth.js'
export { createUserRepo } from './repo.js'
export { AuthError } from './errors.js'
export { createFakeUserRepo } from './fake-repo.js'
export { default as authDbSchema } from './db/tables/index.cjs'