package handlers import ( "net/http" "os" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "github.com/logto-io/go/client" ) // SessionStorage implements Logto's Storage interface using Gin sessions // Avoid cookie-based sessions due to size limits; use memory-based sessions for demo // In production, use Redis/MongoDB type SessionStorage struct { session sessions.Session } func (s *SessionStorage) GetItem(key string) string { value := s.session.Get(key) if value == nil { return "" } str, ok := value.(string) if !ok { return "" } return str } func (s *SessionStorage) SetItem(key, value string) { s.session.Set(key, value) s.session.Save() } // getLogtoConfig returns Logto config from environment variables func getLogtoConfig() *client.LogtoConfig { return &client.LogtoConfig{ Endpoint: os.Getenv("LOGTO_ENDPOINT"), AppId: os.Getenv("LOGTO_APP_ID"), AppSecret: os.Getenv("LOGTO_APP_SECRET"), } } // HomeHandler shows auth state and sign-in/sign-out links func HomeHandler(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient(getLogtoConfig(), &SessionStorage{session: session}) authState := "You are not logged in to this website. :(" if logtoClient.IsAuthenticated() { authState = "You are logged in to this website! :)" } homePage := "

Hello Logto

" + "
" + authState + "
" + `
Sign In
` + `
Sign Out
` ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage)) } // SignInHandler starts the Logto sign-in flow func SignInHandler(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient(getLogtoConfig(), &SessionStorage{session: session}) redirectUri := os.Getenv("LOGTO_REDIRECT_URI") signInUri, err := logtoClient.SignIn(redirectUri) if err != nil { ctx.String(http.StatusInternalServerError, err.Error()) return } ctx.Redirect(http.StatusTemporaryRedirect, signInUri) } // CallbackHandler handles the Logto sign-in callback func CallbackHandler(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient(getLogtoConfig(), &SessionStorage{session: session}) err := logtoClient.HandleSignInCallback(ctx.Request) if err != nil { ctx.String(http.StatusInternalServerError, err.Error()) return } // Redirect to the frontend page instead of the backend auth page ctx.Redirect(http.StatusTemporaryRedirect, os.Getenv("BASE_URL")) } // SignOutHandler starts the Logto sign-out flow func SignOutHandler(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient(getLogtoConfig(), &SessionStorage{session: session}) postSignOutRedirectUri := os.Getenv("LOGTO_POST_SIGN_OUT_REDIRECT_URI") signOutUri, err := logtoClient.SignOut(postSignOutRedirectUri) if err != nil { ctx.String(http.StatusOK, err.Error()) return } ctx.Redirect(http.StatusTemporaryRedirect, signOutUri) } // UserIdTokenClaimsHandler returns the user's ID token claims as JSON func UserIdTokenClaimsHandler(ctx *gin.Context) { session := sessions.Default(ctx) logtoClient := client.NewLogtoClient(getLogtoConfig(), &SessionStorage{session: session}) idTokenClaims, err := logtoClient.GetIdTokenClaims() if err != nil { ctx.String(http.StatusOK, err.Error()) return } ctx.JSON(http.StatusOK, idTokenClaims) }