markbates / goth

Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.
https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b
MIT License
5.45k stars 587 forks source link

Using the example app (with just Google auth) - "could not find a matching session for this request" #564

Closed jvincilione closed 3 months ago

jvincilione commented 3 months ago

I'm running an app using Gin and wanted to add Google and possibly a couple other OAuth providers. I ran into an issue where I'm consistently getting the error "could not find a matching session for this request".

After debugging I noticed that "_gothic_session" is always empty. Assuming I was doing something wrong, I found a few implementations and tutorials online, but none of them used gorilla/sessions except for one. After pulling the repo for one not using sessions I got a secure key error. I tried the one using sessions and received the "could not find a matching session for this request" error after going through the google login screens

I decided to pull the example application in this repo - stripped it down to just use google (and run on :8080), and got the same result. So, I added gorilla/sessions and tried again. While I'm able to go through the google auth flow (selecting my user, etc), it errors "could not find a matching session for this request".

Here is the full code I tried to run - with the google Client ID/Secret redacted:

package main

import (
    "fmt"
    "html/template"
    "log"
    "net/http"
    "sort"

    "github.com/gorilla/pat"
    "github.com/gorilla/sessions"
    "github.com/markbates/goth"
    "github.com/markbates/goth/gothic"
    "github.com/markbates/goth/providers/google"
)

func main() {
    store := sessions.NewCookieStore([]byte("My_Secure_key"))
    store.MaxAge(86400 * 30)
    store.Options.Path = "/"
    store.Options.HttpOnly = true
    store.Options.Secure = false
    store.Options.SameSite = http.SameSiteStrictMode

    gothic.Store = store
    goth.UseProviders(
        google.New("CLIENT_ID", "CLIENT_SECRET", "http://127.0.0.1:8080/auth/google/callback"),
    )

    m := map[string]string{
        "google": "Google",
    }
    var keys []string
    for k := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    providerIndex := &ProviderIndex{Providers: keys, ProvidersMap: m}

    p := pat.New()
    p.Get("/auth/{provider}/callback", func(res http.ResponseWriter, req *http.Request) {

        user, err := gothic.CompleteUserAuth(res, req)
        if err != nil {
            fmt.Fprintln(res, err)
            return
        }
        t, _ := template.New("foo").Parse(userTemplate)
        t.Execute(res, user)
    })

    p.Get("/logout/{provider}", func(res http.ResponseWriter, req *http.Request) {
        gothic.Logout(res, req)
        res.Header().Set("Location", "/")
        res.WriteHeader(http.StatusTemporaryRedirect)
    })

    p.Get("/auth/{provider}", func(res http.ResponseWriter, req *http.Request) {
        // try to get the user without re-authenticating
        if gothUser, err := gothic.CompleteUserAuth(res, req); err == nil {
            t, _ := template.New("foo").Parse(userTemplate)
            t.Execute(res, gothUser)
        } else {
            gothic.BeginAuthHandler(res, req)
        }
    })

    p.Get("/", func(res http.ResponseWriter, req *http.Request) {
        t, _ := template.New("foo").Parse(indexTemplate)
        t.Execute(res, providerIndex)
    })

    log.Println("listening on localhost:3000")
    log.Fatal(http.ListenAndServe(":8080", p))
}

type ProviderIndex struct {
    Providers    []string
    ProvidersMap map[string]string
}

var indexTemplate = `{{range $key,$value:=.Providers}}
    <p><a href="/auth/{{$value}}">Log in with {{index $.ProvidersMap $value}}</a></p>
{{end}}`

var userTemplate = `
<p><a href="/logout/{{.Provider}}">logout</a></p>
<p>Name: {{.Name}} [{{.LastName}}, {{.FirstName}}]</p>
<p>Email: {{.Email}}</p>
<p>NickName: {{.NickName}}</p>
<p>Location: {{.Location}}</p>
<p>AvatarURL: {{.AvatarURL}} <img src="{{.AvatarURL}}"></p>
<p>Description: {{.Description}}</p>
<p>UserID: {{.UserID}}</p>
<p>AccessToken: {{.AccessToken}}</p>
<p>ExpiresAt: {{.ExpiresAt}}</p>
<p>RefreshToken: {{.RefreshToken}}</p>
`

For reference, I tried both localhost and 127.0.0.1 along with adding my domain to my hosts file and trying it that way. All resulted in the same. Additionally, I also tried adding a domain to the session options.

Go Version: go version go1.22.4 darwin/amd64 MacOS Sonoma 14.5

jvincilione commented 3 months ago

Of course, the moment I post this after fighting it for 5 hours I get it to work.

manavkush commented 3 months ago

Hey @jvincilione , I'm having the exact same error. What helped you in getting it to work.

matwate commented 3 months ago

I also have the same error @jvincilione, can you explain how to get it to work?

manavkush commented 3 months ago

Got it working. The problem with me was that the gorilla/sessions main branch was broken for golang versions <= 1.22. I downgraded to the last release of the package. Then the error was resolved.

jvincilione commented 3 months ago

I also have the same error @jvincilione, can you explain how to get it to work?

@matwate My issue was that I had store.Options.SameSite = http.SameSiteStrictMode.

My setup is substantially different from when I wrote this, but currently, I have store.Options.SameSite = http.SameSiteLaxMode instead and don't have any issues.

xargr commented 3 months ago

thx @jvincilione ! you saved my day...

I can confirm that store.Options.SameSite = http.SameSiteLaxMode works!

cocoza4 commented 3 months ago

with store.Options.SameSite = http.SameSiteLaxMode I still face the Error #01: could not find a matching session for this request. Anyone still having this error?

jvincilione commented 2 months ago

@cocoza4 - there are several things that can cause this issue, what have you tried?

meersajid commented 1 week ago

I wasted 2 days on this... was failing in chrome while firefox was fine. SameSiteLaxMode works in both now