markbates / markbates/goth

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

Open
#615 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
6.6k
Forks
631
PR merge metrics
No merged PRs in 30d

Description

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)](https://server-6zuj.onrender.com)
and client url: [nextjs](https://client-2r99.onrender.com)

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

`router.go`
```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`
```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")
}
```

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with router.go and oauth.go, tracing the deployed sign-in and callback flow from the Render URLs through session creation and cookie handling. Compare the production session, CORS, SameSite, Secure, and OAuth redirect configuration shown in these entry points. Done means the production callback completes authentication without the matching-session error.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, go, nextjs
Domain
authentication, backend, cloud
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.