first commit

This commit is contained in:
2026-10-03 10:44:29 +08:00
parent f373cb8d37
commit bcf3d3769c
58 changed files with 4313 additions and 487 deletions
+46
View File
@@ -0,0 +1,46 @@
// 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)
}
}