Files
alterminal/internal/oidc/jwks.go
T
2026-10-03 10:44:29 +08:00

47 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package oidc 實作 OIDC 端點:/.well-known/jwks.json,以及之後的
// Discovery、/authorize、/token 等,供 RP(Application)整合。
package oidc
import (
"fmt"
"log"
"net/http"
"gorm.io/gorm"
"alterminal/internal/auth"
"alterminal/internal/jwk"
)
// jwksMaxAge 為 JWKS 回應的建議快取秒數。金鑰輪替流程為「先產生並
// 發佈新金鑰,舊金鑰退休前仍留在 JWKS 供已簽發的 token 驗證」,因此
// RP 快取一小時並不影響驗證:快取期間內新舊金鑰皆可取得。
const jwksMaxAge = 3600
// JWKSHandler 處理 GET /.well-known/jwks.json(RFC 7517 §5):發佈所有
// 使用中簽章金鑰的公開 JWK,供 RP 驗證 ID Token/Access Token 的
// 簽章。已退休金鑰不再發佈;無使用中金鑰時回應空的 keys 陣列。
func JWKSHandler(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var keys []jwk.SigningKey
if err := db.Where("retired_at IS NULL").Order("created_at DESC").Find(&keys).Error; err != nil {
log.Printf("jwks: %v", err)
auth.WriteError(w, http.StatusInternalServerError, "內部錯誤")
return
}
set := jwk.JWKS{Keys: make([]jwk.JWK, 0, len(keys))}
for i := range keys {
k, err := keys[i].PublicJWK()
if err != nil {
// 單一金鑰的私鑰儲存毀損時跳過該金鑰並記錄待查,不讓整個
// 端點失靈——其餘金鑰照常發佈,RP 仍可驗證其簽發的 token。
log.Printf("jwks: 金鑰 %d(kid=%s)無法轉為公開 JWK: %v", keys[i].ID, keys[i].Kid, err)
continue
}
set.Keys = append(set.Keys, *k)
}
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", jwksMaxAge))
auth.WriteJSON(w, http.StatusOK, set)
}
}