template-monorepo/docs/identity-member-design.md

304 lines
12 KiB
Markdown
Raw Normal View History

# Identity / Member / Permission <20><><EFBFBD>Ҳլ[<5B>c
2026-05-19 13:56:59 +00:00
Gateway <20>T<EFBFBD>ӷ~<7E>ȼҲա]**auth** / **member** / **permission**<EFBFBD>^<5E>P<EFBFBD>~<7E><> **ZITADEL**<EFBFBD>]<5D><><EFBFBD><EFBFBD><EFBFBD>^<5E>B**LDAP**<2A>]<5D><><EFBFBD>~<7E>ؿ<EFBFBD><D8BF>^<5E>B**SCIM 2.0**<2A>]<5D><><EFBFBD>~ provisioning<6E>^<5E><><EFBFBD><EFBFBD><EFBFBD>X<EFBFBD>`<60><><EFBFBD>C**<2A><><EFBFBD>e<EFBFBD>u<EFBFBD>͸<EFBFBD><CDB8>Ҳռh<D5BC><68><EFBFBD><EFBFBD><EFBFBD>]<5D>p<EFBFBD>M<EFBFBD><4D><EFBFBD>P<EFBFBD><50><EFBFBD>p**<2A>F<EFBFBD>Ҳդ<D2B2><D5A4><EFBFBD><EFBFBD>ЬݡG
2026-05-19 13:56:59 +00:00
| <20>Ҳ<EFBFBD> | <20><><EFBFBD><EFBFBD> |
|------|------|
| auth | [`internal/model/auth/README.md`](../internal/model/auth/README.md) |
| member | [`internal/model/member/README.md`](../internal/model/member/README.md) |
| permission | [`internal/model/permission/README.md`](../internal/model/permission/README.md) |
| notification | [`internal/model/notification/README.md`](../internal/model/notification/README.md) |
2026-05-19 13:56:59 +00:00
<EFBFBD><EFBFBD><EFBFBD>h<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>G[`docs/model.md`](./model.md)<29>C
2026-05-19 13:56:59 +00:00
---
## 1. <20>]<5D>p<EFBFBD>ؼ<EFBFBD>
2026-05-19 13:56:59 +00:00
| <20>ؼ<EFBFBD> | <20><><EFBFBD><EFBFBD> |
2026-05-19 13:56:59 +00:00
|------|------|
| <20>Τ@<40><><EFBFBD><EFBFBD> | ZITADEL <20><> IdP<64>]OIDC<44>BLDAP IdP<64>BSocial Login<69>^ |
| <20>~<7E>ȷ|<7C><> | Gateway `member` <20><> tenant-scoped profile |
| <20>Ӳɫױ<C9AB><D7B1>v | Gateway `permission`<EFBFBD>]Casbin RBAC + Permission Tree<65>^<5E>FB2B <20><><EFBFBD><EFBFBD><EFBFBD>i<EFBFBD>۩w<DBA9>q Role <20>äĿ<C3A4> Permission |
| <20><><EFBFBD>~ Token | Gateway <20><>ñ CloudEP JWT<57>FZITADEL OIDC token <20>u<EFBFBD>b `/auth/token/exchange` <20>Τ@<40><> |
| <20>W<EFBFBD><57> | <20><><EFBFBD><EFBFBD><EFBFBD>x 100 <20>U+ <20>|<7C><><EFBFBD>F<EFBFBD><EFBFBD><E6AFB2><EFBFBD>i<EFBFBD>F 50 <20>U |
| UID | <20>H<EFBFBD><48><EFBFBD><69>a<EFBFBD><61><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD><65><EFBFBD>]<5D>p `ACME-10000003`<EFBFBD>^<5E>F<EFBFBD>ߤ@<40><> `(tenant_id, uid)` |
---
2026-05-19 13:56:59 +00:00
## 2. <20>֤߭<D6A4><DFAD>h
2026-05-19 13:56:59 +00:00
1. **¾<EFBFBD>d<EFBFBD><EFBFBD><EFBFBD><EFBFBD>**
- `auth`<EFBFBD>G<EFBFBD>A<EFBFBD>O<EFBFBD>֡]Authentication<6F>^
- `member`<EFBFBD>G<EFBFBD>A<EFBFBD><EFBFBD><EFBFBD>~<7E>ȸ<EFBFBD><C8B8>ơ]Profile<6C>^
- `permission`<EFBFBD>G<EFBFBD>A<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>]Authorization<6F>^
2026-05-19 13:56:59 +00:00
2. **LDAP <20><><EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>J bind**
<20>n<EFBFBD>J<EFBFBD><4A><EFBFBD>Ҥ@<40>ߨ<EFBFBD> ZITADEL LDAP IdP<64>FGateway LDAP client <20>u<EFBFBD><75> Directory Sync<6E>]read-only<6C>^<5E>C
2026-05-19 13:56:59 +00:00
3. **<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>j<EFBFBD><EFBFBD>**
<20>Ҧ<EFBFBD><D2A6><EFBFBD><EFBFBD>[<5B>Ƹ<EFBFBD><C6B8>ƥH `tenant_id` <20><><EFBFBD><EFBFBD><EFBFBD>ɡFJWT `tenant_id` <20>P<EFBFBD>ШD<D0A8><EFBFBD><EAB7BD><EFBFBD>@<40>P <20><> 403<30>C
4. **B2B <20>v<EFBFBD><76><EFBFBD>۩w<DBA9>q**
- <20><><EFBFBD>x seed <20><><EFBFBD><EFBFBD> Permission Tree<65>]<5D>t `http_path` + `http_method`<EFBFBD>^
- <20><><EFBFBD><EFBFBD><EFBFBD>ئۭq Role<6C>A<EFBFBD>q Tree **<EFBFBD>Ŀ<EFBFBD>** Permission<6F>]<5D>۰ʸ<DBB0> parent<6E>^
- Casbin <20>H `(tenant_id, role_key, path, method)` <20><><EFBFBD><EFBFBD><EFBFBD>A<EFBFBD>󯲤<EFBFBD><F3AFB2A4>P<EFBFBD>W<EFBFBD><57><EFBFBD><EFBFBD><E2A4AC><EFBFBD>ìV
2026-05-19 13:56:59 +00:00
5. **<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> vs <20>~<7E><><EFBFBD><EFBFBD><EFBFBD>Ҥ<EFBFBD><D2A4>h**
| <20>ŧO | <20>ѽ֭t<D6AD>d | <20>d<EFBFBD><64> |
|------|---------|------|
| <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> | ZITADEL Org Policy | <20>n<EFBFBD>J MFA<46>B<EFBFBD><42><EFBFBD>U email<69>B<EFBFBD>ѰO<D1B0>K<EFBFBD>X<EFBFBD>B<EFBFBD>b<EFBFBD><62><EFBFBD><EFBFBD><EFBFBD>w |
| <20>~<7E>ȯ<EFBFBD> | Gateway `member` | <20>~<7E><> email/phone OTP<54>BTOTP step-up |
Gateway **<EFBFBD><EFBFBD>**Ū `ZITADEL.email_verified` <20><><EFBFBD>~<7E>Ȧu<C8A6><75><EFBFBD>F<EFBFBD><46>Ū `member.business_email_verified`<EFBFBD>C
2026-05-19 13:56:59 +00:00
6. **UseCase <20><><EFBFBD>l<EFBFBD><6C>**
UseCase <20><> atomic primitive<76>A<EFBFBD>T<EFBFBD><EFBFBD>۩I<DBA9>s<EFBFBD>F<EFBFBD><46><EFBFBD><EFBFBD><EFBFBD>l<EFBFBD>y<EFBFBD>{<7B>]OTP <20><> <20>H<EFBFBD>H <20><> <20><><EFBFBD>X <20><> flip flag<61>^<5E>@<40>ߦb `internal/logic/<module>/` <20>s<EFBFBD>ơC
2026-05-19 13:56:59 +00:00
---
## 3. <20>Ҳը̿<D5A8><CCBF><EFBFBD><EFBFBD>V
2026-05-19 13:56:59 +00:00
2026-05-19 17:04:26 +00:00
```
handler <20><> logic <20><> model/{module}/domain/usecase<73>]<5D><><EFBFBD><EFBFBD><EFBFBD>^
<20><>
model/{module}/repository<72>]<5D><><EFBFBD><EFBFBD><EFBFBD>^
<20><>
MongoDB / Redis
2026-05-19 17:04:26 +00:00
logic <20>i<EFBFBD>P<EFBFBD>ɩI<C9A9>s<EFBFBD>h<EFBFBD><68> model <20><> usecase<73>]<5D>s<EFBFBD>ơ^
usecase <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̿<EFBFBD>
auth/logic <20><> member.usecase<73>]CreateUnverified / Activate / EnsureFromOIDC<44>^
auth/logic <20><> permission.usecase<73>]SyncRolesFromClaims<6D>A<EFBFBD><41><EFBFBD>ӡ^
auth/logic <20><> notification.Notifier<65>]OTP / <20><><EFBFBD>U<EFBFBD>H<EFBFBD>^
member/logic <20><> notification.Notifier<65>]<5D>~<7E><><EFBFBD><EFBFBD><EFBFBD>ҡBstep-up OTP<54>^
permission <20><> <20>W<EFBFBD>ߡA<DFA1><41><EFBFBD>ϦV<CFA6>̿<EFBFBD>
2026-05-19 13:56:59 +00:00
```
---
## 4. <20>~<7E><><EFBFBD>t<EFBFBD>Τ<EFBFBD><CEA4>u
2026-05-19 13:56:59 +00:00
| <20><><EFBFBD>O | ZITADEL | auth | member | permission | notification |
|------|---------|------|--------|------------|---------------|
| OIDC / Social Login | ? | callback <20><> JWT | EnsureFromOIDC | SyncRoles | <20>X |
| <20><><EFBFBD>x Email + Password <20><><EFBFBD>U | <20><> user<65>]local<61>^ | logic <20>s<EFBFBD><73> | LifecycleUseCase | <20>X | <20>H OTP |
| <20><><EFBFBD><EFBFBD> MFA <20>j<EFBFBD><6A> | ? Org Policy | <20>X | <20>X | <20>X | <20>X |
| LDAP <20>n<EFBFBD>J | ? LDAP IdP | <20>X | <20>X | Group<75><70>Role | <20>X |
| <20><><EFBFBD>~ JWT | <20>X | ? CloudEP | <20>X | <20>X | <20>X |
| <20>~<7E><> TOTP (Authenticator) | <20>X | <20>X | ? AES-GCM <20>s secret | <20>X | <20>X |
| <20>~<7E><> Email/Phone OTP | <20>X | <20>X | ? OTPUseCase | <20>X | ? <20>ҪO + <20>o<EFBFBD>e |
| API <20>Ӳɫ<D3B2><C9AB>v<EFBFBD><76> | <20><> Role | <20>X | <20>X | ? Casbin | <20>X |
| SCIM Users/Groups | <20>i<EFBFBD>P<EFBFBD>B | <20>X | <20>~<7E>ȼg<C8BC>J | Group<75><70>Role | <20>X |
| Directory Sync | <20>X | <20>X | Worker | Group<75><70>Role | <20>P<EFBFBD>B<EFBFBD><42><EFBFBD>`<60>iĵ |
2026-05-19 17:04:26 +00:00
### 4.1 <20><><EFBFBD><EFBFBD> ? ZITADEL Org
2026-05-19 17:04:26 +00:00
```
1 CloudEP Tenant = 1 ZITADEL Organization = 1 <20><><EFBFBD>ƹj<C6B9><6A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2026-05-19 17:04:26 +00:00
```
| <20><><EFBFBD><EFBFBD> | <20>ӷ<EFBFBD> | <20>γ~ |
2026-05-19 17:04:26 +00:00
|------|------|------|
| `tenant_id` | Gateway <20><><EFBFBD><EFBFBD> | <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> / <20><><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD> |
| `tenant.org_id` | ZITADEL `org_id` | <20><><EFBFBD><EFBFBD><EFBFBD>t<EFBFBD>ι<EFBFBD><CEB9><EFBFBD> |
| `member.zitadel_user_id` | ZITADEL `sub` | <20><><EFBFBD><EFBFBD><EFBFBD>M<EFBFBD>g |
| `member.uid` | `UIDGenerator` | <20>~<7E>ȥiŪ<69>D<EFBFBD><44> |
2026-05-19 17:04:26 +00:00
### 4.2 Tenant <20>إߡ]Saga<67>^
2026-05-19 17:04:26 +00:00
```
1. POST /api/v1/admin/tenants
<20><> Mongo upsert tenants { status: provisioning, org_id: "" }
2. ZITADEL Mgmt.CreateOrganization(slug)
<20><> org_id
3. UPDATE tenants { org_id, status: active }
4. seed system roles + Casbin reload
<EFBFBD><EFBFBD><EFBFBD>Ѹ<EFBFBD><EFBFBD>v<EFBFBD>]cron <20>C 5 <20><><EFBFBD><EFBFBD><EFBFBD>^<5E>G
- <20>B<EFBFBD>J 2 <20><><EFBFBD><EFBFBD> <20><> status=failed<65>A<EFBFBD><41><EFBFBD>ưh<C6B0>סA3 <20><><EFBFBD><EFBFBD><EFBFBD>
- <20>B<EFBFBD>J 3 <20><><EFBFBD><EFBFBD> <20><> status=orphan_zitadel_org<72>A<EFBFBD><41><EFBFBD>y<EFBFBD>ɸj
2026-05-19 17:04:26 +00:00
```
### 4.3 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2026-05-19 17:04:26 +00:00
| <20><><EFBFBD><EFBFBD> | <20>n<EFBFBD>J | LDAP | <20>v<EFBFBD><76> |
|------|------|------|------|
| **B2C** | Email / Social | <20>X | <20>t<EFBFBD>ιw<CEB9>] Role<6C>]<5D><>Ū<EFBFBD>ҪO<D2AA>^ |
| **B2B** | ZITADEL <20><> LDAP IdP | ? | <20><><EFBFBD><EFBFBD><EFBFBD>۩w<DBA9>q Role + Permission |
| **Hybrid** | Social + LDAP | ? | B2B <20>۩w<DBA9>q<EFBFBD>F<EFBFBD>~<7E><><EFBFBD>Ȥ<EFBFBD><C8A4><EFBFBD> B2C <20>ҪO |
2026-05-19 13:56:59 +00:00
---
## 5. UID <20>]<5D>p
2026-05-19 13:56:59 +00:00
| <20>W<EFBFBD>h | <20><> |
|------|----|
| <20>榡 | `{TenantUIDPrefix}-{Sequence}` |
| Prefix | 2~4 <20>Ӥj<D3A4>g<EFBFBD>r<EFBFBD><72><EFBFBD>]Tenant <20>إ߮ɳ]<5D>A<EFBFBD><41><EFBFBD><EFBFBD><EFBFBD>ߤ@<40>^ |
| <20>_<EFBFBD>l<EFBFBD>Ǹ<EFBFBD> | **10,000,000**<EFBFBD>]7 <20><><EFBFBD>_<EFBFBD><5F><EFBFBD>A<EFBFBD>קK `ACME-1` <20>o<EFBFBD>صu UID<49>^ |
| <20>ߤ@<40><> | `(tenant_id, uid)` |
| <20>d<EFBFBD><64> | `ACME-10000003` |
2026-05-19 13:56:59 +00:00
<EFBFBD><EFBFBD><EFBFBD>@<40>G`member.UIDGenerator` <20><> Redis `INCR member:seq:{tenant}`<EFBFBD>F<EFBFBD><EFBFBD><EFBFBD><EFBFBD> INCR=1 <20>ɦ۰<C9A6> `INCRBY 9_999_999` <20>ɨ<EFBFBD><C9A8>_<EFBFBD><5F><EFBFBD>ȡC
2026-05-19 13:56:59 +00:00
---
## 6. Permission <20>ҫ<EFBFBD>
2026-05-19 13:56:59 +00:00
```
<EFBFBD><EFBFBD><EFBFBD>x<EFBFBD>h (Platform-wide) <20><><EFBFBD><EFBFBD><EFBFBD>h (per-tenant)
<EFBFBD>z<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>{ <20>z<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>{
<EFBFBD>x Permission <20>x <20>w<EFBFBD>w <20>Ŀ<EFBFBD> <20>w<EFBFBD>w<EFBFBD><77> <20>x Role <20>x
<EFBFBD>x Catalog (Tree) <20>x <20>x <20>x
<EFBFBD>|<7C>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>} <20>x <20><> <20><><EFBFBD><EFBFBD> <20>x
<20>x UserRole<6C>x
<20>x <20>x
<20>x <20><> <20><><EFBFBD>M <20>x
<20>xRoleMap <20>x
<20>|<7C>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>w<EFBFBD>}
<20><> LoadPolicy
Casbin Enforcer (Redis adapter)
<20><>
CasbinRBAC Middleware
2026-05-19 13:56:59 +00:00
```
| <20><><EFBFBD><EFBFBD> | <20><><EFBFBD><EFBFBD> |
|------|------|
| Permission | <20><><EFBFBD>x<EFBFBD>Ÿ`<60>I<EFBFBD>]<5D><><EFBFBD><EFBFBD> dot notation<6F>^<5E>C**Name <20><><EFBFBD>i<EFBFBD><69>**<2A>F<EFBFBD>o<EFBFBD><6F><EFBFBD><EFBFBD> `status=close` |
| Role | <20><><EFBFBD><EFBFBD><E1A4BA><EFBFBD><EFBFBD><EFBFBD>A`(tenant_id, key)` unique<75>F**Key <20><><EFBFBD>i<EFBFBD><69>**<2A>]<5D>~<7E><> IdP mapping <20><><EFBFBD><EFBFBD><EFBFBD>j Key<65>^ |
| RolePermission | <20><><EFBFBD>q<EFBFBD><71><EFBFBD>N<EFBFBD><4E><EFBFBD><EFBFBD><EFBFBD>s<EFBFBD>F<EFBFBD>۰ʸ<DBB0> parent permission |
| UserRole | `source` <20>Ϥ<EFBFBD> `manual` / `zitadel` / `ldap` / `scim`<EFBFBD>F`ReplaceForSource` <20><><EFBFBD>~ manual |
| RoleMapping | <20>~<7E><> group/role <20><> <20><><EFBFBD><EFBFBD> `role.key`<EFBFBD>A<EFBFBD><EFBFBD> SyncFromX ½Ķ IdP claims |
| Casbin Rule | `[tenant, role.key, http_path, http_methods, perm.name]` |
2026-05-19 13:56:59 +00:00
**<2A>h pod <20>P<EFBFBD>B**<2A>GRedis Pub/Sub <20>Y<EFBFBD><59> + 5 <20><><EFBFBD><EFBFBD> cron <20>©<EFBFBD><C2A9>]<5D>©<EFBFBD><C2A9><EFBFBD>ij<EFBFBD>b svc <20>h<EFBFBD>Ƶ{<7B>A<EFBFBD><41><EFBFBD>Ҳդ<D2B2><D5A4><EFBFBD><EFBFBD>ء^<5E>C
2026-05-19 13:56:59 +00:00
---
## 7. Middleware <20><>
2026-05-19 13:56:59 +00:00
```
HTTP Request
<20><>
[AuthJWT] <20><> <20>ѪR Bearer <20><> actor.WithActor(ctx, tenant, uid)
<20><>
[CasbinRBAC] <20><> <20><><EFBFBD><EFBFBD><EFBFBD>b AuthJWT <20><><EFBFBD><EFBFBD><EFBFBD>Fany-allow <20>L<EFBFBD>Ҧ<EFBFBD> open role
<20><>
handler <20><> logic <20><> usecase
2026-05-19 13:56:59 +00:00
```
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>G<EFBFBD>b `.api` <20><> `@server (middleware: AuthJWT,CasbinRBAC)` <20>ŧi<C5A7>A`make gen-api` <20>|<7C><> middleware <20>g<EFBFBD>i `routes.go`<EFBFBD>C**<2A>T<EFBFBD><54>** <20>b `gateway.go` <20><> `server.Use(...)` <20><><EFBFBD><EFBFBD>C<EFBFBD>Ԩ<EFBFBD> [`generate/api/README.md`](../generate/api/README.md#middleware-go-zero-<2D><><EFBFBD>W<EFBFBD><57><EFBFBD>q)<29>C
2026-05-19 13:56:59 +00:00
Actor <20>@<40>ߥ<EFBFBD> `internal/library/actor.WithActor` / `ActorFromContext`<EFBFBD>A<EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>b<EFBFBD>U logic package <20>۩w `actorKey struct{}`<EFBFBD>C
2026-05-19 13:56:59 +00:00
---
## 8. Notification <20>P<EFBFBD>~<7E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
2026-05-19 17:04:26 +00:00
> <EFBFBD>ԲӡG[`internal/model/notification/README.md`](../internal/model/notification/README.md)<29>B[`internal/model/member/README.md`](../internal/model/member/README.md)
2026-05-19 17:04:26 +00:00
| <20>y<EFBFBD>{ | <20>s<EFBFBD>Ƽh | <20>Ψ쪺 atomic |
|------|--------|---------------|
| <20><><EFBFBD>x<EFBFBD><78><EFBFBD>U email OTP | `logic/auth.RegisterLogic` | `member.OTP.Generate` + `notifier.Send(verify_registration_email)` |
| <20>~<7E><> email/phone <20><><EFBFBD><EFBFBD> | `logic/member.startVerification` + `confirmVerification` | `VerifyRate` <20><> `OTP.Generate` <20><> `Notifier.Send` <20><> `Profile.SetXxxVerified` |
| TOTP <20>j<EFBFBD>w / step-up | `logic/member.*_t_o_t_p_*` | `TOTP.StartEnroll` / `ConfirmEnroll` / `VerifyCode` |
2026-05-19 17:04:26 +00:00
> Notification <20>O library-style domain<69>A<EFBFBD><41> ServiceContext <20>Ұ<EFBFBD> `RetryWorker`<60>]<5D><><EFBFBD>B<EFBFBD><42><EFBFBD><EFBFBD> + DLQ<4C>^<5E>CProvider chain<69>GSMTP / SES / Mitake / Mock<63>A<EFBFBD><41> `Sort` failover<65>C
2026-05-19 17:04:26 +00:00
---
## 9. Rate Limit / Audit<69>]<5D>W<EFBFBD><57><EFBFBD>^
2026-05-19 17:04:26 +00:00
| <20>D<EFBFBD>D | <20>{<7B>p | <20>W<EFBFBD><57> |
2026-05-19 17:04:26 +00:00
|------|------|------|
| OTP / <20>~<7E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> cooldown | ? <20>w<EFBFBD><77><EFBFBD>@<40>]`member.VerifyRate`<60>ARedis SETNX + daily INCR<43>^ | <20>X |
| HTTP <20><><EFBFBD><EFBFBD> rate limit | <20><><EFBFBD><EFBFBD><EFBFBD>@ | <20><>ij go-zero middleware + Redis sliding-window<6F>]ZSET<45>^<5E>Akey = `rl:{dim}:{path}` |
| Audit Log | <20><><EFBFBD><EFBFBD><EFBFBD>@ | `audit_logs` collection<6F>]<5D>W<EFBFBD><57> DB <20><>ij<EFBFBD>^<5E>F`critical` <20>P<EFBFBD>B<EFBFBD>g<EFBFBD>A`info` <20><EFBFBD>FTTL 90d |
2026-05-19 13:56:59 +00:00
---
## 10. <20>W<EFBFBD>ҦҶq<D2B6>]100 <20>U+ <20>|<7C><><EFBFBD>^
2026-05-19 17:04:26 +00:00
| <20><><EFBFBD><EFBFBD> | <20><>ij |
|------|------|
| `members` <20><><EFBFBD><EFBFBD> | hashed `tenant_id`<EFBFBD>]<5D><EFBFBD><E6AFB2> 50 <20>U<EFBFBD>i<EFBFBD><69><EFBFBD>ƦX unique index <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^ |
| Casbin policy | <20>@ tenant <20>@ enforcer<65>Alazy <20>إߡFRedis Set <20>x<EFBFBD>s rule |
| JWT <20>¦W<C2A6><57> | <20>u<EFBFBD>¦W<C2A6><57><EFBFBD>w<EFBFBD>M<EFBFBD>P<EFBFBD><50> jti<74>ATTL = <20>ܦ۵M<DBB5>L<EFBFBD><4C> |
| Member list <20>d<EFBFBD><64> | <20>j<EFBFBD><6A><EFBFBD>a `tenant_id` + `(create_at, _id)` cursor <20><><EFBFBD><EFBFBD> |
| Notification DLQ | <20>W<EFBFBD>L `MaxRetry` <20>g<EFBFBD>J `notification_dlq`<EFBFBD>Fadmin CLI <20><><EFBFBD><EFBFBD> |
2026-05-19 13:56:59 +00:00
---
## 11. <20>w<EFBFBD>M<EFBFBD><4D><EFBFBD>ƶ<EFBFBD>
2026-05-19 13:56:59 +00:00
| <20>D<EFBFBD>D | <20>M<EFBFBD><4D> |
2026-05-19 13:56:59 +00:00
|------|------|
| Tenant <20>إ߶<D8A5><DFB6><EFBFBD> | Gateway <20><><EFBFBD>د<EFBFBD><D8AF>Z<EFBFBD>]status=provisioning<6E>^<5E><> ZITADEL CreateOrg <20><> <20><> `org_id`<EFBFBD>F<EFBFBD><EFBFBD><EFBFBD>v cron |
| Permission name <20><><EFBFBD>W | <20>T<EFBFBD><54><EFBFBD>]<5D>Q RolePermission / Casbin policy.name <20>ޥΡ^<5E>F<EFBFBD>o<EFBFBD><6F><EFBFBD><EFBFBD> `status=close` |
| Role.Key <20><><EFBFBD>W | <20>T<EFBFBD><54><EFBFBD>]<5D>~<7E><> IdP mapping <20><><EFBFBD><EFBFBD><EFBFBD>j<EFBFBD>^ |
| `is_system` Role | <20><><EFBFBD>i<EFBFBD>R<EFBFBD>B<EFBFBD><42><EFBFBD>i<EFBFBD><69> status |
| `manual` source ReplaceForSource | <20>ڵ<EFBFBD><DAB5>]<5D><> SyncFromX <20>~<7E>~<7E><><EFBFBD>ʫ<EFBFBD><CAAB><EFBFBD><EFBFBD>^ |
| Gateway <20>Τ@<40><><EFBFBD>U | <20><><EFBFBD>N ZITADEL Hosted Page<67>Femail + social <20>P UX<55>Ainvite <20><> logic <20><> |
| Email / SMS OTP | <20><> Gateway `notification` <20>۰e<DBB0>A**<2A><>**<2A><> ZITADEL Notification |
| <20>~<7E><> TOTP | <20>P ZITADEL TOTP <20>W<EFBFBD>ߡFsecret AES-GCM <20>s Mongo |
| Email <20><><EFBFBD>Ҧu<D2A6><75> | Ū `member.business_email_verified`<EFBFBD>A**<2A><>**Ū ZITADEL `email_verified` |
| UseCase <20><><EFBFBD><EFBFBD><EFBFBD>I | <20>j<EFBFBD><6A><EFBFBD>G<EFBFBD><47><EFBFBD><EFBFBD><EFBFBD>l<EFBFBD>y<EFBFBD>{<7B>b logic <20>s<EFBFBD>ơ]model.md <20><>6.1<EFBFBD>^ |
| Casbin <20>h pod <20>P<EFBFBD>B | Redis Pub/Sub <20>Y<EFBFBD><59> + 5min cron <20>©<EFBFBD> |
| Permission Catalog <20>ܧ<EFBFBD> | seed CLI<4C>]`cmd/permission-seed`<60>^<5E>FUI <20><><EFBFBD>i<EFBFBD><69><EFBFBD><EFBFBD><EFBFBD><EFBFBD> |
2026-05-19 13:56:59 +00:00
---
## 12. <20>ؿ<EFBFBD><D8BF><EFBFBD><EFBFBD>c
```
internal/
<EFBFBD>u<EFBFBD>w<EFBFBD>w library/
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w actor/ # ctx actor helper<65>]<5D><> module <20>@<40><> key<65>^
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w crypto/ # AES-GCM cipher<65>]TOTP secret KEK<45>^
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w errors/ # 8 <20>X SSCCCDDD
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w mongo/ # DocumentDB + cache
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w redis/ # go-zero client + Pub/Sub
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w validate/ # struct <20><><EFBFBD><EFBFBD>
<EFBFBD>x <20>|<7C>w<EFBFBD>w zitadel/ # ZITADEL HTTP client
<EFBFBD>u<EFBFBD>w<EFBFBD>w middleware/
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w authjwt_middleware.go # <20><> JWT <20><> actor
<EFBFBD>x <20>|<7C>w<EFBFBD>w casbinrbac_middleware.go # any-allow RBAC
<EFBFBD>u<EFBFBD>w<EFBFBD>w model/
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w auth/ # <20>ܽнX / <20><><EFBFBD>U metadata / OAuth session / JWT
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w member/ # Tenant / Member / Identity / OTP / TOTP / UID
<EFBFBD>x <20>u<EFBFBD>w<EFBFBD>w notification/ # Email / SMS / <20>ҪO / Worker / DLQ
<EFBFBD>x <20>|<7C>w<EFBFBD>w permission/ # Permission Catalog / Role / RolePermission / Casbin
<EFBFBD>|<7C>w<EFBFBD>w worker/
<20>|<7C>w<EFBFBD>w notification_retry/ # Notification RetryWorker runner
2026-05-19 13:56:59 +00:00
```
---
## 13. <20>]<5D>w<EFBFBD><77>
2026-05-19 13:56:59 +00:00
<EFBFBD>D<EFBFBD>n<EFBFBD>϶<EFBFBD><EFBFBD>]<5D><><EFBFBD>㨣 [`etc/README.md`](../etc/README.md) + `etc/gateway.dev.example.yaml`<EFBFBD>^<5E>G
2026-05-19 13:56:59 +00:00
```yaml
Mongo: {...}
Redis: {...}
Auth: # JWT secret / TTL / RegistrationSessionTTLSeconds
Member: # OTP / TOTP / Registration
Permission: # Casbin.Enabled / ModelPath / PolicyAdapter / Cache / Reload
Notification:# Email / SMS / Async / RatePerTenant
Zitadel: # Issuer / ServiceUserToken / Google* / DefaultOrgID
2026-05-19 17:04:26 +00:00
```
2026-05-19 13:56:59 +00:00
---
## 14. <20>i<EFBFBD>׳t<D7B3><74>
2026-05-19 13:56:59 +00:00
| <20>Ҳ<EFBFBD> | <20><><EFBFBD>A | <20>Ƶ<EFBFBD> |
2026-05-19 13:56:59 +00:00
|------|------|------|
| auth | ? <20>w<EFBFBD><77><EFBFBD>@ | <20>Τ@<40><><EFBFBD>U (Email + Google) / login / token refresh / logout |
| member | ? <20>w<EFBFBD><77><EFBFBD>@ | profile / <20>~<7E><> email/phone OTP / TOTP <20><><EFBFBD>y<EFBFBD>{ |
| permission | ? <20>w<EFBFBD><77><EFBFBD>@ | Catalog / Role CRUD / RolePermission / UserRole / RoleMapping / Casbin RBAC |
| notification | ? <20>֤ߧ<D6A4><DFA7><EFBFBD> | Email/SMS sync+async + DLQ<4C>FHTTP admin API <20><><EFBFBD><EFBFBD> |
| LDAP Directory Sync | ? <20><><EFBFBD><EFBFBD><EFBFBD>@ | <20><> SCIM / SyncFromX <20><><EFBFBD>i |
| SCIM 2.0 server | ? <20><><EFBFBD><EFBFBD><EFBFBD>@ | <20>̥<EFBFBD><CCA5>~<7E>Ȥ<EFBFBD><C8A4>ݨD |
| Audit Log | ? <20><><EFBFBD><EFBFBD><EFBFBD>@ | <20>W<EFBFBD><57> collection + TTL 90d |
| HTTP Rate Limit middleware | ? <20><><EFBFBD><EFBFBD><EFBFBD>@ | OTP cooldown <20>w<EFBFBD><77><EFBFBD>F<EFBFBD><46><EFBFBD><EFBFBD><EFBFBD>ݸ<EFBFBD> |