feat: Add optional OIDC support (#39)

This allows the API to trigger an OAuth workflow to create the JWT for authentication. For now the workflow is triggered by manually visiting `/api/login/oidc` on the frontend app until the UI repo is updated to add support.

Co-authored-by: Peter Olds <peter@olds.co>
This commit is contained in:
Yuzerion
2025-01-13 04:49:46 +08:00
committed by GitHub
parent e10e6cfe4d
commit 51f6391ded
10 changed files with 460 additions and 6 deletions

View File

@@ -51,10 +51,31 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
U.HandleErr(w, r, err, http.StatusUnauthorized)
return
}
if err := setAuthenticatedCookie(w, r, creds.Username); err != nil {
U.HandleErr(w, r, err, http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func AuthMethodHandler(w http.ResponseWriter, r *http.Request) {
switch {
case common.APIJWTSecret == nil:
U.WriteBody(w, []byte("skip"))
case common.OIDCIssuerURL != "":
U.WriteBody(w, []byte("oidc"))
case common.APIPasswordHash != nil:
U.WriteBody(w, []byte("password"))
default:
U.WriteBody(w, []byte("skip"))
}
w.WriteHeader(http.StatusOK)
}
func setAuthenticatedCookie(w http.ResponseWriter, r *http.Request, username string) error {
expiresAt := time.Now().Add(common.APIJWTTokenTTL)
claim := &Claims{
Username: creds.Username,
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expiresAt),
},
@@ -62,8 +83,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claim)
tokenStr, err := token.SignedString(common.APIJWTSecret)
if err != nil {
U.HandleErr(w, r, err)
return
return err
}
http.SetCookie(w, &http.Cookie{
Name: "token",
@@ -73,7 +93,7 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
SameSite: http.SameSiteStrictMode,
Path: "/",
})
w.WriteHeader(http.StatusOK)
return nil
}
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
@@ -89,6 +109,20 @@ func LogoutHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTemporaryRedirect)
}
// Initialize sets up authentication providers.
func Initialize() error {
// Initialize OIDC if configured.
if common.OIDCIssuerURL != "" {
return InitOIDC(
common.OIDCIssuerURL,
common.OIDCClientID,
common.OIDCClientSecret,
common.OIDCRedirectURL,
)
}
return nil
}
func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
if common.IsDebugSkipAuth || common.APIJWTSecret == nil {
return next