feat(identity): 接入认证中心多租户登录
支持 Manifest V2 动态 tid 验证、Tenant Context 同步和租户内 JIT 投影,并保留 Manifest V1 与旧 Session 兼容。\n\n增加 tenantHint、租户切换、普通注册关闭及 application/principal/tenant 两级 SSF 撤销;迁移、定向安全测试和本地双租户跨仓 E2E 已通过。\n\nrelease_required=true;未执行 Release、Staging 或真实链路。
This commit is contained in:
@@ -15,7 +15,12 @@ type OIDCSession struct {
|
||||
SessionTokenHash []byte
|
||||
GatewayUserID string
|
||||
GatewayTenantID string
|
||||
OIDCUserBindingID string
|
||||
ExternalUserID string
|
||||
Issuer string
|
||||
ApplicationID string
|
||||
TenantID string
|
||||
OIDCClientID string
|
||||
UserStatus string
|
||||
UserDeleted bool
|
||||
TokenCiphertext []byte
|
||||
@@ -34,6 +39,11 @@ type CreateOIDCSessionInput struct {
|
||||
SessionTokenHash []byte
|
||||
GatewayUserID string
|
||||
GatewayTenantID string
|
||||
OIDCUserBindingID string
|
||||
OIDCClientID string
|
||||
Issuer string
|
||||
ApplicationID string
|
||||
TenantID string
|
||||
TokenCiphertext []byte
|
||||
AccessTokenExpiresAt time.Time
|
||||
LastSeenAt time.Time
|
||||
@@ -46,12 +56,27 @@ func (s *Store) CreateOIDCSession(ctx context.Context, input CreateOIDCSessionIn
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
INSERT INTO gateway_oidc_sessions (
|
||||
session_token_hash, gateway_user_id, gateway_tenant_id, token_ciphertext,
|
||||
access_token_expires_at, last_seen_at, idle_expires_at, absolute_expires_at
|
||||
access_token_expires_at, last_seen_at, idle_expires_at, absolute_expires_at,
|
||||
oidc_user_binding_id, oidc_client_id
|
||||
)
|
||||
VALUES ($1, $2::uuid, $3::uuid, $4, $5, $6, $7, $8)
|
||||
SELECT $1, u.id, $3::uuid, $4, $5, $6, $7, $8, NULLIF($9, '')::uuid, NULLIF($10, '')
|
||||
FROM gateway_users u
|
||||
LEFT JOIN gateway_oidc_user_bindings ub ON ub.id = NULLIF($9, '')::uuid
|
||||
LEFT JOIN gateway_oidc_tenant_bindings tb ON tb.id = ub.tenant_binding_id
|
||||
WHERE u.id = $2::uuid
|
||||
AND u.gateway_tenant_id = $3::uuid
|
||||
AND ($9 = '' OR (
|
||||
ub.gateway_user_id = u.id
|
||||
AND tb.gateway_tenant_id = u.gateway_tenant_id
|
||||
AND tb.issuer = $11
|
||||
AND tb.application_id = $12
|
||||
AND tb.external_tenant_id = $13
|
||||
))
|
||||
RETURNING id::text`,
|
||||
input.SessionTokenHash, input.GatewayUserID, input.GatewayTenantID, input.TokenCiphertext,
|
||||
input.AccessTokenExpiresAt, input.LastSeenAt, input.IdleExpiresAt, input.AbsoluteExpiresAt,
|
||||
input.OIDCUserBindingID, input.OIDCClientID,
|
||||
input.Issuer, input.ApplicationID, input.TenantID,
|
||||
).Scan(&id)
|
||||
if err != nil {
|
||||
return OIDCSession{}, err
|
||||
@@ -64,6 +89,8 @@ func (s *Store) FindOIDCSessionByHash(ctx context.Context, hash []byte) (OIDCSes
|
||||
SELECT `+oidcSessionColumns+`
|
||||
FROM gateway_oidc_sessions s
|
||||
JOIN gateway_users u ON u.id = s.gateway_user_id
|
||||
LEFT JOIN gateway_oidc_user_bindings ub ON ub.id = s.oidc_user_binding_id
|
||||
LEFT JOIN gateway_oidc_tenant_bindings tb ON tb.id = ub.tenant_binding_id
|
||||
WHERE s.session_token_hash = $1`, hash))
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return OIDCSession{}, ErrOIDCSessionNotFound
|
||||
@@ -138,7 +165,9 @@ WHERE idle_expires_at <= $1 OR absolute_expires_at <= $1`, now)
|
||||
|
||||
const oidcSessionColumns = `
|
||||
s.id::text, s.session_token_hash, s.gateway_user_id::text, s.gateway_tenant_id::text,
|
||||
COALESCE(u.external_user_id, ''), u.status, u.deleted_at IS NOT NULL,
|
||||
COALESCE(s.oidc_user_binding_id::text, ''), COALESCE(ub.subject, u.external_user_id, ''),
|
||||
COALESCE(tb.issuer, ''), COALESCE(tb.application_id, ''), COALESCE(tb.external_tenant_id, ''),
|
||||
COALESCE(s.oidc_client_id, ''), u.status, u.deleted_at IS NOT NULL,
|
||||
s.token_ciphertext, s.access_token_expires_at, s.last_seen_at, s.idle_expires_at,
|
||||
s.absolute_expires_at, s.refresh_version, COALESCE(s.refresh_lock_id::text, ''),
|
||||
COALESCE(s.refresh_lock_until, 'epoch'::timestamptz), s.created_at, s.updated_at`
|
||||
@@ -147,7 +176,8 @@ func scanOIDCSession(row pgx.Row) (OIDCSession, error) {
|
||||
var item OIDCSession
|
||||
err := row.Scan(
|
||||
&item.ID, &item.SessionTokenHash, &item.GatewayUserID, &item.GatewayTenantID,
|
||||
&item.ExternalUserID, &item.UserStatus, &item.UserDeleted, &item.TokenCiphertext,
|
||||
&item.OIDCUserBindingID, &item.ExternalUserID, &item.Issuer, &item.ApplicationID,
|
||||
&item.TenantID, &item.OIDCClientID, &item.UserStatus, &item.UserDeleted, &item.TokenCiphertext,
|
||||
&item.AccessTokenExpiresAt, &item.LastSeenAt, &item.IdleExpiresAt, &item.AbsoluteExpiresAt,
|
||||
&item.RefreshVersion, &item.RefreshLockID, &item.RefreshLockUntil, &item.CreatedAt, &item.UpdatedAt,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user