package oidc import ( "fmt" "net/http" "alterminal/internal/application" "alterminal/internal/auth" ) // discoveryMaxAge 為 Discovery 文件的建議快取秒數,與 JWKS 一致:內容 // 僅在部署設定變更時改變。 const discoveryMaxAge = 3600 // discoveryDocument 為 OIDC Discovery 文件(OIDC Discovery 1.0 §3)。 // 本服務僅支援授權碼流程(RFC 6749 §4.1.1)與 refresh token grant // (§6);subject type 僅 public(sub 對使用者恆為同一值)。 type discoveryDocument struct { Issuer string `json:"issuer"` AuthorizationEndpoint string `json:"authorization_endpoint"` TokenEndpoint string `json:"token_endpoint"` UserInfoEndpoint string `json:"userinfo_endpoint"` JWKSURI string `json:"jwks_uri"` ScopesSupported []string `json:"scopes_supported"` ResponseTypesSupported []string `json:"response_types_supported"` ResponseModesSupported []string `json:"response_modes_supported"` GrantTypesSupported []string `json:"grant_types_supported"` SubjectTypesSupported []string `json:"subject_types_supported"` IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"` TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"` CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` ClaimsSupported []string `json:"claims_supported"` } // DiscoveryHandler 處理 GET /.well-known/openid-configuration:發佈本 // 服務的 OIDC 端點與能力中繼資料,供 RP 以標準方式取得組態。issuer 於 // main 讀取 ISSUER 環境變數後注入——issuer 字串須與簽入 token 的 iss // claim 完全一致(OIDC Core §3.1.3.7 的 issuer 驗證)。 func DiscoveryHandler(issuer string) http.HandlerFunc { doc := discoveryDocument{ Issuer: issuer, AuthorizationEndpoint: issuer + "/authorize", TokenEndpoint: issuer + "/token", UserInfoEndpoint: issuer + "/userinfo", JWKSURI: issuer + "/.well-known/jwks.json", ScopesSupported: application.ScopesSupported(), ResponseTypesSupported: []string{"code"}, ResponseModesSupported: []string{"query"}, GrantTypesSupported: []string{"authorization_code", "refresh_token"}, SubjectTypesSupported: []string{"public"}, IDTokenSigningAlgValuesSupported: []string{"RS256"}, TokenEndpointAuthMethodsSupported: []string{"client_secret_basic", "client_secret_post", "none"}, CodeChallengeMethodsSupported: []string{"S256"}, ClaimsSupported: []string{ "sub", "iss", "aud", "exp", "iat", "auth_time", "nonce", "name", "preferred_username", "email", "email_verified", }, } return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", discoveryMaxAge)) auth.WriteJSON(w, http.StatusOK, doc) } }