Skip to content

docs: add IDENTITY_MODEL.md#23

Closed
Copilot wants to merge 3 commits intomainfrom
copilot/add-identity-model-documentation
Closed

docs: add IDENTITY_MODEL.md#23
Copilot wants to merge 3 commits intomainfrom
copilot/add-identity-model-documentation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 15, 2026

Identra lacked a single authoritative reference for its identity model, making it hard to reason about merge rules, conflict semantics, and error codes without reading the source.

What's included

  • Definitions — User record schema, External Identity (OAuth), Email Identity, Phone Identity (planned)
  • Uniqueness & normalization — global uniqueness rules for email/provider IDs; email is trimmed but case-preserved; planned E.164 for phone
  • Verification semantics — provider email trusted as-is; email-code = consumed OTP; password = bcrypt; SMS planned
  • Flow diagrams + decision tables for every auth path:
    • OAuth login (ensureOAuthUser: find by provider ID → auto-merge by email → create)
    • OAuth bind (idempotency, conflict cases, no email-match requirement)
    • Email-code login (account created only after code is consumed)
    • RegisterByPassword vs LoginByPassword — explicit separation, NOT_FOUND/FAILED_PRECONDITION distinctions documented
    • Phone/SMS (planned semantics)
  • Conflict matrix — 14 scenarios mapped to gRPC status + HTTP status
  • Rate limiting — default values, config keys, fail-open behaviour for both login and send-code limiters
  • Security notes — auto-merge trust model, user enumeration trade-off, multi-replica OAuth state caveat, token TTLs, email code properties
Original prompt

Create an English identity model documentation file for Identra.

Context:

  • Repo: poly-workshop/identra (default branch main)
  • Identra supports OAuth login + account binding, email-code login, password login, and will likely add phone login.
  • Recent changes introduced explicit RegisterByPassword vs LoginByPassword, and added rate limiting/brute-force protection.

Task:

  1. Add a new documentation file at docs/IDENTITY_MODEL.md.
  2. Document the identity model and rules clearly and unambiguously, covering:
    • Definitions: User, External Identity (provider + provider_user_id), Email identity, Phone identity.
    • Uniqueness constraints (global uniqueness for provider identities, emails, phone numbers).
    • Normalization rules (email lowercasing/trim; phone E.164).
    • Verification semantics: what "verified" means for provider emails vs email-code vs sms-code.
    • Flows & decision tables:
      • OAuth login flow: only auto-merge when chosen provider email is verified; choose verified+primary first; only one chosen email drives merge.
      • Bind flow: manual binding does not require matching email; handle conflicts; idempotency rules.
      • Email-code login: create user if not found; only after code verification.
      • Password login + registration semantics.
      • (Planned) Phone/SMS login: analogous to email-code.
    • Conflict matrix and expected gRPC/HTTP error codes.
    • Security notes: why merge is limited, user enumeration considerations, etc.
  3. Ensure the doc matches the repository’s current API surface (e.g., /password/register and /password/login endpoints exist).

Acceptance criteria:

  • File exists and is well-formatted Markdown.
  • Rules are explicit enough to implement tests from.
  • Includes concise examples and at least two decision tables.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: Identra 值得继续提升的地方

Assistant: 可以,Identra 仍然很有继续提升的空间。如果你是想评估 poly-workshop/identra 这个仓库“接下来还能优化什么”,我会优先从这几个方向看:

1. 产品定位是否足够清晰

先问三个问题:

  • 它解决的是不是一个足够明确的问题?
  • 目标用户是谁?
  • 跟现有方案相比,优势是什么?

如果仓库首页、文档、示例不能让人 30 秒内明白这些,第一优先级就是补强 README、示例、使用场景、对比说明

2. 文档和上手体验

很多项目不是技术不行,而是别人用不起来。可重点检查:

  • README 是否有快速开始
  • 安装步骤是否完整
  • 是否有最小可运行示例
  • 配置项是否解释清楚
  • 常见错误是否有 FAQ

3. 代码结构与可维护性

值得提升的典型点:

  • 模块职责是否清晰
  • 命名是否统一
  • 是否存在重复代码
  • 核心逻辑是否易测
  • 配置、常量、工具函数是否分层合理

如果项目已经能跑,但后续开发很慢,通常瓶颈就在这里。

4. 测试覆盖

如果测试少,后续迭代成本会越来越高。建议看:

  • 核心逻辑有没有单元测试
  • 关键流程有没有集成测试
  • CI 是否自动跑测试
  • 是否覆盖异常路径和边界条件

5. 工程化能力

一个项目是否“成熟”,往往看工程细节:

  • lint / format 是否统一
  • CI/CD 是否稳定
  • release 流程是否明确
  • versioning 是否规范
  • 是否有环境变量管理、日志、错误处理

6. 性能与稳定性

如果 Identra 有身份识别、检索、匹配、推理之类能力,这部分尤其关键:

  • 响应时间
  • 并发下表现
  • 内存占用
  • 缓存策略
  • 错误恢复能力
  • 可观测性(日志、指标、trace)

7. 安全与权限设计

如果涉及 identity / auth / 用户数据,这一块优先级很高:

  • 鉴权是否严谨
  • 敏感信息是否泄露
  • token / secret 管理是否规范
  • 输入校验是否充分
  • 是否有权限边界测试

8. 社区与协作友好度

如果你希望它持续发展,而不只是“自己能用”:

  • 是否有 CONTRIBUTING
  • 是否有 issue / PR 模板
  • 是否有 roadmap
  • 是否有明确 license
  • 是否方便新贡献者理解和参与

如果你想要更具体一点,我会把“值得继续提升的地方”分成这 3 个优先级:

P0:最先做

  • README/文档补齐
  • 快速开始示例
  • 测试和 CI
  • 核心模块重构,降低维护成本

P1:接着做

  • 错误处理和日志
  • 配置管理
  • 性能优化
  • API/接口稳定性

P2:长期做

  • 插件化/扩展性
  • 更完善的贡献流程
  • roadmap 和版本规划
  • 社区运营与生态整合

如果你愿意,我下一步可以直接基于 poly-workshop/identra 仓库做一次具体诊断,例如:

  1. 看 README 和目录结构,
  2. 分析它目前最明显的 5 个短板,
  3. 给出一份按优先级排序的改进建议。

如果要的话,我可以直接开始。

User: 开始

Assistant: 我已经开始看了。当前最值得继续提升的,是测试与CI、认证语义、安全边界、文档完整度,以及服务拆分。

下面是基于 poly-workshop/identra 当前仓库状态给出的第一次具体诊断


先给结论:我会按这个优先级改

P0:最该先做

  1. 补上自动化测试执行流程
  2. 修正“登录即注册”的认证语义
  3. 加强认证与反滥用安全措施
  4. 补全生产部署与配置文档

P1:接着做

  1. 拆分 service.go,降低维护成本
  2. 补集成测试/端到端测试
  3. 统一 OAuth 抽象,别让 GitHub 成为硬编码中心

P2:长期优化

  1. 加入可观测性:审计日志、指标、错误分类
  2. 完善贡献者体验与版本发布规范
  3. 为多租户/多 provider 扩展预留设计

1) CI 太薄,只有 lint,没有 test

这是我认为最明显的短板之一。

仓库现在有两个 workflow:

  • lint.yml
  • docker.yml

lint.yml 只跑 golangci-lint,没有 go test
而且 docker.yml 只在打 tag 或手动触发时构建镜像。也就是说,当前主分支和 PR 并没有通过 CI 持续验证核心行为是否正确

name: Lint

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Run golangci-lint
        uses: golangci/golangci-lint-action@v9

为什么这是问题

项目本身已经有一些测试文件,例如:

  • internal/infrastructure/security/tokens_test.go
  • internal/infrastructure/persistence/gorm/db_test.go
  • internal/application/identra/service_oauth_email_test.go
  • internal/application/identra/service_ensure_oauth_user_test.go

说明你们已经有“测试意识”,但没有把它接到质量闸门上。这会导致:

  • PR 合并前无法自动拦回归
  • 新增功能时容易把认证流程搞坏
  • token、oauth、email code 这种高风险逻辑缺少持续验证

建议

直接加:

  • go test ./...
  • 测试覆盖率上传或最低门槛
  • 对关键包单独跑 race 检查
  • PR 必跑 test + lint

2) “登录即注册”的语义需要重新审视

这点非常值得尽快明确,因为它不只是代码问题,更是产品与安全边界问题

LoginByPassword 里,如果用...

This pull request was created from Copilot chat.

Copilot AI changed the title [WIP] Add identity model documentation file for Identra docs: add IDENTITY_MODEL.md Apr 15, 2026
Copilot AI requested a review from slhmy April 15, 2026 08:07
@slhmy slhmy marked this pull request as ready for review April 15, 2026 08:13
@slhmy slhmy requested a review from Copilot April 15, 2026 08:13
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new authoritative identity-model document to make Identra’s auth/merge/conflict semantics easier to understand without reading service code.

Changes:

  • Introduces docs/IDENTITY_MODEL.md describing identities, uniqueness/normalization, verification semantics, and auth flows.
  • Documents conflict scenarios with expected gRPC/HTTP codes.
  • Documents rate limiting defaults and security considerations.

Comment thread docs/IDENTITY_MODEL.md
Comment on lines +35 to +42
| `email` | string | The user's canonical email address. May be empty for OAuth-only accounts created without an email. Globally unique when non-empty. |
| `hashed_password`| string (nullable) | bcrypt hash of the user's password. `null` means password login is not set up for this account. |
| `github_id` | string (nullable) | Provider user ID for the GitHub OAuth identity. Globally unique when non-empty. |
| `last_login_at` | timestamp (nullable) | Updated on every successful authentication. |
| `created_at` | timestamp | Set on creation. |
| `updated_at` | timestamp | Updated on every write. |

A user record may exist with **no email** (OAuth-only user whose provider did not supply one) or with an email but **no password** (email-code-only or OAuth-only account).
Comment thread docs/IDENTITY_MODEL.md
Comment on lines +354 to +371
| Parameter | Default | Config key |
|------------------------|---------|------------------------|
| Max failed attempts | 5 | `LoginMaxAttempts` |
| Lockout window | 15 min | `LoginLockoutDuration` |

The counter key is the **email address** being authenticated. On each **failed** attempt the counter is incremented. On a **successful** login the counter is reset. The limiter is checked before any credential verification; if the limit is already exceeded the request is rejected immediately with `RESOURCE_EXHAUSTED`.

The limiter fails **open**: if Redis is unavailable the check is skipped and login proceeds normally. This prioritizes availability over perfect rate-limit enforcement.

### Send-Code Rate Limiter

Applied to: `POST /email/code`

| Parameter | Default | Config key |
|------------------------|---------|-------------------------|
| Max sends per window | 5 | `SendCodeMaxAttempts` |
| Window duration | 1 hour | `SendCodeWindow` |

Comment thread docs/IDENTITY_MODEL.md
| Provider name | Lowercased and whitespace-stripped. | `" GitHub "` → `"github"` |
| Phone number | (Planned) Normalized to E.164 format. | `"(555) 867-5309"` → `"+15558675309"` |

> **Note on email case:** The current implementation stores and matches email addresses exactly as normalized (trimmed, original case). Two addresses that differ only in case (e.g., `alice@example.com` vs `Alice@example.com`) are treated as **different** identities. Callers should canonicalize email addresses to lowercase before submitting them if case-insensitive uniqueness is required.
Comment thread docs/IDENTITY_MODEL.md
Comment on lines +216 to +219
3. Generate a cryptographically random 6-digit code (zero-padded).
4. Store `(email → code)` in Redis with a 10-minute TTL (overwrites any previous code).
5. Send the code to the email address via SMTP.
6. Record the send attempt in the rate limiter.
Comment thread docs/IDENTITY_MODEL.md
| `user.github_id` | Global | Unique when non-empty. One GitHub account maps to at most one User. |
| Phone number | Global | (Planned) Unique when non-empty, stored in E.164 format. |

Uniqueness is enforced at the database layer (unique index). The application layer checks for existing records before insert and maps duplicate-key errors to `ALREADY_EXISTS` gRPC status.
Comment thread docs/IDENTITY_MODEL.md
Comment on lines +102 to +107
| OAuth provider email | The email address returned by the OAuth provider is accepted as verified **by the provider**. Identra trusts it without additional checks. | Provider asserts the email in the user-info response. |
| Email-code (`/email/login`) | The user controls the inbox for the submitted email address. | A 6-digit one-time code (valid 10 minutes) is sent and must be consumed to complete login. |
| Password (`/password/login`) | The user knows the password stored for that email address. | bcrypt hash comparison. |
| SMS-code (planned) | The user controls the phone number. | A one-time code sent via SMS must be consumed. |

A provider email used in the OAuth auto-merge flow (Section 5.1) is treated as verified by the provider; no additional Identra-level email verification is performed.
Comment thread docs/IDENTITY_MODEL.md
Comment on lines +143 to +151
| Provider ID found? | Email provided? | Email matches existing user? | Action | Result |
|--------------------|-----------------|------------------------------|------------------------------------------------------------------------|-------------------------------|
| Yes | (any) | (any) | Update user's email to provider email if it has changed (non-empty email only). | Existing user returned. |
| No | Yes | Yes | Link provider ID to the existing email-matched user (auto-merge). | Existing user returned. |
| No | Yes | No | Create new user with `email` + provider ID. | New user created and returned.|
| No | No | N/A | Create new user with provider ID only; email is empty. | New user created and returned.|

**Auto-merge rule:** When a provider presents an email address that already belongs to a local user, Identra automatically links the provider identity to that local user *without asking the user to confirm*. This relies entirely on the provider having verified the email. See [Section 8](#8-security-notes) for the security rationale and limitations.

Comment thread docs/IDENTITY_MODEL.md
Comment on lines +348 to +349
Identra uses a Redis-backed sliding-window counter for two independent rate limits.

Comment thread docs/IDENTITY_MODEL.md
Comment on lines +396 to +399
| Token | Default TTL | Config key |
|--------------|-------------|----------------------------------|
| Access token | 15 minutes | `AccessTokenExpirationDuration` |
| Refresh token| 7 days | `RefreshTokenExpirationDuration` |
@slhmy slhmy closed this Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants