Session returns empty array
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 6.6k
- Forks
- 631
- PR merge metrics
- No merged PRs in 30d
Description
Hi,
Im having a hard time to figure out why package is not working as expected.(Maybe im using wrong lol) but my current expected behavior put google and make it work with vite-react spa app. currently it redirects succesfully but not sets any session or cannot read any session with it so im using go 1.22.4 with;
- github.com/gin-gonic/gin v1.10.0,
- github.com/gorilla/sessions v1.3.0
- github.com/gin-gonic/gin v1.10.0
(im not sure what causes the issue but after fighting straight 3 days with the package to make it work with gin and not getting a result is kinda bummer, i added redirects with sending vales to front as a workaround but its no use after no session to check basically. :( )
Edit: currently using with dev mode
my service for auth.go:
```go
const (
key = "secret" // TODO: change this to a more secure key
MaxAge = int(time.Hour * 24 * 30 / time.Second) // This converts time.Duration to seconds.
)
var BaseUrl string
// var PORT string
func GetBaseURL() string {
BaseUrl = os.Getenv("BACKEND_URL")
// PORT = os.Getenv("PORT")
// return fmt.Sprintf("%s:%s", BaseUrl, PORT)
return BaseUrl
}
// buildAuthCallbackURL builds the callback URL for the authentication provider
// based on the provider name and remember to change the api version if it changes.
func buildAuthCallbackURL(provider string) string {
baseUrl := GetBaseURL()
url := fmt.Sprintf("%s/api/v1/auth/%s/callback", baseUrl, provider)
fmt.Println(url)
return url
}
func NewAuth() {
googleClientID := os.Getenv("GOOGLE_CLIENT_ID")
googleClientSecret := os.Getenv("GOOGLE_CLIENT_SECRET")
store := sessions.NewCookieStore([]byte(key))
fmt.Println("MaxAge", MaxAge)
store.MaxAge(MaxAge)
store.Options.Path = "/"
store.Options.HttpOnly = true
store.Options.Secure = helpers.IsProduction()
store.Options.SameSite = http.SameSiteLaxMode
if helpers.IsProduction() {
store.Options.SameSite = http.SameSiteStrictMode
}
gothic.Store = store
goth.UseProviders(
google.New(googleClientID, googleClientSecret, buildAuthCallbackURL("google")),
)
}
```
public routes to handle callback etc:
public.go:
```go
var (
once sync.Once
websiteURL string
dashURL string
onboardURL string
errorPageURL string
)
// initializeURLs is used to initialize all URLs once.
func initializeURLs() {
websiteURL = os.Getenv("WEB_APP_URL")
if websiteURL == "" {
log.Fatal("WEB_APP_URL is not set, terminating application.")
}
dashURL = fmt.Sprintf("%s/", websiteURL)
onboardURL = fmt.Sprintf("%s/onboarding", websiteURL)
errorPageURL = fmt.Sprintf("%s/auth-error", websiteURL)
}
// getDashboardURL returns the dashboard URL.
func getDashboardURL() string {
once.Do(initializeURLs) // Initialize the URLs
return dashURL
}
// getOnboardingURL returns the onboarding URL.
func getOnboardingURL() string {
once.Do(initializeURLs) // Initialize the URLs
return onboardURL
}
// getErrorPageURL returns the error page URL with an error message.
func getErrorPageURL(errorMessage string) string {
once.Do(initializeURLs) // Initialize the URLs
safeMessage := url.QueryEscape(errorMessage) // Protection against XSS attacks
return fmt.Sprintf("%s?error=%s", errorPageURL, safeMessage)
}
type contextKey string
const providerKey contextKey = "provider"
func GetAuthCallbackFunction(c *gin.Context) {
provider := c.Param("provider")
if provider == "" {
utils.RespondWithError(c, http.StatusBadRequest, "Provider not specified", nil)
return
}
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), providerKey, provider))
// Complete user authentication using gothic package
gothUser, err := gothic.CompleteUserAuth(c.Writer, c.Request)
if err != nil {
log.Printf("Authentication failed: %s", err.Error()) // Log the error
// Show a general error message to the user
errorMessage := "Authentication failed. Please try again or contact support if the problem persists."
c.Redirect(http.StatusFound, getErrorPageURL(errorMessage))
return
}
// Now using provider and social ID to check user existence
userID, exists, err := services.CheckUserExistAndGetID(gothUser.Provider, gothUser.UserID)
if err != nil {
utils.RespondWithError(c, http.StatusInternalServerError, "Database operation failed", err)
return
}
// Redirect based on whether the user exists
var redirectURL string
if exists {
// Redirect URL for existing users
redirectURL = fmt.Sprintf("%s?userID=%s&provider=%s", getDashboardURL(), userID.String(), gothUser.Provider)
} else {
// Redirect to the onboarding URL for new users
redirectURL = fmt.Sprintf("%s?provider=%s&email=%s&picture=%s&socialID=%s",
getOnboardingURL(), gothUser.Provider, url.QueryEscape(gothUser.Email), url.QueryEscape(gothUser.AvatarURL), gothUser.UserID)
}
c.Redirect(http.StatusFound, redirectURL)
}
func LogoutHandler(c *gin.Context) {
provider := c.Param("provider")
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), providerKey, provider))
err := gothic.Logout(c.Writer, c.Request)
if err != nil {
utils.RespondWithError(c, http.StatusInternalServerError, "Failed to log out", err)
return
}
c.Redirect(http.StatusFound, getDashboardURL())
}
func GetAuthHandler(c *gin.Context) {
provider := c.Param("provider")
// Attempt to complete the user authentication from an existing session
if gothUser, err := gothic.CompleteUserAuth(c.Writer, c.Request); err == nil {
// User is already authenticated, redirect to home or another appropriate page
c.JSON(http.StatusFound, gin.H{
"user": gothUser,
"redirectURL": getDashboardURL(), // Suggest where to redirect
})
return
}
// No valid session, need to authenticate
// Add 'provider' to the query parameters of the request
q := c.Request.URL.Query()
q.Add("provider", provider)
c.Request.URL.RawQuery = q.Encode()
// Set the modified request back to context with the provider key
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), providerKey, provider))
// Start the authentication process since no existing session is found
gothic.BeginAuthHandler(c.Writer, c.Request)
}
// GetAuthStatus checks the user's authentication status.
func GetAuthStatus(c *gin.Context) {
// Check the user's session
gothUser, err := gothic.CompleteUserAuth(c.Writer, c.Request)
fmt.Println(gothUser) // this returns an empty map
if err != nil {
utils.RespondWithSuccess(c, gin.H{"isAuthenticated": false}, "User is not authenticated")
} else {
utils.RespondWithSuccess(c, gin.H{"isAuthenticated": true, "user": gothUser}, "User is authenticated")
}
}
```
routes:
```go
authGroup := router.Group("/auth")
{
authGroup.GET("/status", routes.GetAuthStatus)
authProviderGroup := authGroup.Group("/:provider")
{
authProviderGroup.GET("/callback", routes.GetAuthCallbackFunction)
authProviderGroup.GET("/logout", routes.LogoutHandler)
authProviderGroup.GET("/", routes.GetAuthHandler)
}
}
```
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with auth.go's NewAuth session-store setup and public.go's GetAuthCallbackFunction, GetAuthHandler, and GetAuthStatus entry points. Reproduce the Google callback flow and inspect the request used by /auth/status, then trace whether the same session is available after the redirect. Done means the callback establishes a readable session and /auth/status returns the authenticated user instead of an empty value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100