Skip to content

Latest commit

 

History

162 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Logto logo

Logto Go SDKs

Build Status Codecov Go Report Card PkgGoDev

The repo for SDKs and working samples written in Go.

Check out the Go SDK tutorial or Go reference for more information.

Installation

To install Logto Go SDK, use go get.

For core package:

go get github.com/logto-io/go/v2/core

For client package:

go get github.com/logto-io/go/v2/client

To update Logto Go SDK to the latest version, use:

go get -u github.com/logto-io/go/v2/core
go get -u github.com/logto-io/go/v2/client

Packages

Name Description
core Logto SDK core package
client Logto client built upon the core package

User info and ID token claims

client.FetchUserInfo and client.GetIdTokenClaims return core.UserInfoResponse and core.IdTokenClaims respectively. Besides basic claims such as sub, name, email, both types model the standard claims included in the profile scope: family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, locale, as well as created_at and updated_at. Logto only returns these standard claims when their values are not empty, so absent claims are left as zero values.

To access claims that are not modeled as struct fields, e.g. custom claims, use the GetClaim method:

userInfo, err := logtoClient.FetchUserInfo()
if err != nil {
	// Handle error
}

if value, ok := userInfo.GetClaim("custom_claim"); ok {
	// Use the claim value
}

idTokenClaims, err := logtoClient.GetIdTokenClaims()
if err != nil {
	// Handle error
}

if value, ok := idTokenClaims.GetClaim("custom_claim"); ok {
	// Use the claim value
}

Error handling

SDK functions return structured errors that can be inspected with errors.Is and errors.As to turn failures into user actions.

The most common scenario: the stored refresh token has expired or been revoked, so the session cannot be renewed. LogtoClient methods (e.g. GetAccessToken, FetchUserInfo) report this as client.ErrNotAuthenticated, the same error returned when the user has never signed in:

import (
	"errors"

	"github.com/logto-io/go/v2/client"
)

userInfo, err := logtoClient.FetchUserInfo()
if err != nil {
	if errors.Is(err, client.ErrNotAuthenticated) {
		// No valid session, redirect the user to sign in again.
	}
	// Handle other errors.
}

For everything else, any non-200 response from the Logto server is a *core.ResponseError carrying the HTTP status code, the parsed OIDC and Logto error fields, and the raw body:

import "github.com/logto-io/go/v2/core"

var responseError *core.ResponseError
if errors.As(err, &responseError) {
	log.Printf(
		"logto request failed: status=%d, code=%s, description=%s",
		responseError.StatusCode,
		responseError.Code,
		responseError.ErrorDescription,
	)
}

Propagating request context

LogtoClient methods such as SignIn, HandleSignInCallback, GetAccessToken, FetchUserInfo, and SignOut send HTTP requests to Logto. To propagate cancellation, deadlines, and tracing information from the incoming request to those calls, bind the client to the request's context.Context with client.WithContext. A LogtoClient is created per incoming request together with its session storage, so pass that request's context:

func handler(w http.ResponseWriter, r *http.Request) {
	logtoClient := client.NewLogtoClient(logtoConfig, storage, client.WithContext(r.Context()))
	// ...
}

In Gin, use c.Request.Context().

WithContext composes with WithHttpClient, which injects a custom *http.Client. With an OpenTelemetry-instrumented transport, the spans created for SDK requests are parented to the incoming request's span:

// Reuse one instrumented HTTP client across requests.
httpClient := &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}

logtoClient := client.NewLogtoClient(logtoConfig, storage,
	client.WithHttpClient(httpClient),
	client.WithContext(r.Context()),
)

Without WithContext, SDK requests use context.Background(). The core package offers the same capability through Context variants of its request functions, such as core.FetchOidcConfigContext and core.FetchTokenByRefreshTokenContext; the existing functions keep working unchanged.

Running behind a reverse proxy

By default, HandleSignInCallback reconstructs the callback URI from the incoming request, inferring the scheme from the TLS state (or the X-Forwarded-Proto header) and the host from the Host header.

If your application runs behind reverse proxies or multi-layer gateways (e.g., Cloudflare -> ALB -> Nginx, or Firebase Hosting -> Cloud Run), the scheme and host seen by your application may differ from the public address, causing sign-in callbacks to fail with a "callback uri not match redirect uri" error. Trusting these headers also exposes the application to Host header injection.

To avoid this, set the optional BaseUrl in LogtoConfig to the public base URL of your application. It must be an absolute URL with a scheme and may include a path prefix:

logtoConfig := &client.LogtoConfig{
	Endpoint:  "<your-logto-endpoint>",
	AppId:     "<your-application-id>",
	AppSecret: "<your-application-secret>",
	// The public base URL of your application
	BaseUrl:   "https://my-app.com",
}

When BaseUrl is set, the callback URI is constructed by directly appending the incoming request URI (path and query) to it, and the request's Host and X-Forwarded-Proto headers are no longer used. Make sure the concatenated result matches the public callback URL registered as the redirect URI — for example, if a proxy strips a path prefix before forwarding requests to your application, include that prefix in BaseUrl.

Resources

Website Docs Discord

About

☁️ Logto Golang SDKs.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

28 stars

Watchers

4 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages