UPDATE: Add rederict
All checks were successful
Build and Release / release (push) Successful in 1m7s

This commit is contained in:
2026-03-31 12:47:15 +07:00
parent 8e2dcb817f
commit 7f589b4994
2 changed files with 56 additions and 18 deletions

View File

@@ -2,11 +2,13 @@ package controllers
import ( import (
"context" "context"
"encoding/base64"
"encoding/json"
"history-api/internal/dtos/request" "history-api/internal/dtos/request"
"history-api/internal/dtos/response" "history-api/internal/dtos/response"
"history-api/internal/models"
"history-api/internal/services" "history-api/internal/services"
"history-api/pkg/validator" "history-api/pkg/validator"
"strings"
"time" "time"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
@@ -296,20 +298,32 @@ func (h *AuthController) ForgotPassword(c fiber.Ctx) error {
func (h *AuthController) GoogleLogin(c fiber.Ctx) error { func (h *AuthController) GoogleLogin(c fiber.Ctx) error {
_, cancel := context.WithTimeout(context.Background(), 10*time.Second) _, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
state := uuid.New().String() state := uuid.New().String()
secure := c.Protocol() == "https" redirect := c.Query("redirect")
if redirect == "" {
redirect = "http://localhost:3000"
}
data := models.OAuthState{
State: state,
RedirectURL: redirect,
}
b, _ := json.Marshal(data)
encoded := base64.URLEncoding.EncodeToString(b)
c.Cookie(&fiber.Cookie{ c.Cookie(&fiber.Cookie{
Name: "oauth_state", Name: "oauth_state",
Value: state, Value: state,
Expires: time.Now().Add(15 * time.Minute), Expires: time.Now().Add(15 * time.Minute),
HTTPOnly: true, HTTPOnly: true,
Secure: secure, Secure: c.Protocol() == "https",
SameSite: "None", SameSite: "None",
}) })
url := h.oauth.AuthCodeURL(state) url := h.oauth.AuthCodeURL(encoded)
return c.Redirect().To(url) return c.Redirect().To(url)
} }
@@ -326,18 +340,31 @@ func (h *AuthController) GoogleLogin(c fiber.Ctx) error {
func (h *AuthController) GoogleCallback(c fiber.Ctx) error { func (h *AuthController) GoogleCallback(c fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
stateFromGoogle := c.Query("state")
stateFromCookie := c.Cookies("oauth_state")
if stateFromGoogle == "" || stateFromGoogle != stateFromCookie { encoded := c.Query("state")
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid state"})
b, err := base64.URLEncoding.DecodeString(encoded)
if err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid state"})
} }
var data models.OAuthState
if err := json.Unmarshal(b, &data); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid state"})
}
stateFromCookie := c.Cookies("oauth_state")
if data.State != stateFromCookie {
return c.Status(401).JSON(fiber.Map{"error": "Invalid state"})
}
c.ClearCookie("oauth_state") c.ClearCookie("oauth_state")
code := c.Query("code") code := c.Query("code")
token, err := h.oauth.Exchange(context.Background(), code)
token, err := h.oauth.Exchange(ctx, code)
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Token exchange failed"}) return c.Status(500).JSON(fiber.Map{"error": "Token exchange failed"})
} }
idToken, ok := token.Extra("id_token").(string) idToken, ok := token.Extra("id_token").(string)
@@ -345,14 +372,9 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error {
return c.Status(500).JSON(fiber.Map{"error": "No id_token"}) return c.Status(500).JSON(fiber.Map{"error": "No id_token"})
} }
parts := strings.Split(idToken, ".")
if len(parts) < 2 {
return c.Status(500).JSON(fiber.Map{"error": "Invalid id_token"})
}
payload, err := idtoken.Validate(ctx, idToken, h.oauth.ClientID) payload, err := idtoken.Validate(ctx, idToken, h.oauth.ClientID)
if err != nil { if err != nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Token verification failed"}) return c.Status(401).JSON(fiber.Map{"error": "Token verification failed"})
} }
googleUser := request.SigninWithGoogleDto{ googleUser := request.SigninWithGoogleDto{
@@ -364,7 +386,7 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error {
res, err := h.service.SigninWithGoogle(ctx, &googleUser) res, err := h.service.SigninWithGoogle(ctx, &googleUser)
if err != nil { if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ return c.Status(500).JSON(response.CommonResponse{
Status: false, Status: false,
Message: err.Error(), Message: err.Error(),
}) })
@@ -386,5 +408,16 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error {
SameSite: "None", SameSite: "None",
}) })
return c.Redirect().To("http://localhost:5500") allowed := map[string]bool{
"http://localhost:3000": true,
"http://localhost:5500": true,
"https://app.yourdomain.com": true,
}
redirectURL := data.RedirectURL
if !allowed[redirectURL] {
redirectURL = "http://localhost:3000"
}
return c.Redirect().To(redirectURL)
} }

View File

@@ -7,3 +7,8 @@ type TokenEntity struct {
Token string `json:"token"` Token string `json:"token"`
TokenType constants.TokenType `json:"token_type"` TokenType constants.TokenType `json:"token_type"`
} }
type OAuthState struct {
State string `json:"state"`
RedirectURL string `json:"redirect"`
}