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() } }