feat(identity): 收敛 AI Gateway 统一认证入口
Web 登录页合并为单一统一认证入口,未指定上下文时由认证中心在登录后决策。继续接受旧客户端显式传入 platform 或 tenant,并保持回调上下文及 tenant_hint 的绑定校验。同步更新 OpenAPI 产物。\n\n验证:Go 全量测试与 go vet 通过;Web lint、141 项 Vitest 和生产构建通过;本地 platform.admin 登录及管理工作台访问通过。
This commit is contained in:
@@ -86,14 +86,14 @@ func (c *OIDCPublicClient) AuthorizationURL(
|
||||
return "", errors.New("state, nonce and PKCE verifier are required")
|
||||
}
|
||||
contextType = strings.TrimSpace(contextType)
|
||||
if contextType != "platform" && contextType != "tenant" {
|
||||
return "", errors.New("context type must be platform or tenant")
|
||||
if contextType != "" && contextType != "platform" && contextType != "tenant" {
|
||||
return "", errors.New("context type must be empty, platform or tenant")
|
||||
}
|
||||
if strings.TrimSpace(tenantHint) != "" && uuid.Validate(strings.TrimSpace(tenantHint)) != nil {
|
||||
return "", errors.New("tenant hint must be a UUID")
|
||||
}
|
||||
if contextType == "platform" && strings.TrimSpace(tenantHint) != "" {
|
||||
return "", errors.New("platform context cannot bind a tenant hint")
|
||||
if contextType != "tenant" && strings.TrimSpace(tenantHint) != "" {
|
||||
return "", errors.New("only tenant context can bind a tenant hint")
|
||||
}
|
||||
config, _, err := c.configuration(ctx)
|
||||
if err != nil {
|
||||
@@ -102,7 +102,9 @@ func (c *OIDCPublicClient) AuthorizationURL(
|
||||
options := []oauth2.AuthCodeOption{
|
||||
oidc.Nonce(nonce),
|
||||
oauth2.S256ChallengeOption(pkceVerifier),
|
||||
oauth2.SetAuthURLParam("context_type", contextType),
|
||||
}
|
||||
if contextType != "" {
|
||||
options = append(options, oauth2.SetAuthURLParam("context_type", contextType))
|
||||
}
|
||||
if strings.TrimSpace(tenantHint) != "" {
|
||||
options = append(options, oauth2.SetAuthURLParam("tenant_hint", strings.TrimSpace(tenantHint)))
|
||||
|
||||
@@ -92,6 +92,43 @@ func TestOIDCPublicClientUsesAuthorizationCodePKCES256WithoutSecret(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestOIDCPublicClientOmitsOptionalContextForUnifiedLogin(t *testing.T) {
|
||||
var issuer string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/.well-known/openid-configuration" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{
|
||||
"issuer": issuer, "authorization_endpoint": issuer + "/authorize",
|
||||
"token_endpoint": issuer + "/token", "jwks_uri": issuer + "/jwks",
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
issuer = server.URL
|
||||
|
||||
client, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "test", Issuer: issuer, ClientID: "gateway-public",
|
||||
RedirectURI: "https://gateway.example.com/callback",
|
||||
PostLogoutRedirectURI: "https://gateway.example.com/",
|
||||
HTTPClient: server.Client(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
authorizationURL, err := client.AuthorizationURL(
|
||||
context.Background(), "state", "nonce",
|
||||
"test-pkce-verifier-with-at-least-43-characters-1234", "", "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parsed, _ := url.Parse(authorizationURL)
|
||||
if parsed.Query().Has("context_type") || parsed.Query().Has("tenant_hint") {
|
||||
t.Fatalf("unified login leaked optional context binding: %v", parsed.Query())
|
||||
}
|
||||
}
|
||||
|
||||
func TestOIDCPublicClientRejectsOfflineAccess(t *testing.T) {
|
||||
_, err := NewOIDCPublicClient(OIDCPublicClientConfig{
|
||||
AppEnv: "production",
|
||||
|
||||
Reference in New Issue
Block a user