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:
2026-07-28 17:28:35 +08:00
parent 0b02e62c72
commit 5c679ff13f
45 changed files with 2986 additions and 139 deletions
@@ -50,6 +50,8 @@ type ConnectionManagerConfig struct {
OIDCEnabled bool
OIDCIssuer string
OIDCTenantID string
OIDCApplicationID string
OIDCTenantMode string
ManagementClientID string
ManagementClientSecret string
ExpectedTransmitterIssuer string
@@ -145,7 +147,9 @@ func (err safeConnectionError) Unwrap() error { return err.cause }
func (err safeConnectionError) SafeErrorCategory() string { return err.category }
func NewConnectionManager(ctx context.Context, repository ConnectionRepository, secrets SecretStore, config ConnectionManagerConfig, metrics *Metrics) (*ConnectionManager, error) {
if repository == nil || secrets == nil || !config.OIDCEnabled || config.OIDCIssuer == "" || config.OIDCTenantID == "" {
validIdentityBinding := config.OIDCTenantMode == "multi_tenant" && uuid.Validate(config.OIDCApplicationID) == nil ||
config.OIDCTenantMode != "multi_tenant" && uuid.Validate(config.OIDCTenantID) == nil
if repository == nil || secrets == nil || !config.OIDCEnabled || config.OIDCIssuer == "" || !validIdentityBinding {
return nil, errors.New("security event connection prerequisites are incomplete")
}
if config.HeartbeatInterval <= 0 {
@@ -773,7 +777,8 @@ func (m *ConnectionManager) Evaluate(ctx context.Context, identity auth.OIDCSecu
return auth.OIDCSecurityEventEvaluation{Enabled: true, RequireIntrospection: true}, nil
}
result, evaluateErr := m.repository.EvaluateOIDCSecurityEvent(
ctx, connection.TransmitterIssuer, *connection.Audience, identity.Issuer, identity.TenantID, identity.Subject,
ctx, connection.TransmitterIssuer, *connection.Audience, identity.Issuer, identity.ApplicationID,
identity.TenantID, identity.Subject,
identity.IssuedAt, time.Now().UTC(), m.config.StaleAfter,
)
if result.Revoked && m.metrics != nil {
@@ -821,7 +826,9 @@ func (m *ConnectionManager) activate(ctx context.Context, connection store.Secur
}
verifier, err := NewVerifier(VerifierConfig{
TransmitterIssuer: connection.TransmitterIssuer, Audience: *connection.Audience,
SubjectIssuer: m.config.OIDCIssuer, TenantID: m.config.OIDCTenantID, StreamID: *connection.StreamID,
SubjectIssuer: m.config.OIDCIssuer, TenantID: m.config.OIDCTenantID,
ApplicationID: m.config.OIDCApplicationID, TenantMode: m.config.OIDCTenantMode,
StreamID: *connection.StreamID,
ClockSkew: m.config.ClockSkew, HTTPClient: safeClient,
})
if err != nil {
@@ -1038,7 +1045,11 @@ func (m *ConnectionManager) validatePrerequisites(issuer, managementClientID str
if !m.config.OIDCEnabled || strings.TrimSpace(managementClientID) == "" || len(managementSecret) < 16 {
return errors.New("OIDC and RFC 7662 machine client must be configured")
}
if _, err := uuid.Parse(m.config.OIDCTenantID); err != nil {
if m.config.OIDCTenantMode == "multi_tenant" {
if uuid.Validate(m.config.OIDCApplicationID) != nil {
return errors.New("OIDC application id is invalid")
}
} else if uuid.Validate(m.config.OIDCTenantID) != nil {
return errors.New("OIDC tenant id is invalid")
}
if _, err := validatedIssuerURL(issuer, m.config.AppEnv); err != nil {