Enterprise-ready auth that is secure by default, truly multi-tenant, and ungated for small businesses.
Enterprise-ready authentication for multi-tenant Go applications using OAuth 2.1 and OpenID Connect standards.
This SDK provides complete authentication integration with Wristband, including:
- Login flow - Redirect to Wristband and handle OAuth callbacks
- Session management - Flexible session storage with pluggable session manager interface
- Token handling - Automatic access token refresh
- Logout flow - Token revocation and session cleanup
- Multi-tenancy - Support for tenant subdomains and custom domains
Learn more about Wristband's authentication patterns:
💡 Learn by Example
Want to see the SDK in action? Check out our Go demo application. The demo showcases a lightweight integration with the SDK.
- Requirements
- Installation
- Usage
- Auth Configuration Options
- Auth API
- Related Wristband SDKs
- Wristband Multi-Tenant Go Demo App
- Questions
⚡ Try Our Go Quickstart!
For the fastest way to get started with Go authentication, follow our Quick Start Guide. It walks you through setting up a working Go app with Wristband authentication in minutes. Refer back to this README for comprehensive documentation and advanced usage patterns.
Before installing, ensure you have:
- Go >= 1.24
Add the go-auth package to your project:
go get github.com/wristband-dev/go-authFirst, create an instance of WristbandAuth in your Go application. Then, you can use this instance across your project.
// main.go
package main
import (
"log"
"net/http"
"time"
goauth "github.com/wristband-dev/go-auth"
)
func main() {
// Create the authentication configuration
authConfig := &goauth.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
WristbandApplicationVanityDomain: "your-app.wristband.dev",
}
// Create the Wristband auth instance
wristbandAuth, err := authConfig.WristbandAuth(
// Optional: You can also supply your own HTTP client
goauth.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
)
if err != nil {
log.Fatal("Failed to create Wristband auth:", err)
}
}This Wristband authentication SDK is unopinionated about how you store and manage your application session data after the user has authenticated. We typically recommend encrypted cookie-based sessions due to it being lighter-weight and not requiring a backend session store like Redis or other technologies.
This guide shows an example of a lightweight implementation of a SessionManager using github.com/gorilla/sessions that you can use in your own project.
The SDK defines a SessionManager interface that you need to implement. The session data is typically stored in an encrypted cookie, and on subsequent requests, the middleware decrypts the cookie and restores the session state. It defines three methods that your implementation must provide:
type SessionManager interface {
StoreSession(w http.ResponseWriter, r *http.Request, session *goauth.Session) error
GetSession(r *http.Request) (*goauth.Session, error)
ClearSession(w http.ResponseWriter, r *http.Request) error
}- First, run the following to download the Gorilla sessions library:
go get github.com/gorilla/sessions- Then copy this implementation into your project (e.g.
session.go):
// session.go
package main
import (
"encoding/json"
"errors"
"net/http"
"github.com/gorilla/sessions"
goauth "github.com/wristband-dev/go-auth"
)
const (
// SessionName is the name used for the session cookie
SessionName = "session"
// SessionKey is the key used to store auth data in the session
SessionKey = "auth_session"
)
// GorillaSessionManager implements the goauth.SessionManager interface
// using gorilla/sessions for session management
type GorillaSessionManager struct {
store sessions.Store
}
func NewSessionStore(secret []byte, secureCookies bool) goauth.SessionManager {
store := sessions.NewCookieStore(secret, nil)
store.Options.Secure = secureCookies
store.Options.HttpOnly = true
store.Options.SameSite = http.SameSiteLaxMode
store.Options.MaxAge = 3600 // 1 hour
return &GorillaSessionManager{
store: store,
}
}
// StoreSession implements the goauth.SessionManager interface
func (m *GorillaSessionManager) StoreSession(w http.ResponseWriter, r *http.Request, session *goauth.Session) error {
// Get existing session or create a new one
sess, err := m.store.Get(r, SessionName)
if err != nil {
// If there's an error getting the session, create a new one
// This can happen if the session was tampered with or is invalid
sess, err = m.store.New(r, SessionName)
if err != nil {
return err
}
}
// Serialize the session to JSON
sessionJSON, err := json.Marshal(session)
if err != nil {
return err
}
// Store the serialized session in the session store
sess.Values[SessionKey] = string(sessionJSON)
// Save the session
return sess.Save(r, w)
}
// GetSession implements the goauth.SessionManager interface
func (m *GorillaSessionManager) GetSession(r *http.Request) (*goauth.Session, error) {
// Get existing session
sess, err := m.store.Get(r, SessionName)
if err != nil {
return nil, err
}
// Check if the session contains auth data
sessionJSON, ok := sess.Values[SessionKey]
if !ok {
return nil, errors.New("no auth session found")
}
// Parse the serialized session
var authSession goauth.Session
err = json.Unmarshal([]byte(sessionJSON.(string)), &authSession)
if err != nil {
return nil, err
}
return &authSession, nil
}
// ClearSession implements the goauth.SessionManager interface
func (m *GorillaSessionManager) ClearSession(w http.ResponseWriter, r *http.Request) error {
// Get existing session
sess, err := m.store.Get(r, SessionName)
if err != nil {
// If we can't get the session, that's fine - we wanted to clear it anyway
return nil
}
// Remove the auth data from the session
delete(sess.Values, SessionKey)
// Set session to expire
sess.Options.MaxAge = -1
// Save the session
return sess.Save(r, w)
}- After creating your
SessionManagerimplementation, you'll use it to create a Wristband app instance.
// main.go
package main
import (
goauth "github.com/wristband-dev/go-auth"
)
func main() {
authConfig := &goauth.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
WristbandApplicationVanityDomain: "your-app.wristband.dev",
}
wristbandAuth, err := authConfig.WristbandAuth()
if err != nil {
// Handle error
}
// ADD: Create session manager
// NOTE: You'll need to provide a secure, random 32-character secret as the secret value.
sessionManager := NewSessionStore("<your-generated-secret>", true)
// ADD: Create the Wristband app with the session manager
app := wristbandAuth.NewApp(sessionManager)
}Create an auth middleware using the goauth.Middlewares type in your project (e.g. main.go file). Apply app.RequireAuthentication to any handler that should require a valid authenticated session. This middleware retrieves the session from your configured SessionManager, verifies that the access token is still valid (refreshing it when necessary), and stores the session in the request context for downstream handlers. If no valid session exists, the request will receive a 401 Unauthorized response or be redirected to the login URL if token refresh fails.
// main.go
package main
import (
goauth "github.com/wristband-dev/go-auth"
)
func main() {
authConfig := &goauth.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
WristbandApplicationVanityDomain: "your-app.wristband.dev",
}
wristbandAuth, err := authConfig.WristbandAuth()
if err != nil {
// Handle error
}
sessionManager := NewSessionStore("<your-generated-secret>", true)
app := wristbandAuth.NewApp(sessionManager)
// ADD: Create middleware chain for protected endpoints
authMiddlewares := goauth.Middlewares{
app.RequireAuthentication
}
}There are four core API endpoints your Go server should expose to facilitate authentication workflows in Wristband:
- Login Endpoint
- Callback Endpoint
- Logout Endpoint
- Session Endpoint
You'll need to add these endpoints to your Go routes. There's also one additional endpoint you can implement depending on your authentication needs:
- Token Endpoint (optional)
The goal of the Login Endpoint is to initiate an auth request by redirecting to the Wristband Authorization Endpoint. It will store any state tied to the auth request in a Login State Cookie, which will later be used by the Callback Endpoint. The frontend of your application should redirect to this endpoint when users need to log in to your application.
// Create the Wristband app with your session manager and callback URL
app := wristbandAuth.NewApp(sessionManager)
// Login Endpoint - initiates the auth request and redirects to Wristband
http.Handle("/api/auth/login", app.LoginHandler())Options
Optional configuration can be provided using various LoginOpt functions. Refer to the Login section for more details.
The goal of the Callback Endpoint is to receive incoming calls from Wristband after the user has authenticated and ensure that the Login State cookie contains all auth request state in order to complete the Login Workflow. From there, it will call the Wristband Token Endpoint to fetch necessary JWTs, call the Wristband Userinfo Endpoint to get the user's data, and create a session for the application containing the JWTs and user data.
// Callback Endpoint - completes auth and creates the application session
http.Handle("/api/auth/callback",
app.CallbackHandler(
// Replace with your own default return URL.
goauth.WithCallbackRedirectURL("http://localhost:5173/home"),
),
)The goal of the Logout Endpoint is to destroy the application's session that was established during the Callback Endpoint execution. If refresh tokens were requested during the Login Workflow, then a call to the Wristband Revoke Token Endpoint will occur. It then will redirect to the Wristband Logout Endpoint in order to destroy the user's authentication session within the Wristband platform. From there, Wristband will send the user to the Tenant-Level Login Page (unless configured otherwise).
// Logout Endpoint - revokes refresh token, clears session, redirects to Wristband logout
http.Handle("/api/auth/logout", app.LogoutHandler())Options
Optional configuration can be provided using various LogoutOption functions. Refer to the Logout API section for more details.
Note
This endpoint is required for Wristband frontend SDKs to function. For more details, see the Wristband Session Management documentation.
Wristband frontend SDKs require a Session Endpoint in your backend to verify authentication status and retrieve session metadata. Create a protected session endpoint that uses app.SessionHandler() to return the session response format expected by Wristband's frontend SDKs. The response model will always have a UserId and a TenantId in it. You can include any additional data for your frontend by customizing the Metadata parameter (optional), which requires JSON-serializable values. The response must not be cached.
⚠️ Important: Make sure to protect this endpoint by using the auth middleware!
// Session Endpoint - returns session data to the frontend
http.Handle(
"/api/auth/session",
authMiddlewares.Apply(app.SessionHandler())
)The Session Endpoint returns a SessionEndpointResponse to your frontend:
{
"tenantId": "tenant_abc123",
"userId": "user_xyz789",
"metadata": {
"foo": "bar",
// Any other optional data you provide...
}
}After implementing the core auth endpoints, make sure to include them in your Go application main.go file.
// main.go
package main
import (
goauth "github.com/wristband-dev/go-auth"
)
func main() {
authConfig := &goauth.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
WristbandApplicationVanityDomain: "your-app.wristband.dev",
}
wristbandAuth, err := authConfig.WristbandAuth()
if err != nil {
// Handle error
}
sessionManager := NewSessionStore("<your-generated-secret>", true)
app := wristbandAuth.NewApp(sessionManager)
authMiddlewares := goauth.Middlewares{
app.RequireAuthentication
}
// ADD: Map auth endpoints
http.Handle("/api/auth/login", app.LoginHandler())
http.Handle(
"/api/auth/callback",
app.CallbackHandler(
goauth.WithCallbackRedirectURL("<default_return_url>")
)
)
http.Handle("/api/auth/logout", app.LogoutHandler())
http.Handle(
"/api/auth/session",
authMiddlewares.Apply(app.SessionHandler())
)
}Note
This endpoint is required when your frontend needs to make authenticated API requests directly to Wristband or other protected services. For more details, see the Wristband documentation on using access tokens from the frontend.
If your application doesn't need frontend access to tokens (e.g., all API calls go through your backend), you can skip this endpoint.
Some applications require the frontend to make direct API calls to Wristband or other protected services using the user's access token. The Token Endpoint provides a secure way for your frontend to retrieve the current access token and its expiration time without exposing it in the session cookie or in browser storage.
Create a protected token endpoint that uses app.TokenHandler() to return the token data expected by Wristband's frontend SDKs. The response must not be cached.
⚠️ Important: Make sure to protect this endpoint by using the auth middleware!
// Token Endpoint - returns token data to the frontend
http.Handle(
"/api/auth/token",
authMiddlewares.Apply(app.TokenHandler())
)The Token Endpoint returns a TokenEndpointResponse to your frontend:
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresAt": 1735689600000
}Your frontend can then use the accessToken in the Authorization header when making API requests:
const tokenResponse = await fetch('/api/auth/token');
const { accessToken } = await tokenResponse.json();
// Use token to call Wristband API
const userResponse = await fetch('https://<your-wristband-app-vanity_domain>/api/v1/users/123', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});To protect an endpoint from unauthenticated access, apply the auth middleware as shown below:
// Protected endpoint handler
protectedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"message": "This is a protected route"}`))
})
// Apply middleware to the registered protected endpoint
http.Handle("/api/protected", authMiddlewares.Apply(protectedHandler))Now, if somebody tries to call this API without a valid session, a 401 Unauthorized response will be returned.
Note
This section is only applicable if you need to call Wristband APIs or protect your own backend services with Wristband tokens.
If you intend to utilize Wristband APIs within your application or secure any backend APIs or downstream services using the access token provided by Wristband, you must include this token in the Authorization HTTP request header.
Authorization: Bearer <access_token_value>When using auth middleware, the session is automatically added to the request context. You can access it using goauth.SessionFromContext in order to get the access token:
func apiCallHandler(w http.ResponseWriter, r *http.Request) {
// Retrieve session set by RequireAuthentication
session := goauth.SessionFromContext(r.Context())
if session == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Make API call with access token
req, err := http.NewRequest("GET", "https://api.yourapp.com/data", nil)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
req.Header.Set("Authorization", "Bearer "+session.AccessToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// Process response
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}For scenarios where your frontend needs to make direct API calls with the user's access token, use the Token Endpoint to securely retrieve the current access token.
The AuthConfig struct provides comprehensive configuration options for the SDK:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ClientID | string | Yes | - | The client ID for the application. |
| ClientSecret | string | Yes | - | The client secret for the application. |
| WristbandApplicationVanityDomain | string | Yes | - | The vanity domain of the Wristband application. |
| AutoConfigureEnabled | bool | No | true |
Tells the SDK to automatically set some configuration values by calling to Wristband's SDK Auto-Configuration Endpoint. Any manually provided configurations will take precedence over the configs returned from the endpoint. |
| LoginStateSecret | string | No | ClientSecret | A secret (32 or more characters in length) used for encryption and decryption of login state cookies. For enhanced security, it is recommended to provide a value that is unique from the client secret. |
| LoginURL | string | No | Auto-configured | The URL for initiating the login request. Required when auto-configure is disabled. |
| RedirectURI | string | No | Auto-configured | The redirect URI for callback after authentication. Required when auto-configure is disabled. |
| CustomApplicationLoginPageURL | string | No | Auto-configured | The custom application login (tenant discovery) page URL if you are self-hosting the application login/tenant discovery UI. |
| DangerouslyDisableSecureCookies | bool | No | false |
If set to true, the "Secure" attribute will not be included in any cookie settings. This should only be done when testing in local development. |
| IsApplicationCustomDomainActive | *bool | No | Auto-configured | Indicates whether an application-level custom domain is active in your Wristband application. |
| ParseTenantFromRootDomain | string | No | Auto-configured | The root domain for your application from which to parse out the tenant domain name. Indicates whether tenant subdomains are used for authentication. |
| Scopes | []string | No | ["openid", "offline_access", "email"] |
The scopes required for authentication. |
| TokenExpirationBuffer | int | No | 60 |
The buffer time (in seconds) to subtract from the access token's expiration time. This causes the token to be treated as expired before its actual expiration, helping to avoid token expiration during API calls. |
The SDK supports both manual configuration and autoconfiguration via the ConfigResolver:
// Configure Wristband authentication with autoconfiguration enabled
authConfig := &goauth.AuthConfig{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
WristbandApplicationVanityDomain: "your-app.wristband.dev",
AutoConfigureEnabled: true, // Enable auto-configuration
Scopes: []string{"openid", "offline_access", "email"},
TokenExpirationBuffer: 120, // 2 minutes
}
// Create Wristband auth instance (with optional custom HTTP client)
wristbandAuth, err := authConfig.WristbandAuth(
goauth.WithHTTPClient(customHTTPClient),
)
if err != nil {
log.Fatal("Failed to create Wristband auth:", err)
}Available Options
The WristbandAuth() method accepts the following optional parameters:
| Option | Description |
|---|---|
WithHTTPClient(client *http.Client) |
Uses a custom HTTP client for API requests. |
WithCookieEncryption(cookieEncryption CookieEncryption) |
Provides a custom cookie encryption implementation. |
Configure cookie options for session storage:
app := wristbandAuth.NewApp(sessionManager,
goauth.WithCookieOptions(goauth.CookieOptions{
Domain: ".yourapp.com",
Path: "/",
MaxAge: 86400, // 24 hours
// DangerouslyDisableSecureCookies: true, // Only for development
}),
)This section covers integration with the SDK for custom HTTP handlers instead of the provided ones. It can also be used when working with a web framework.
There are some abstractions around http requests and responses to enable integration with various web frameworks. Implementations are provided for the standard library which are used in the Wristband provided http.Handler implementations.
They are abstracted away using the HTTPContext interface. This interface is used to:
- Read URI information using the
- Listing request cookie names and retrieving a request cookie via the
cookies.CookieRequestinterface. - Writing response cookies.
WristbandAuth.HandleLogin initiates the authentication flow by generating an authorization URL and storing login state in a cookie. Refer to LoginHandler for an example.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| httpCtx | HTTPContext | Yes | The HTTP request/response context |
| options | *LoginOption | Yes | Optional configuration for the login flow. |
Returns
| Type | Description |
|---|---|
| string | The authorization url to redirect to with the necessary query parameters set. |
| error | Any error that occurred during login flow. |
These are all optional configurations to be used for building the authorize request URL. The LoginOpt interface provides the ability to set these configurations.
| Option | Description |
|---|---|
WithCustomState |
Custom state data for the login request.. |
WithReturnURL |
URL to return to after authentication is completed. If a value is provided, then it takes precedence over the return_url request query parameter |
WithDefaultTenantCustomDomain |
Tenant custom domain for the login request if the name cannot be found in either the subdomain or the "tenant_custom_domain" query parameter. |
WithDefaultTenantName |
Tenant name for the login request if the name cannot be found in either the subdomain or the "tenant_name" query parameter |
Use the NewLoginOptions function to initialize the configuration to be provided to HandleLogin.
Wristband supports multiple tenant domain configurations, including tenant subdomains and tenant custom domains. When the Go SDK constructs the Wristband Authorize URL during Login(), it resolves the tenant domain using the following precedence order:
tenant_custom_domainquery parameter: If provided, this takes top priority.- Tenant subdomain in the request host: Used when
AuthConfig.ParseTenantFromRootDomainis configured and the incoming request host contains a tenant subdomain. tenant_namequery parameter: Evaluated when tenant subdomains are not being used.WithDefaultTenantCustomDomain(domain string)login option: Used if none of the above are present.WithDefaultTenantName(name string)login option: Used as the final fallback.
If none of these are specified, Login() returns the Application-Level Login (Tenant Discovery) URL (or your configured CustomApplicationLoginPageURL), which your login handler should redirect the user to.
If your application does not wish to utilize tenant subdomains, you can pass the tenant_name query parameter to your Login Endpoint and the SDK will use it when generating the Wristband Authorize URL.
GET https://yourapp.io/auth/login?tenant_name=customer01Your AuthConfig might look like the following when manually configuring URLs (no tenant subdomains):
authConfig := goauth.NewAuthConfig(
"ic6saso5hzdvbnof3bwgccejxy",
"30e9977124b13037d035be10d727806f",
"yourapp-yourcompany.us.wristband.dev",
goauth.WithAutoConfigureDisabled(
"https://yourapp.io/auth/login",
"https://yourapp.io/auth/callback",
),
)
wristbandAuth, err := authConfig.WristbandAuth()
if err != nil {
// handle error
}If your application wishes to utilize tenant subdomains, then you do not need to pass a query param when redirecting to your Login Endpoint. The SDK will parse the tenant subdomain from the request host when AuthConfig.ParseTenantFromRootDomain is configured. When using tenant subdomains, your configured login_url and redirect_uri must contain the {tenant_name} placeholder.
GET https://customer01.yourapp.io/auth/loginYour AuthConfig might look like the following when manually configuring URLs for tenant subdomains:
authConfig := goauth.NewAuthConfig(
"ic6saso5hzdvbnof3bwgccejxy",
"30e9977124b13037d035be10d727806f",
"yourapp-yourcompany.us.wristband.dev",
goauth.WithParseTenantFromRootDomain("yourapp.io"),
goauth.WithAutoConfigureDisabled(
"https://{tenant_name}.yourapp.io/auth/login",
"https://{tenant_name}.yourapp.io/auth/callback",
),
)
wristbandAuth, err := authConfig.WristbandAuth()
if err != nil {
// handle error
}For certain use cases, it may be useful to specify a default tenant name in the event that HandleLogin cannot resolve a tenant name in either the request query parameters or the URL subdomain. You can specify a fallback default tenant name via WithDefaultTenantName(...):
http.HandleFunc("/auth/login", app.LoginHandler(
goauth.WithDefaultTenantName("default"),
))If your application wishes to utilize tenant custom domains, you can pass the tenant_custom_domain query parameter to your Login Endpoint, and the SDK will be able to make the appropriate redirection to the Wristband Authorize Endpoint.
GET https://yourapp.io/auth/login?tenant_custom_domain=mytenant.comThe tenant custom domain takes precedence over all other possible domains else when present.
For certain use cases, it may be useful to specify a default tenant custom domain in the event that HandleLogin cannot find a tenant custom domain in the query parameters. You can specify a fallback default tenant custom domain via WithDefaultTenantCustomDomain(...):
http.HandleFunc("/auth/login", app.LoginHandler(
goauth.WithDefaultTenantCustomDomain("mytenant.com"),
))The default tenant custom domain takes precedence over all other possible domain configurations when present except for the case where the tenant_custom_domain query parameter exists in the request.
Before your Login Endpoint redirects to Wristband, it will create a Login State Cookie to cache all necessary data required in the Callback Endpoint. You can inject additional state into that cookie by setting LoginOptions.CustomState:
http.HandleFunc("/auth/login", app.LoginHandler(goauth.WithCustomState(map[string]any{"test": "abc"}))Warning
Injecting custom state is an advanced feature, and it is recommended to use customState sparingly. Most applications may not need it at all. The max cookie size is 4kB. From our own tests, passing a customState JSON of at most 1kB should be a safe ceiling.
Example
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
authURL, err := wristbandAuth.Login(w, r,
goauth.WithDefaultTenantName("default-tenant"),
)
if err != nil {
http.Error(w, "Login failed", http.StatusInternalServerError)
return
}
http.Redirect(w, r, authURL, http.StatusFound)
})Handles the OAuth callback from Wristband, exchanges the authorization code for tokens, and retrieves user information.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| httpCtx | HTTPContext | Yes | The HTTP request/response context |
Returns
| Type | Description |
|---|---|
| *CallbackContext | Contains tokens, user info, and redirect URL. |
| error | Any error that occurred during callback processing. |
When an error is returned, use goauth.IsRedirectError in case the request should be redirected. If so, log the error and redirect.
CallbackContext Fields
| Field | Type | Description |
|---|---|---|
| TokenResponse | TokenResponse | OAuth tokens (access, refresh, ID tokens, and expiration). |
| LoginState | LoginState | Login context (return URL, nonce, code verifier, state). |
| UserInfo | UserInfoResponse | User information from the Wristband Userinfo endpoint. |
| TenantName | string | The tenant name for the authenticated user. |
| TenantCustomDomain | string | Custom domain for the tenant. |
Example
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
result, err := wristbandAuth.Callback(w, r)
if err != nil {
// Check for redirection.
if redirectError, ok := goauth.IsRedirectError(err); ok {
http.Redirect(res, req, redirectError.URL, http.StatusSeeOther)
}
http.Error(w, "Callback failed", http.StatusInternalServerError)
return
}
// Store session and redirect
session := &goauth.Session{
AccessToken: result.AccessToken,
RefreshToken: result.RefreshToken,
ExpiresAt: result.ExpiresAt,
UserId: result.UserInfo.Sub,
TenantId: result.TenantId,
}
sessionManager.StoreSession(r.Context(), w, r, session)
http.Redirect(w, r, result.RedirectURL, http.StatusFound)
})Generates a logout URL for the Wristband logout endpoint.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| req | RequestURI | Yes | The HTTP request URI context |
| options | LogoutConfig | Yes | Optional configuration for the logout flow. |
Returns
| Type | Description |
|---|---|
| string | The logout URL to redirect the user to. |
| error | Any error that occurred during logout. |
The LogoutConfig can be configured using NewLogoutConfig and providing any desired LogoutOption configurations.
| Option | Description |
|---|---|
WithRedirectURL(url string) |
Sets the URL to redirect to after logout. |
WithTenantDomain(domain string) |
Sets the tenant domain for logout. |
WithTenantCustomDomain(domain string) |
Sets the tenant custom domain for logout. |
Example
http.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
session, _ := sessionManager.GetSession(r.Context(), r)
if session == nil {
http.Redirect(w, r, "/", http.StatusFound)
return
}
httpCtx := wristbandAuth.NewStandardHttpContext(w, r)
logoutURL, err := wristbandAuth.LogoutURL(httpCtx, NewLogoutConfig())
if err != nil {
http.Error(w, "Logout failed", http.StatusInternalServerError)
return
}
// Revoke token as necessary
if session.RefreshToken != "" {
_ = wristbandAuth.RevokeToken(session.RefreshToken, goauth.RefreshTokenType)
}
sessionManager.ClearSession(r.Context(), w, r)
http.Redirect(w, r, logoutURL, http.StatusFound)
})Revokes the refresh token.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| token | string | Yes | The token value |
| tokenType | string | Yes | The type of token |
Returns
| Type | Description |
|---|---|
| error | Any error that occurred during revoke request. |
Checks if the access token is expired (or about to expire based on TokenExpirationBuffer) and refreshes it if necessary.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| refreshToken | string | Yes | The refresh token to use for obtaining a new access token. |
| expiresAt | int64 | Yes | Absolute expiration time of the current access token in milliseconds since the Unix epoch. |
Returns
| Type | Description |
|---|---|
| *TokenResponse | Contains new tokens if refreshed, nil if not needed. |
| error | Any error that occurred during token refresh. |
TokenResponse Fields
| Field | Type | Description |
|---|---|---|
| AccessToken | string | The new access token. |
| TokenType | string | The token type (e.g., "Bearer"). |
| RefreshToken | string | The new refresh token (if rotated). |
| IDToken | string | The ID token containing user identity claims. |
| ExpiresIn | int | Token lifetime in seconds. |
Example
func refreshMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
session, _ := sessionManager.GetSession(r.Context(), r)
if session == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
tokenResponse, err := wristbandAuth.RefreshTokenIfExpired(
session.RefreshToken,
session.ExpiresAt,
)
if err != nil {
http.Error(w, "Token refresh failed", http.StatusUnauthorized)
return
}
// Update session if tokens were refreshed
if tokenResponse != nil {
session.AccessToken = tokenResponse.AccessToken
session.RefreshToken = tokenResponse.RefreshToken
session.ExpiresAt = tokenResponse.ExpiresAt
sessionManager.StoreSession(r.Context(), w, r, session)
}
next.ServeHTTP(w, r)
})
}This SDK integrates with other Wristband SDKs to provide a complete authentication solution:
For handling client-side authentication and session management in your React frontend, check out the Wristband React Client Auth SDK. It integrates seamlessly with this backend SDK by consuming the Session and Token endpoints you create. Refer to that GitHub repository for more information on frontend authentication patterns.
You can check out the Wristband Go demo app to see this SDK in action. Refer to that GitHub repository for more information.
Reach out to the Wristband team at support@wristband.dev for any questions regarding this SDK.
Made with ❤️ by the Wristband team