feat(auth): bulk import defaults to auto — invite per row, temporary only for undeliverable rows (#3236)#3307
Merged
Merged
Conversation
…y only for undeliverable rows (#3236) The identity bulk-import endpoint (`POST /api/v1/auth/admin/import-users`) gains a fourth `passwordPolicy`, `auto`, and it is now the default (was `none`). `auto` decides PER ROW instead of forcing one policy on the whole batch: - a row with a deliverable channel (real email + wired email service, or phone + wired SMS-invite) is INVITED — no shared secret leaves the server; - a row with no deliverable channel (placeholder email, phone-only without SMS, or an email row when no email service is wired) falls back to a temporary password, returned once with `must_change_password` stamped. This shrinks the temporary-password blast radius from "the whole batch" to "only the rows that genuinely can't be reached", and — unlike `invite` — `auto` never rejects the request for missing infrastructure (undeliverable rows just degrade). The per-row outcome is surfaced on `rows[].delivery` (email/sms/temporary) with a batch breakdown on `summary.delivery` and in the run audit. The write path is refactored to a single per-row plan (resolved in pre-validation, keyed by identity so it survives the coerced-row copy handed to createData) and one unified post-write pass driven by the invite/temporary maps rather than a global policy branch — so the interleaved `auto` batch and the three fixed policies share one code path. `invite`/`temporary`/`none` behave exactly as before. Behavior change: callers that OMIT `passwordPolicy` now get `auto` (proactive invitations / temporary fallback) instead of `none` (identity only). Pass `passwordPolicy: 'none'` explicitly for the old identity-only behavior. Every explicit-policy call is unaffected; the response is a strict superset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3236.
背景 / 前提更正
Issue #3236 的前提是「默认策略是
temporary」,但实际默认早已是none(#2820 改的:导入只供给身份、不发凭证、不分发任何秘密)。所以「危险默认」这一层其实已经不存在了。Issue 里仍然成立的价值:当管理员确实想在导入时发凭证时,
temporary是个「整批一刀切」的锤子——一批里既有可投递又有不可投递的行时,invite会把不可投递的行整个 fail 掉,temporary又会给所有行(包括本可安全 invite 的行)都发临时密码。#3236 的真正提案是逐行决策。改动
新增第四种
passwordPolicy——auto,并设为新默认(原为none)。auto逐行决定:temporary,临时密码在响应里返回一次并盖must_change_password。这把临时密码的爆炸半径从「整批」收缩到「真没渠道的少数行」;而且和
invite不同,auto永不因缺基建而拒绝请求(无渠道的行只是降级)。逐行结果挂在rows[].delivery(email/sms/temporary),整批分布挂在summary.delivery,并写入运行审计。三个原有策略行为完全不变,仍可显式选择:
invite(逐行 fail 不可投递行,永不降级)/temporary(整批临时密码)/none(仅身份)。重构
写入路径统一成:预校验时算好逐行
RowPlan(按身份 key,因为createData拿到的是 coerce 过的行副本,不能靠对象引用),写后用一个统一遍历(靠 invite / temporary 两个 map 驱动,不再按全局 policy 分支)。于是auto的混合批次和三个固定策略共用一条代码路径。省略
passwordPolicy的调用方,现在拿到的是auto(默认会主动外发邀请 / 返回临时密码),不再是none。要恢复「只建身份」的老行为需显式传passwordPolicy: 'none'。已显式传策略的调用方不受影响,响应是严格超集(新增delivery字段)。按@objectstack/plugin-auth的minor发。未纳入(单独跟进)
Issue 标注为「先行止血项」的那个 bug(
temporary盖must_change_password失败时只 warn、仍以 created+临时密码 静默返回)本 PR 未修——Issue 明确说单独修。注意:auto成为默认后,该静默失败现在落在了默认路径上,值得尽快单独补一个 PR。测试
auto测试(逐行分流 / SMS 邀请 / 无渠道全降级 / 邀请失败不回滚 / dryRun 不投递),并更新默认策略测试。admin-import-users.test.ts26 项全绿;plugin-auth 全量 465 项全绿;DTS 类型检查过;eslint 干净。🤖 Generated with Claude Code