package auth import ( "fmt" "log" "net/http" "time" "github.com/go-playground/validator/v10" "github.com/golang-jwt/jwt/v5" "github.com/gorilla/mux" "github.com/markbates/goth" "github.com/markbates/goth/gothic" "sismedika.com/sas/westone/configs" "sismedika.com/sas/westone/types" "sismedika.com/sas/westone/utils" ) type Handler struct { store types.OauthStore errorz types.ErrorLogStore usrStore types.UserStore } func NewHandler(store types.OauthStore, errorz types.ErrorLogStore, usrStore types.UserStore) *Handler { return &Handler{ store: store, errorz: errorz, usrStore: usrStore, } } func (h *Handler) RegisterRoutes(router *mux.Router) { authroute := router.PathPrefix("/auth").Subrouter() authroute.HandleFunc("/generateauthcode", h.handlerGenerateAuthCode).Methods(http.MethodPost) // auth authroute.HandleFunc("/login", h.handleLogin).Methods(http.MethodPost) // oauth authroute.HandleFunc("/{provider}/login", h.handleAuthLogin).Methods(http.MethodGet) authroute.HandleFunc("/{provider}/logout", h.handleAuthLogout).Methods(http.MethodGet) authroute.HandleFunc("/{provider}/callback", h.handleAuthCallback).Methods(http.MethodGet) authroute.HandleFunc("/{provider}/linking", h.handleAuthLinkAccount).Methods(http.MethodPost) authroute.HandleFunc("/{provider}/authcode", h.handlerRedirectCode).Methods(http.MethodGet) protec := authroute.PathPrefix("/redirect").Subrouter() protec.Use(AuthMiddleware) protec.HandleFunc("/dashboard", h.handleRedirectDash).Methods(http.MethodGet) } func (h *Handler) handleAuthLogin(w http.ResponseWriter, r *http.Request) { if gothUser, err := gothic.CompleteUserAuth(w, r); err == nil { // log.Println(gothUser) log.Printf("[INFO] account found: %v", gothUser.Email) } else { gothic.BeginAuthHandler(w, r) } } func (h *Handler) handleAuthCallback(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) provider := vars["provider"] akungoogle, err := gothic.CompleteUserAuth(w, r) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } // add user to cookie session err = addUsertoSession(w, r, akungoogle) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } // check google is linked to server userID, err := h.store.CheckGoogleAccountLinked(types.UserGoogle{M_UserGoogleEmail: akungoogle.Email, M_UserGoogleIdentifier: akungoogle.UserID}) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } if userID > 0 { // generate jwt token user, err := h.usrStore.GetUserByID(userID) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } user.Type_Akun = provider timenow := time.Now() token, err := CreateJWT(types.DataJWT{ User: *user, Ip: r.RemoteAddr, Agent: r.UserAgent(), Version: "v1", LastLogin: timenow.Format("2006-01-02 15:04:05"), }) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } // data := "Bearer " + token // w.Header().Set("Authorization", data) // http.Redirect(w, r, "/api/v1/auth/redirect/dashboard", http.StatusFound) utils.WriteJSONLogin(w, http.StatusOK, user, token, provider) } else { log.Println("[INFO] redirect to auth-code") // http.Redirect(w, r, "/api/v1/auth/"+provider+"/authcode", http.StatusFound) http.Redirect(w, r, configs.Envs.PublicHost+"/auth-code", http.StatusFound) } } func (h *Handler) handleRedirectDash(w http.ResponseWriter, r *http.Request) { claims, ok := r.Context().Value(UserContextKey).(jwt.MapClaims) if !ok { utils.WriteError(w, http.StatusInternalServerError, fmt.Errorf("token not found in context")) return } username := claims["Username"].(string) temp := "WELCOME TO DASHBOARD, " + username utils.WriteJSON(w, http.StatusOK, temp) } func (h *Handler) handlerRedirectCode(w http.ResponseWriter, r *http.Request) { user_session, _ := store.Get(r, usersession) user := user_session.Values["user"].(goth.User) if user.IDToken == "" { log.Println("[ERROR] token not found in context") utils.WriteJSON(w, http.StatusInternalServerError, "not found") return } temp := "INSERT AUTH CODE, " + user.Email utils.WriteJSON(w, http.StatusOK, temp) } func (h *Handler) handleAuthLogout(w http.ResponseWriter, r *http.Request) { err := gothic.Logout(w, r) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } err = removeUsertoSession(w, r) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } utils.WriteJSON(w, http.StatusOK, "success logout") } func (h *Handler) handlerGenerateAuthCode(w http.ResponseWriter, r *http.Request) { var payload types.GenerateAuthCode if err := utils.ParseJSON(r, &payload); err != nil { utils.WriteError(w, http.StatusBadRequest, err) return } if err := utils.Validate.Struct(payload); err != nil { errorss := err.(validator.ValidationErrors) utils.WriteError(w, http.StatusBadRequest, fmt.Errorf("invalid payload: %v", errorss)) return } err := h.store.GenerateAuthCode(payload.Email, payload.Types, payload.UserID) if err != nil { log.Println("[ERROR] failed generate auth code") utils.WriteJSON(w, http.StatusInternalServerError, "failed generate auth code") return } msg := "Auth Code Generated for " + payload.Email utils.WriteJSON(w, http.StatusOK, msg) } func (h *Handler) handleAuthLinkAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) provider := vars["provider"] // var payload types.AuthCode var payload types.AuthCodePayload if err := utils.ParseJSON(r, &payload); err != nil { utils.WriteError(w, http.StatusBadRequest, err) return } if err := utils.Validate.Struct(payload); err != nil { errors := err.(validator.ValidationErrors) utils.WriteError(w, http.StatusBadRequest, fmt.Errorf("invalid payload: %v", errors)) return } user_session, _ := store.Get(r, usersession) akunGoogle := user_session.Values["user"].(goth.User) if akunGoogle.IDToken == "" { log.Println("[ERROR] google token not found in context") utils.WriteError(w, http.StatusInternalServerError, fmt.Errorf("token not found")) return } userID, err := h.store.CompareAuthCode(payload.Code, akunGoogle, provider) if err != nil { log.Println("[ERROR] compare auth code") utils.WriteError(w, http.StatusInternalServerError, err) return } user, err := h.usrStore.GetUserByID(userID) if err != nil { log.Println("[ERROR] cant get m_user data") utils.WriteError(w, http.StatusInternalServerError, err) return } user.Type_Akun = provider timenow := time.Now() token, err := CreateJWT(types.DataJWT{ User: *user, Ip: r.RemoteAddr, Agent: r.UserAgent(), Version: "v1", LastLogin: timenow.Format("2006-01-02 15:04:05"), }) if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) return } err = h.store.AddGoolgeAccount(types.UserGoogle{ M_UserGoogleM_UserID: user.M_UserID, M_UserGoogleEmail: akunGoogle.Email, M_UserGoogleIdentifier: akunGoogle.UserID, M_UserGoogleCode: payload.Code, M_UserGoogleToken: token, }) if err != nil { log.Printf("[ERROR] failed insert google account to db, %v", err) utils.WriteError(w, http.StatusInternalServerError, err) return } utils.WriteJSONLogin(w, http.StatusOK, user, token, provider) }