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:
@@ -99,14 +99,18 @@ func (builder *RuntimeBuilder) Build(ctx context.Context, revision identity.Revi
|
||||
if err := identity.ValidateRevisionURLs(revision, builder.config.AppEnv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if builder.store == nil || builder.secrets == nil || revision.Issuer == "" || revision.TenantID == "" ||
|
||||
revision.Audience == "" || revision.RolePrefix == "" {
|
||||
if builder.store == nil || builder.secrets == nil || revision.Issuer == "" ||
|
||||
revision.Audience == "" || revision.RolePrefix == "" ||
|
||||
revision.TenantMode != "multi_tenant" && revision.TenantID == "" ||
|
||||
revision.TenantMode == "multi_tenant" && (revision.ApplicationID == "" || revision.TenantContextEndpoint == "") {
|
||||
return nil, errors.New("identity runtime configuration is incomplete")
|
||||
}
|
||||
if exists, err := builder.store.HasActiveTenantKey(ctx, revision.LocalTenantKey); err != nil {
|
||||
return nil, err
|
||||
} else if !exists {
|
||||
return nil, identity.ErrLocalTenantInvalid
|
||||
if revision.TenantMode != "multi_tenant" {
|
||||
if exists, err := builder.store.HasActiveTenantKey(ctx, revision.LocalTenantKey); err != nil {
|
||||
return nil, err
|
||||
} else if !exists {
|
||||
return nil, identity.ErrLocalTenantInvalid
|
||||
}
|
||||
}
|
||||
runtimeCtx, cancel := context.WithCancel(builder.ctx)
|
||||
runtime := &Runtime{Revision: revision, CookieSecure: secureCookieFor(revision.PublicBaseURL), close: cancel}
|
||||
@@ -151,6 +155,7 @@ func (builder *RuntimeBuilder) Build(ctx context.Context, revision identity.Revi
|
||||
verifier, err := auth.NewOIDCVerifier(auth.OIDCConfig{
|
||||
AppEnv: builder.config.AppEnv,
|
||||
Issuer: revision.Issuer, Audience: revision.Audience, TenantID: revision.TenantID,
|
||||
TenantMode: revision.TenantMode, ApplicationID: revision.ApplicationID, ClientID: revision.BrowserClientID,
|
||||
RolePrefix: revision.RolePrefix, RequiredScopes: append([]string(nil), revision.Scopes...),
|
||||
JWKSCacheTTL: builder.config.JWKSCacheTTL, IntrospectionEnabled: revision.TokenIntrospection,
|
||||
IntrospectionCredentialProvider: credentialProvider, SecurityEventEvaluator: evaluator,
|
||||
@@ -166,6 +171,23 @@ func (builder *RuntimeBuilder) Build(ctx context.Context, revision identity.Revi
|
||||
return nil, err
|
||||
}
|
||||
runtime.Verifier = verifier
|
||||
if revision.TenantMode == "multi_tenant" {
|
||||
tenantContext, err := identity.NewTenantContextClient(
|
||||
builder.config.AppEnv, revision.Issuer, revision.ApplicationID,
|
||||
revision.TenantContextEndpoint, revision.TenantContextAudience, revision.TenantContextScope,
|
||||
credentialProvider, nil,
|
||||
)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
runtime.TenantContext = tenantContext
|
||||
runtime.start = func() {
|
||||
go runTenantContextSynchronizer(
|
||||
runtimeCtx, builder.store, tenantContext, revision.Issuer, revision.ApplicationID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if slices.Contains(revision.Capabilities, "oidc_login") {
|
||||
if revision.BrowserClientID == "" || revision.SessionEncryptionKeyRef == "" {
|
||||
@@ -396,6 +418,7 @@ func (builder *RuntimeBuilder) newSecurityEventManager(ctx context.Context, revi
|
||||
func (builder *RuntimeBuilder) securityEventManagerConfig(revision identity.Revision, strictRevisionBinding bool) securityevents.ConnectionManagerConfig {
|
||||
return securityevents.ConnectionManagerConfig{
|
||||
AppEnv: builder.config.AppEnv, OIDCEnabled: true, OIDCIssuer: revision.Issuer, OIDCTenantID: revision.TenantID,
|
||||
OIDCApplicationID: revision.ApplicationID, OIDCTenantMode: revision.TenantMode,
|
||||
ManagementClientID: revision.MachineClientID, PublicBaseURL: revision.PublicBaseURL,
|
||||
ExpectedTransmitterIssuer: strings.TrimRight(strings.TrimSpace(revision.SecurityEventIssuer), "/"),
|
||||
ExpectedAudience: revision.SecurityEventAudience,
|
||||
|
||||
Reference in New Issue
Block a user