Files
vt/utils/authentication.go
2025-11-06 11:46:35 +01:00

174 lines
4.7 KiB
Go

package utils
import (
"context"
"fmt"
"net/http"
"os"
"time"
"git.kocoder.xyz/kocoded/vt/model"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/adaptor"
"golang.org/x/oauth2"
)
func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) {
setCallbackCookieExp(w, r, name, value, int(time.Hour.Seconds()))
}
func setCallbackCookieExp(w http.ResponseWriter, r *http.Request, name, value string, maxAge int) {
c := &http.Cookie{
Name: name,
Value: value,
Path: "/",
MaxAge: maxAge,
Secure: r.TLS != nil,
HttpOnly: true,
}
http.SetCookie(w, c)
}
type Info struct {
}
func CreateOIDCClient(ctx context.Context, app *fiber.App, appCtx Application) {
provider, err := oidc.NewProvider(ctx, "https://keycloak.kocoder.xyz/realms/che")
if err != nil {
appCtx.Logger.Error("Error generating OIDC Provider. ", "error", err)
}
oauthConfig := oauth2.Config{
ClientID: os.Getenv("CLIENT_ID"),
ClientSecret: os.Getenv("CLIENT_SECRET"),
RedirectURL: os.Getenv("BACKEND_URI") + "/api/auth/callback",
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile", "email"},
}
app.Get("/api/auth", adaptor.HTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
state, err := RandString(16)
if err != nil {
appCtx.Logger.Warn("Unable to create a state", "error", err)
http.Error(w, "Unable to create a state", http.StatusInternalServerError)
}
setCallbackCookie(w, r, "state", state)
http.Redirect(w, r, oauthConfig.AuthCodeURL(state), http.StatusFound)
}))
app.Get("/api/auth/callback", adaptor.HTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
state, err := r.Cookie("state")
if err != nil {
appCtx.Logger.Warn("State cookie not found", "error", err)
http.Error(w, "state not found", http.StatusBadRequest)
return
}
if r.URL.Query().Get("state") != state.Value {
appCtx.Logger.Warn("State cookie and header not matching", "error", err)
http.Error(w, "states not matching", http.StatusBadRequest)
return
}
oauth2Token, err := oauthConfig.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
appCtx.Logger.Warn("Failed to exchange token", "error", err)
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
return
}
userInfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
appCtx.Logger.Warn("failed to get userinfo", "error", err)
http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError)
return
}
resp := struct {
Token *oauth2.Token
UserInfo *oidc.UserInfo
}{oauth2Token, userInfo}
claims := &model.User{}
err = resp.UserInfo.Claims(claims)
if err != nil {
panic(err)
}
fmt.Println(claims)
user := &model.User{}
if appCtx.DB.Where(model.User{Email: resp.UserInfo.Email}).Assign(claims).FirstOrCreate(user).Error != nil {
appCtx.Logger.Warn("Failed to create user in DB")
http.Error(w, "failed to create user", http.StatusInternalServerError)
return
}
setCallbackCookieExp(w, r, "state", "", -1)
cookie, err := RandString(24)
if err != nil {
appCtx.Logger.Warn("Couldn't generate session-cookie.")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
setCallbackCookieExp(w, r, "auth-cookie", cookie, int(time.Hour.Seconds()))
appCtx.AddSession(&Session{Token: cookie, UserID: user.ID, MandantId: 1})
http.Redirect(w, r, os.Getenv("FRONTEND_URI")+"/dashboard", http.StatusFound)
}))
app.Get("/api/auth/currentSession", func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
session, err := appCtx.GetSessionFromToken(authToken)
if err != nil {
return err
}
return c.JSON(session)
})
app.Get("/api/auth/logout", func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
appCtx.RemoveSession(authToken)
cookie := new(fiber.Cookie)
cookie.Name = "auth-cookie"
cookie.Expires = time.Now().Add(-1 * time.Minute)
c.Cookie(cookie)
return c.Redirect(os.Getenv("FRONTEND_URI"))
})
}
type keyType struct{}
var UserKey keyType
func IsAuthenticated(appCtx Application) fiber.Handler {
fmt.Println("Gettings Session")
return func(c *fiber.Ctx) error {
authToken := c.Cookies("auth-cookie")
fmt.Println("Gettings Session", "sessiontoken", authToken)
session, err := appCtx.GetSessionFromToken(authToken)
if err != nil {
appCtx.Logger.Warn("Unauthorized GET Attempt", "reason", err)
return c.SendStatus(fiber.StatusUnauthorized)
}
fmt.Println("Saving Session", "session", session)
c.Locals("USER_KEY", session)
return c.Next()
}
}