147 lines
3.9 KiB
Go
147 lines
3.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"gitea.local/admin/hspguard/internal/types"
|
|
"gitea.local/admin/hspguard/internal/web"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
func WriteJWKS(w http.ResponseWriter, r *http.Request) {
|
|
pubKey, err := parseBase64PublicKey("JWT_PUBLIC_KEY")
|
|
if err != nil {
|
|
web.Error(w, "failed to parse public key", http.StatusInternalServerError)
|
|
}
|
|
|
|
n := base64.RawURLEncoding.EncodeToString(pubKey.N.Bytes())
|
|
e := base64.RawURLEncoding.EncodeToString([]byte{1, 0, 1}) // 65537 = 0x010001
|
|
|
|
jwks := map[string]interface{}{
|
|
"keys": []map[string]string{
|
|
{
|
|
"kty": "RSA",
|
|
"kid": "my-rsa-key-1",
|
|
"use": "sig",
|
|
"alg": "RS256",
|
|
"n": n,
|
|
"e": e,
|
|
},
|
|
},
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(jwks)
|
|
}
|
|
|
|
func OpenIdConfiguration(w http.ResponseWriter, r *http.Request) {
|
|
type Response struct {
|
|
TokenEndpoint string `json:"token_endpoint"`
|
|
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
|
JwksURI string `json:"jwks_uri"`
|
|
Issuer string `json:"issuer"`
|
|
EndSessionEndpoint string `json:"end_session_endpoint"`
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
encoder := json.NewEncoder(w)
|
|
if err := encoder.Encode(Response{
|
|
TokenEndpoint: "https://cb5f-2a00-10-5b00-c801-e955-5c68-63d0-b777.ngrok-free.app/api/v1/oauth/token",
|
|
AuthorizationEndpoint: "https://cb5f-2a00-10-5b00-c801-e955-5c68-63d0-b777.ngrok-free.app/authorize",
|
|
JwksURI: "https://cb5f-2a00-10-5b00-c801-e955-5c68-63d0-b777.ngrok-free.app/.well-known/jwks.json",
|
|
Issuer: "https://cb5f-2a00-10-5b00-c801-e955-5c68-63d0-b777.ngrok-free.app",
|
|
EndSessionEndpoint: "https://cb5f-2a00-10-5b00-c801-e955-5c68-63d0-b777.ngrok-free.app/api/v1/oauth/logout",
|
|
}); err != nil {
|
|
web.Error(w, "failed to encode response", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func parseBase64PrivateKey(envVar string) (*rsa.PrivateKey, error) {
|
|
b64 := os.Getenv(envVar)
|
|
if b64 == "" {
|
|
return nil, fmt.Errorf("env var %s is empty", envVar)
|
|
}
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(b64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
|
|
}
|
|
|
|
key, err := x509.ParsePKCS8PrivateKey(decoded)
|
|
return key.(*rsa.PrivateKey), err
|
|
}
|
|
|
|
func parseBase64PublicKey(envVar string) (*rsa.PublicKey, error) {
|
|
b64 := os.Getenv(envVar)
|
|
if b64 == "" {
|
|
return nil, fmt.Errorf("env var %s is empty", envVar)
|
|
}
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(b64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
|
|
}
|
|
|
|
pubInterface, err := x509.ParsePKIXPublicKey(decoded)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse public key: %v", err)
|
|
}
|
|
|
|
pubKey, ok := pubInterface.(*rsa.PublicKey)
|
|
if !ok {
|
|
return nil, fmt.Errorf("not an RSA public key")
|
|
}
|
|
|
|
return pubKey, nil
|
|
}
|
|
|
|
func SignJwtToken(claims jwt.Claims) (string, error) {
|
|
privateKey, err := parseBase64PrivateKey("JWT_PRIVATE_KEY")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
|
|
|
token.Header["kid"] = "my-rsa-key-1"
|
|
|
|
s, err := token.SignedString(privateKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func VerifyToken(token string) (*jwt.Token, *types.UserClaims, error) {
|
|
publicKey, err := parseBase64PublicKey("JWT_PUBLIC_KEY")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
claims := &types.UserClaims{}
|
|
parsed, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) {
|
|
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
|
|
}
|
|
return publicKey, nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("invalid token: %w", err)
|
|
}
|
|
|
|
if !parsed.Valid {
|
|
return nil, nil, fmt.Errorf("token is not valid")
|
|
}
|
|
|
|
return parsed, claims, nil
|
|
}
|