Skip to main content
Version: Next

Go: Integrate Logto

note

This tutorial assumes you have created an Application of type "Traditional Web" in Admin Console. If you are not ready, read this before continuing.

tip

The following demonstration is built upon the Gin Web Framework. You may also integrate Logto into other frameworks by taking the same steps. In the following code snippets, we assume your app is running on http://localhost:8080.

Add Logto SDK as a dependencyโ€‹

Execute in the project root directory:

go get github.com/logto-io/go

Add the github.com/logto-io/go/client package to your application code:

// main.go
package main

import (
"github.com/gin-gonic/gin"
// Add dependency
"github.com/logto-io/go/client"
)

func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.String(200, "Hello Logto!")
})
router.Run(":8080")
}

Use sessions to store user authentication informationโ€‹

In traditional web applications, the user authentication information will be stored in the user session.

Logto SDK provides a Storage interface, you can implement a Storage adapter based on your web framework so that the Logto SDK can store user authentication information in the session.

note

We do NOT recommend using cookie-based sessions, as user authentication information stored by Logto may exceed the cookie size limit. In this example, we use memory-based sessions. You can use Redis, MongoDB, and other technologies in production to store sessions as needed.

The Storage type in the Logto SDK is as follows:

// github.com/logto-io/client/storage.go
package client

type Storage interface {
GetItem(key string) string
SetItem(key, value string)
}

We will use github.com/gin-contrib/sessions as an example to demonstrate this process.

Apply session middlewareโ€‹

Apply the github.com/gin-contrib/sessions middleware to the application, so that we can get the user session by the user request context in the route handler:

package main

import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/memstore"
"github.com/gin-gonic/gin"
"github.com/logto-io/go/client"
)

func main() {
router := gin.Default()

// We use memory-based session in this example
store := memstore.NewStore([]byte("your session secret"))
router.Use(sessions.Sessions("logto-session", store))

router.GET("/", func(ctx *gin.Context) {
// Get user session
session := sessions.Default(ctx)
// ...
ctx.String(200, "Hello Logto!")
})
router.Run(":8080")
}

Create session storage for Logto to store user authentication informationโ€‹

Create a session_storage.go file, define a SessionStorage and implement the Logto SDK's Storage interfaces:

// session_storage.go
package main

import (
"github.com/gin-contrib/sessions"
)

type SessionStorage struct {
session sessions.Session
}

func (storage *SessionStorage) GetItem(key string) string {
value := storage.session.Get(key)
if value == nil {
return ""
}
return value.(string)
}

func (storage *SessionStorage) SetItem(key, value string) {
storage.session.Set(key, value)
storage.session.Save()
}

Now, in the route handler, you can create a session storage for Logto as follows:

session := sessions.Default(ctx)
sessionStorage := &SessionStorage{session: session}

Init LogtoClientโ€‹

Create LogtConfigโ€‹

tip

You can find and copy "App Secret" from application details page in Admin Console:

App Secret
// main.go
func main() {
// ...
logtoConfig := &client.LogtoConfig{
Endpoint: "<your-logto-endpoint>", // E.g. http://localhost:3001
AppId: "<your-application-id>",
AppSecret: "<your-application-secret>",
}
// ...
}

Init LogtoClient for each user requestโ€‹

// main.go
func main() {
// ...

router.GET("/", func(ctx *gin.Context) {
// Init LogtoClient
session := sessions.Default(ctx)
logtoClient := client.NewLogtoClient(
logtoConfig,
&SessionStorage{session: session},
)

// Use Logto to control the content of the home page
authState := "You are not logged in to this website. :("

if logtoClient.IsAuthenticated() {
authState = "You are logged in to this website! :)"
}

homePage := `<h1>Hello Logto</h1>` +
"<div>" + authState + "</div>"

ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage))
})

// ...
}

Sign Inโ€‹

The sign-in flow can be simplified as:

Web sign-in flow

Configure Redirect URIโ€‹

Let's switch to the Application details page of Admin Console in this section. Add a Redirect URI http://localhost:8080/sign-in-callback and click "Save Changes".

Redirect URI in Admin Console

Redirect URI is an OAuth 2.0 concept which implies the location should redirect after authentication.

Add a route for handling sign-in requestsโ€‹

//main.go
func main() {
// ...

// Add a link to perform a sign-in request on the home page
router.GET("/", func(ctx *gin.Context) {
// ...
homePage := `<h1>Hello Logto</h1>` +
"<div>" + authState + "</div>" +
// Add link
`<div><a href="/sign-in">Sign In</a></div>`

ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage))
})

// Add a route for handling sign-in requests
router.GET("/sign-in", func(ctx *gin.Context) {
session := sessions.Default(ctx)
logtoClient := client.NewLogtoClient(
logtoConfig,
&SessionStorage{session: session},
)

// The sign-in request is handled by Logto.
// The user will be redirected to the Redirect URI on signed in.
signInUri, err := logtoClient.SignIn("http://localhost:8080/sign-in-callback")
if err != nil {
ctx.String(http.StatusInternalServerError, err.Error())
return
}

// Redirect the user to the Logto sign-in page.
ctx.Redirect(http.StatusTemporaryRedirect, signInUri)
})

// ...
}

Add a route for handling sign-in callback requestsโ€‹

When the user signs in successfully on the Logto sign-in page, Logto will redirect the user to the Redirect URI.

Since the Redirect URI is http://localhost:8080/sign-in-callback, we add the /sign-in-callback route to handle the callback after signing in.

// main.go
func main() {
// ...

// Add a route for handling sign-in callback requests
router.GET("/sign-in-callback", func(ctx *gin.Context) {
session := sessions.Default(ctx)
logtoClient := client.NewLogtoClient(
logtoConfig,
&SessionStorage{session: session},
)

// The sign-in callback request is handled by Logto
err := logtoClient.HandleSignInCallback(ctx.Request)
if err != nil {
ctx.String(http.StatusInternalServerError, err.Error())
return
}

// Jump to the page specified by the developer.
// This example takes the user back to the home page.
ctx.Redirect(http.StatusTemporaryRedirect, "/")
})

// ...
}

Sign outโ€‹

Configure Post Sign-out Redirect URIโ€‹

Let's switch to the Application details page of Admin Console in this section. Add a Post Sign-out Redirect URI http://localhost:8080 and click "Save Changes".

Post Sign-out Redirect URI in Admin Console

Post Sign-outRedirect URI is an OAuth 2.0 concept which implies the location should redirect after signing out.

This configuration enables the user to return to the home page after signing out.

Add a route for handling signing out requestsโ€‹

//main.go
func main() {
// ...

// Add a link to perform a sign-out request on the home page
router.GET("/", func(ctx *gin.Context) {
// ...
homePage := `<h1>Hello Logto</h1>` +
"<div>" + authState + "</div>" +
`<div><a href="/sign-in">Sign In</a></div>` +
// Add link
`<div><a href="/sign-out">Sign Out</a></div>`

ctx.Data(http.StatusOK, "text/html; charset=utf-8", []byte(homePage))
})

// Add a route for handling signing out requests
router.GET("/sign-out", func(ctx *gin.Context) {
session := sessions.Default(ctx)
logtoClient := client.NewLogtoClient(
logtoConfig,
&SessionStorage{session: session},
)

// The sign-out request is handled by Logto.
// The user will be redirected to the Post Sign-out Redirect URI on signed out.
signOutUri, signOutErr := logtoClient.SignOut("http://localhost:8080")

if signOutErr != nil {
ctx.String(http.StatusOK, signOutErr.Error())
return
}

ctx.Redirect(http.StatusTemporaryRedirect, signOutUri)
})

// ...
}

After the user makes a signing-out request, Logto will clear all user authentication information in the session.

Fetch user informationโ€‹

Logto SDK helps you fetch the user information from the OIDC UserInfo Endpoint.

You can get the user information by calling logtoClient.FetchUserInfo() after signing in.

The user information response will vary based on the scopes used in the LogtoConfig while initializing the LogtoClient; and the following table lists the relations between user information and scopes:

Field NameTypeRequired ScopeNotes
substringopenidThe openid scope is added by default.
namestringprofileThe profile scope is added by default.
usernamestringprofileThe profile scope is added by default.
picturestringprofileThe profile scope is added by default.
emailstringemail
email_verifiedbooleanemail
phone_numberstringphone
phone_number_verifiedbooleanphone
custom_dataobjectcustom_data
identitiesobjectidentities

Further readingsโ€‹