174 lines
4.7 KiB
Go
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()
|
|
}
|
|
}
|