Skip to content

Goth returning error during oauth callback "could not find a matching session for this request". #615

@Cprakhar

Description

@Cprakhar

My oauth is not working for production, I deployed the server and client as web service on render.com using docker container.
Here is the server url : server(go)
and client url: nextjs

The error I am getting from the server is: "Error completing OAuth authentication: could not find a matching session for this request."

router.go

package router

import (
	"net/http"
	"time"

	"github.com/cprakhar/datawhiz/config"
	"github.com/cprakhar/datawhiz/internal/handlers"
	"github.com/cprakhar/datawhiz/internal/middleware"
	"github.com/gin-contrib/cors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	"github.com/markbates/goth/gothic"
)

// NewRouter initializes the Gin router with the necessary routes and middleware.
func NewRouter(cfg *config.Config) *gin.Engine {
	router := gin.Default()

	var sameSite http.SameSite
	switch cfg.Env.SameSite {
	case "lax":
		sameSite = http.SameSiteLaxMode
	case "strict":
		sameSite = http.SameSiteStrictMode
	case "none":
		sameSite = http.SameSiteNoneMode
	default:
		sameSite = http.SameSiteLaxMode // Default to Lax if not specified
	}

	store := cookie.NewStore([]byte(cfg.Env.SessionSecret))
	store.Options(sessions.Options{
		Path: "/",
		MaxAge: int(cfg.Env.SessionMaxAge),
		HttpOnly: true,
		Secure: cfg.Env.SessionSecure,
		SameSite: sameSite,
	})
	gothic.Store = store

	cors := cors.New(cors.Config{
		AllowOrigins:     []string{cfg.Env.FrontendBaseURL},
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH","DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Accept", "Authorization"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		MaxAge:           12 * time.Hour,
	})

	router.Use(sessions.Sessions(gothic.SessionName, store))
	router.Use(cors)

	h := &handlers.Handler{Cfg: cfg}

	h.InitProviders()

	api := router.Group("/api/v1")

	api.GET("/health", h.HandleHealthCheck)

	api.POST("/auth/register", h.HandleRegister)
	api.GET("/auth/oauth/signin", h.HandleOAuthSignIn)
	api.GET("/auth/oauth/callback", h.HandleOAuthCallback)
	api.POST("/auth/login", h.HandleLogin)
	api.POST("/auth/logout", middleware.RequireAuth(), h.HandleLogout)
	api.GET("/auth/me", middleware.RequireAuth(), h.HandleMe)


	api.GET("/connections", middleware.RequireAuth(), h.HandleGetConnections)
	api.GET("/connections/:id", middleware.RequireAuth(), h.HandleGetConnection)
	api.POST("/connections/ping", middleware.RequireAuth(), h.HandlePingConnection)
	api.POST("/connections", middleware.RequireAuth(), h.HandleCreateConnection)
	api.POST("/connections/:id/activate", middleware.RequireAuth(), h.HandleActivateConnection)
	api.DELETE("/connections/:id/deactivate", middleware.RequireAuth(), h.HandleDeactivateConnection)
	api.DELETE("/connections/:id", middleware.RequireAuth(), h.HandleDeleteConnection)
	
	api.GET("/tables/:id", middleware.RequireAuth(), h.HandleGetTables)
	api.GET("/tables/:id/:table_name/schema", middleware.RequireAuth(), h.HandleGetTableSchema)
	api.GET("/tables/:id/:table_name/records", middleware.RequireAuth(), h.HandleGetTableRecords)

	api.POST("/query/:id/generate", middleware.RequireAuth(), h.HandleGenerateQuery)
	api.POST("/query/:id/execute", middleware.RequireAuth(), h.HandleExecuteQuery)
	api.GET("/query/history/:id", middleware.RequireAuth(), h.HandleGetQueryHistory)
	api.DELETE("/query/history/:id", middleware.RequireAuth(), h.HandleDeleteQueryHistory)
	return router
}

oauth.go

package handlers

import (
	"context"
	"log"
	"net/http"

	"github.com/cprakhar/datawhiz/internal/database/schema"
	"github.com/cprakhar/datawhiz/internal/database/users"

	"github.com/cprakhar/datawhiz/utils/response"
	"github.com/cprakhar/datawhiz/utils/secure"
	"github.com/gin-gonic/gin"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/github"
	"github.com/markbates/goth/providers/google"
)

// InitProviders initializes the OAuth providers with the necessary credentials.
func (h *Handler) InitProviders() {
	goth.UseProviders(
		google.New(
			h.Cfg.Env.GoogleClientID,
			h.Cfg.Env.GoogleClientSecret,
			h.Cfg.Env.BackendBaseURL + "/api/v1/auth/oauth/callback?provider=google",
			"email", "profile",
		),
		github.New(
			h.Cfg.Env.GitHubClientID,
			h.Cfg.Env.GitHubClientSecret,
			h.Cfg.Env.BackendBaseURL + "/api/v1/auth/oauth/callback?provider=github",
			"read:user",
		),
	)
}

// HandleOAuthSignIn initiates the OAuth sign-in process by redirecting to the provider's authentication page.
func (h *Handler) HandleOAuthSignIn (ctx *gin.Context) {
	provider := ctx.Query("provider")
	log.Println(ctx.Request.URL)

	type providerKeyType string
	const providerKey providerKeyType = "oauth_provider"

	req := ctx.Request.WithContext(context.WithValue(ctx, providerKey, provider))

	session, err := gothic.Store.Get(ctx.Request, gothic.SessionName)
	if err != nil {
		log.Println("Error getting session:", err)
		response.InternalError(ctx, err)
		return
	}
	session.Values["oauth_provider"] = provider
	session.Save(ctx.Request, ctx.Writer)
	gothic.BeginAuthHandler(ctx.Writer, req)
}

// HandleOAuthCallback handles the OAuth callback after the user has authenticated with the provider.
func (h *Handler) HandleOAuthCallback(ctx *gin.Context) {
	provider := ctx.Query("provider")
	redirectURL := h.Cfg.Env.FrontendBaseURL + "/auth/oauth/callback?provider=" + provider
	req := gothic.GetContextWithProvider(ctx.Request, provider)

	user, err := gothic.CompleteUserAuth(ctx.Writer, req)
	if err != nil {
		log.Println("Error completing OAuth authentication:", err)
		ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
		return
	}

	exists, err := users.CheckUserExists(h.Cfg.DBClient, user.Email)
	if err != nil {
		log.Println("Error checking if user exists:", err)
		ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
		return
	}

	if !exists {
		newUser := &schema.User{
			Name: user.Name,
			Email: user.Email,
			AvatarURL: user.AvatarURL,
			OAuthProvider: provider,
			OAuthID: user.UserID,
		}
		createdUser, err := users.InsertOneUser(h.Cfg.DBClient, newUser)
		if err != nil {
			log.Println("Error creating new user:", err)
			ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
			return
		}

		err = secure.SetSessionCookie(ctx, 
			map[string]interface{}{
				"user_id": createdUser.ID,
				"email": createdUser.Email,
			},
		)
		if err != nil {
			log.Println("Error setting session cookie:", err)
			ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
			return
		}
		ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=success")
		return
	}

	upsertUser := &schema.User{
		Name: user.Name,
		Email: user.Email,
		AvatarURL: user.AvatarURL,
		OAuthProvider: provider,
		OAuthID: user.UserID,
	}
	updatedUser, err := users.UpsertOAuthUser(h.Cfg.DBClient, upsertUser)
	if err != nil {
		log.Println("Error upserting user:", err)
		ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
		return
	}
	err = secure.SetSessionCookie(ctx, 
		map[string]interface{}{
			"user_id": updatedUser.ID,
			"email": updatedUser.Email,
		},
	)
	if err != nil {
		log.Println("Error setting session cookie:", err)
		ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=error")
		return
	}
	ctx.Redirect(http.StatusTemporaryRedirect, redirectURL+"&status=success")
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions