Set up Kinde Management API access
SDKs and APIs
The Kinde Go SDK allows developers to connect their Go applications to Kinde. It provides OAuth 2.0 flow implementations, JWT parsing and validation, helpers for working with permissions, roles, feature flags and entitlements, and a client for the Kinde Management API.
You can also view the Go SDK source on GitHub.
Add the SDK to your project using the following terminal command:
go get github.com/kinde-oss/kinde-goThen import the packages you need:
import ( "github.com/kinde-oss/kinde-go/oauth2/authorization_code" // browser / device auth flows "github.com/kinde-oss/kinde-go/oauth2/client_credentials" // machine-to-machine flow "github.com/kinde-oss/kinde-go/jwt" // JWT parsing and validation "github.com/kinde-oss/kinde-go/kinde" // Management API client)The SDK implements three OAuth 2.0 flows. Choose the package that matches your application type:
| Flow | Use case |
|---|---|
| Authorization Code | Server-rendered web apps with user interaction. |
| Device Authorization | CLIs, TVs, and other devices with limited input. |
| Client Credentials | Machine-to-machine and Management API access (no user). |
http://localhost:8080/callbackhttp://localhost:8080Import the packages for the authorization code flow:
import ( "github.com/kinde-oss/kinde-go/oauth2/authorization_code" "github.com/kinde-oss/kinde-go/jwt")Add your Kinde credentials to a .env file (or your environment config):
KINDE_DOMAIN=https://your-tenant.kinde.comKINDE_CLIENT_ID=your_client_id_hereKINDE_CLIENT_SECRET=your_client_secret_hereKINDE_REDIRECT_URI=http://localhost:8080/callbackLoad them at startup with a package like godotenv, or read them directly with os.Getenv. Replace the placeholder values with the credentials you copied from the Details page in step 1.
The flow requires a WithSessionHooks(...) implementation to store and retrieve tokens between requests. You need to satisfy the ISessionHooks interface, which has these eight methods:
GetRawToken() (*oauth2.Token, error)SetRawToken(token *oauth2.Token) errorGetState() (string, error)SetState(state string) errorGetCodeVerifier() (string, error)SetCodeVerifier(codeVerifier string) errorGetPostAuthRedirect() (string, error)SetPostAuthRedirect(redirect string) errorFor a reference implementation using Gin sessions, see the SessionStorage example in the SDK repo. For production web apps, back your implementation with cookies, a database, or Redis — see Session management for guidance. Once you have a session hooks implementation do the following:
Initialize the authorization code flow:
kindeAuthFlow, err := authorization_code.NewAuthorizationCodeFlow( os.Getenv("KINDE_DOMAIN"), os.Getenv("KINDE_CLIENT_ID"), os.Getenv("KINDE_CLIENT_SECRET"), os.Getenv("KINDE_REDIRECT_URI"), authorization_code.WithSessionHooks(sessionHooks), // your ISessionHooks implementation authorization_code.WithOffline(), // adds offline scope and manages refresh tokens authorization_code.WithAudience("<your API audience>"), authorization_code.WithTokenValidation( true, // validate signature via JWKS jwt.WillValidateAlgorithm(), // validate alg is RS256 jwt.WillValidateAudience("<your API audience>"), ),)WithAudience and WillValidateAudience are only needed if you’re protecting your own API — omit them otherwise.
Register a /login route that redirects the user to Kinde’s hosted login page:
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, kindeAuthFlow.GetAuthURL(), http.StatusFound)})GetAuthURL() generates the full authorization URL including a CSRF-safe state parameter.
Register a /callback route that handles the OAuth 2.0 callback:
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { kindeAuthFlow.AuthorizationCodeReceivedHandler(w, r)})Or call ExchangeCode yourself:
err := kindeAuthFlow.ExchangeCode(ctx, code, receivedState)After the user has authenticated, parse the stored token through your session hooks to access profile fields:
rawToken, err := sessionHooks.GetRawToken()if err != nil { http.Error(w, "not logged in", http.StatusUnauthorized) return}
token, err := jwt.ParseFromSessionStorage( rawToken, jwt.WillValidateWithJWKSUrl(os.Getenv("KINDE_DOMAIN")+"/.well-known/jwks.json"),)if err != nil || !token.IsValid() { http.Error(w, "invalid token", http.StatusUnauthorized) return}
profile := token.GetUserProfile()
// Or access individual claims directlyclaims := token.GetClaims()email := claims["email"]givenName := claims["given_name"]lastName := claims["family_name"]fullName := claims["name"]picture := claims["picture"]userID := token.GetSubject()GetUserProfile() pulls from the ID token, so given_name, family_name, email, and picture are available. To guarantee the freshest data from Kinde, call the userinfo endpoint instead using the HTTP client:
client, err := kindeAuthFlow.GetClient(r.Context())if err != nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return}resp, err := client.Get(os.Getenv("KINDE_DOMAIN") + "/oauth2/v2/user_profile")if err != nil { http.Error(w, "request failed", http.StatusInternalServerError) return}defer resp.Body.Close()Create a new middleware function that uses IsAuthenticated to guard any route. If the user has no valid session, redirect them to login:
http.HandleFunc("/dashboard", func(w http.ResponseWriter, r *http.Request) { ok, err := kindeAuthFlow.IsAuthenticated(r.Context()) if err != nil || !ok { http.Redirect(w, r, kindeAuthFlow.GetAuthURL(), http.StatusFound) return }
// user is authenticated — serve the page w.Write([]byte("Welcome to your dashboard"))})To reuse this across multiple routes, wrap it in middleware:
func requireAuth(flow authorization_code.IAuthorizationCodeFlow, next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ok, err := flow.IsAuthenticated(r.Context()) if err != nil || !ok { http.Redirect(w, r, flow.GetAuthURL(), http.StatusFound) return } next(w, r) }}
// Usagehttp.HandleFunc("/dashboard", requireAuth(kindeAuthFlow, dashboardHandler))http.HandleFunc("/settings", requireAuth(kindeAuthFlow, settingsHandler))Register a /logout route:
http.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) { if err := kindeAuthFlow.Logout(); err != nil { http.Error(w, "logout failed", http.StatusInternalServerError) return } http.Redirect(w, r, os.Getenv("KINDE_LOGOUT_REDIRECT_URI"), http.StatusFound)})Logout() clears the locally stored tokens.KINDE_LOGOUT_REDIRECT_URI completes sign-out on Kinde’s side.Add this variable to your .env:
KINDE_LOGOUT_REDIRECT_URI=http://localhost:8080Start your server and go to http://localhost:8080/login. You should be redirected to the Kinde-hosted login page.
Sign up with a test user account. After authenticating, Kinde redirects back to your callback URL, exchanges the code for tokens, and stores them in your session.
You should land back on your app. Go to your Kinde dashboard > Users to confirm the test user was created.
By default, the user will be redirected to the login screen. To send users directly to the registration screen instead of the login screen do the following:
Create a new flow instance with WithPrompt("create"):
kindeRegisterFlow, err := authorization_code.NewAuthorizationCodeFlow( os.Getenv("KINDE_DOMAIN"), os.Getenv("KINDE_CLIENT_ID"), os.Getenv("KINDE_CLIENT_SECRET"), os.Getenv("KINDE_REDIRECT_URI"), authorization_code.WithSessionHooks(sessionHooks), authorization_code.WithOffline(), authorization_code.WithPrompt("create"),)if err != nil { log.Fatalf("failed to init register flow: %v", err)}Create a /register route:
http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, kindeRegisterFlow.GetAuthURL(), http.StatusFound)})Send the user to the /register route.
For public clients, pass an empty string as the client secret and add WithPKCE():
authorization_code.WithPKCE() // SHA256 (S256) challenge methodauthorization_code.WithPKCEChallengeMethod("plain") // custom challenge methodUse the device authorization flow for CLIs, TVs, and other devices with limited input.
Import the packages for the device authorization flow:
import ( "github.com/kinde-oss/kinde-go/oauth2/authorization_code" "github.com/kinde-oss/kinde-go/jwt" "github.com/kinde-oss/kinde-go/frameworks/cli")Add your Kinde credentials to a .env file (or your environment config):
KINDE_DOMAIN=https://YOUR_BUSINESS_DOMAIN.kinde.comKINDE_CLIENT_ID=<YOUR_CLIENT_ID>Load them at startup with a package like godotenv, or read them directly with os.Getenv. Replace the placeholder values with the credentials you copied from the Details page in step 1.
For CLI and device apps, create a session that stores tokens in the OS secure keychain (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux):
cliSession, err := cli.NewCliSession("your-app-name")if err != nil { log.Fatalf("failed to init session: %v", err)}Initialize the device authorization flow:
deviceFlow, err := authorization_code.NewDeviceAuthorizationFlow( os.Getenv("KINDE_DOMAIN"), authorization_code.WithClientID(os.Getenv("KINDE_CLIENT_ID")), authorization_code.WithSessionHooks(cliSession), authorization_code.WithOffline(), authorization_code.WithTokenValidation( true, jwt.WillValidateAlgorithm(), ),)if err != nil { log.Fatalf("failed to init device flow: %v", err)}Call StartDeviceAuth to start the flow and get the code to display to the user:
da, err := deviceFlow.StartDeviceAuth(context.Background())if err != nil { log.Fatalf("failed to start device auth: %v", err)}
fmt.Printf("Go to %s and enter code: %s\n", da.VerificationURI, da.UserCode)Instruct the user to go to the VerificationURI and enter the UserCode.
The response also includes VerificationURIComplete — a single URL that pre-fills the user code, which you can use to generate a QR code for the user to scan on your device.
Wait for the user to complete the authentication process on their other device.
Call ExchangeDeviceAccessToken while the user authenticates on their other device. The SDK handles polling at the correct interval automatically and blocks until the user approves or the device code expires:
err = deviceFlow.ExchangeDeviceAccessToken(context.Background(), da)if err != nil { log.Fatalf("token exchange failed: %v", err)}Retrieve the token and access user information:
token, err := deviceFlow.GetToken(context.Background())if err != nil { log.Fatalf("failed to get token: %v", err)}
profile := token.GetUserProfile()userID := token.GetSubject()Or use an HTTP client to call a protected API — the access token is attached automatically:
client, err := deviceFlow.GetClient(context.Background())if err != nil { log.Fatalf("failed to get client: %v", err)}
resp, err := client.Get("https://your-api.com/protected-resource")if err != nil { log.Fatalf("request failed: %v", err)}defer resp.Body.Close()Display the user information and grant users protected resources.
Import the packages for the client credentials flow:
import ( "github.com/kinde-oss/kinde-go/oauth2/client_credentials" "github.com/kinde-oss/kinde-go/jwt" "github.com/kinde-oss/kinde-go/kinde" // for Management API)Add your Kinde credentials to a .env file (or your environment config):
KINDE_DOMAIN=https://your-tenant.kinde.comKINDE_CLIENT_ID=your_client_id_hereKINDE_CLIENT_SECRET=your_client_secret_hereLoad them at startup with a package like godotenv, or read them directly with os.Getenv. Replace the placeholder values with the credentials you copied from the Details page in step 1.
Initialize the client credentials flow:
kindeClient, err := client_credentials.NewClientCredentialsFlow( os.Getenv("KINDE_DOMAIN"), os.Getenv("KINDE_CLIENT_ID"), os.Getenv("KINDE_CLIENT_SECRET"), client_credentials.WithKindeManagementAPI(os.Getenv("KINDE_DOMAIN")), // adds the Management API audience client_credentials.WithTokenValidation( true, jwt.WillValidateAlgorithm(), ),)if err != nil { log.Fatalf("failed to init M2M client: %v", err)}WithKindeManagementAPI sets the audience required for Management API access. If you’re calling your own API instead, replace it with client_credentials.WithAudience("<your API audience>").
Use GetClient to retrieve an *http.Client pre-configured with a valid access token. The client fetches a new token automatically when it expires:
client, err := kindeClient.GetClient(context.Background())if err != nil { log.Fatalf("failed to get client: %v", err)}
resp, err := client.Get(os.Getenv("KINDE_DOMAIN") + "/api/v1/users")if err != nil { log.Fatalf("request failed: %v", err)}defer resp.Body.Close()To inspect the raw token directly — for example, to read claims or check expiry — use GetToken:
token, err := kindeClient.GetToken(context.Background())if err != nil { log.Fatalf("failed to get token: %v", err)}
accessToken, ok := token.GetAccessToken()The Management API lets you programmatically manage your Kinde tenant — users, organizations, applications, permissions, and more.
Create the client from the initialized kindeClient:
ctx := context.Background()
managementApi, err := kinde.NewManagementAPI(ctx, os.Getenv("KINDE_DOMAIN"), kindeClient)if err != nil { log.Fatalf("failed to init Management API: %v", err)}Example: list users
res, err := managementApi.GetUsers(ctx, nil)if err != nil { log.Fatalf("failed to get users: %v", err)}Example: create a user
res, err := managementApi.CreateUser(ctx, &management_api.CreateUserReq{ Profile: &management_api.CreateUserProfile{ GivenName: "Jane", FamilyName: "Smith", }, Identities: []management_api.CreateUserIdentity{ { Type: management_api.CreateUserIdentityTypeEmail, Details: management_api.CreateUserIdentityDetails{Email: "jane@example.com"}, }, },})if err != nil { log.Fatalf("failed to create user: %v", err)}Example: create an application
res, err := managementApi.CreateApplication(ctx, &management_api.CreateApplicationReq{ Name: "Backend app", Type: management_api.CreateApplicationReqTypeReg,})if err != nil { // handle error}Handle responses:
Management API methods return an interface that may be one of several response types. Switch on the response to handle each case:
switch response := res.(type) {case management_api.GetUsersBadRequest: fmt.Printf("Bad request: %v\n", response)case management_api.GetUsersForbidden: fmt.Printf("Forbidden (missing scope): %v\n", response)case management_api.GetUsersTooManyRequests: fmt.Printf("Rate limited: %v\n", response)case management_api.GetUsersResponse: fmt.Printf("Users: %v\n", response)default: fmt.Printf("Unexpected response type: %T\n", response)}Learn more about the Kinde Management API here.
Both the authorization code and client credentials flows use session hooks to store and retrieve tokens. Provide an implementation of the SDK’s session hooks interface with WithSessionHooks(...). If none is provided, the client credentials flow defaults to an in-memory session (tokens are lost when the process terminates) - useful for testing and short-lived processes.
For command-line tools and desktop apps, the SDK provides a pre-built session that stores tokens in the operating system’s secure keychain (Keychain on macOS, Credential Manager on Windows, Secret Service on Linux). It handles token chunking for large tokens automatically.
import "github.com/kinde-oss/kinde-go/frameworks/cli"
cliSession, err := cli.NewCliSession("your-app-name")if err != nil { // keychain / secret service not available log.Fatalf("failed to init CLI session: %v", err)}
kindeClient, err := client_credentials.NewClientCredentialsFlow( "https://your-tenant.kinde.com", "<client_id>", "<client_secret>", client_credentials.WithKindeManagementAPI("https://your-tenant.kinde.com"), client_credentials.WithSessionHooks(cliSession),)For production web apps, implement your own session storage backed by a database, Redis, encrypted files, or your existing session system.
The gin_kinde package provides a complete middleware that protects route groups, handles the callback automatically, and redirects unauthenticated users to sign in.
import "github.com/kinde-oss/kinde-go/frameworks/gin_kinde"
func main() { router := gin.Default()
// Set up session middleware (e.g. gin-contrib/sessions) privateGroup := router.Group("/")
gin_kinde.UseKindeAuth(privateGroup, "https://your-tenant.kinde.com", // Kinde domain "your-client-id", "your-client-secret", "http://localhost:8080", // base redirect URL authorization_code.WithPrompt("login"), authorization_code.WithOffline(), )
privateGroup.GET("/profile", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Authenticated!"}) })
router.Run(":8080")}UseKindeAuth accepts the same options as NewAuthorizationCodeFlow, so you can add WithAudience(...), WithPKCE(), WithTokenValidation(...), and others.
For the standard library or other frameworks, check authentication and redirect as needed:
func AuthMiddleware(flow authorization_code.IAuthorizationCodeFlow) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ok, err := flow.IsAuthenticated(r.Context()) if err != nil || !ok { http.Redirect(w, r, flow.GetAuthURL(), http.StatusFound) return } next.ServeHTTP(w, r) }) }}The jwt package parses and validates JWTs and provides convenient access to claims. Validation options are applied once during parsing and the results are cached on the token - subsequent reads (GetSubject(), GetAudience(), etc.) do not re-validate. When used inside the OAuth 2.0 flows, tokens are re-validated each time they are retrieved from the token source.
token, err := jwt.ParseFromString( "your.jwt.token.here", jwt.WillValidateWithJWKSUrl("https://your-tenant.kinde.com/.well-known/jwks.json"), jwt.WillValidateAlgorithm("RS256"), jwt.WillValidateAudience("your-api-audience"),)if err != nil { // handle error}
if token.IsValid() { subject := token.GetSubject() issuer := token.GetIssuer()}Parsing helpers:
| Method | Source |
|---|---|
ParseFromString | A raw token string. |
ParseFromAuthorizationHeader | An HTTP Authorization: Bearer <token> header. |
ParseFromSessionStorage | A JSON-serialized oauth2.Token from session storage. |
ParseOAuth2Token | An existing *oauth2.Token. |
| Option | Purpose |
|---|---|
WillValidateWithJWKSUrl(url) | Validate the signature against a JWKS endpoint. |
WillValidateWithPublicKey(fn) | Validate the signature with a specific RSA public key. |
WillValidateWithKeyFunc(fn) | Provide a custom key function. |
WillValidateAlgorithm(...algs) | Restrict signing algorithms (defaults to RS256). |
WillValidateAudience(aud) | Require a specific audience. |
WillValidateIssuer(iss) | Require a specific issuer. |
WillValidateClaims(fn) | Run custom claim validation. |
WillValidateWithClockSkew(d) | Allow clock skew between servers. |
WillValidateWithTimeFunc(fn) | Supply a custom time function (useful for testing). |
isValid := token.IsValid()subject := token.GetSubject()issuer := token.GetIssuer()audience := token.GetAudience()orgCode := token.GetOrganizationCode()claims := token.GetClaims()
accessToken, ok := token.GetAccessToken()idToken, ok := token.GetIdToken()refreshToken, ok := token.GetRefreshToken()
profile := token.GetUserProfile()errs := token.GetValidationErrors()func protectedHandler(w http.ResponseWriter, r *http.Request) { token, err := jwt.ParseFromAuthorizationHeader( r, jwt.WillValidateWithJWKSUrl("https://your-tenant.kinde.com/.well-known/jwks.json"), jwt.WillValidateAlgorithm("RS256"), jwt.WillValidateAudience("your-api-audience"), ) if err != nil || !token.IsValid() { http.Error(w, "Unauthorized", http.StatusUnauthorized) return }
// token is valid subject := token.GetSubject() _ = subject}The account_api client backs the API-aware token helpers in the sections below. Construct it with a base URL and a function that returns a current access token - for example, one sourced from a flow’s GetToken:
import "github.com/kinde-oss/kinde-go/kinde/account_api"
accountClient, err := account_api.NewClient( "<YOUR_DOMAIN>", // kinde issuer URL or custom domain func(ctx context.Context) (string, error) { token, err := kindeClient.GetToken(ctx) if err != nil { return "", err } accessToken, _ := token.GetAccessToken() return accessToken, nil },)Read permissions directly from the token (see Define user permissions for how they’re defined):
permissions := token.GetPermissions()The token also exposes API-aware helpers that fall back to the Kinde Account API when a value isn’t present in the token. These take a *account_api.Client:
access, err := token.GetPermission(ctx, accountClient, "create:todos", jwt.GetPermissionOptions{})ok, err := token.HasPermissions(ctx, accountClient, []string{"create:todos", "read:todos"}, jwt.HasPermissionsOptions{})roles := token.GetRoles()hasRoles := token.HasRoles("admin", "editor")
// API-aware variantsroles, err := token.GetRolesWithAPI(ctx, accountClient, false)ok, err := token.HasRolesWithAPI(ctx, accountClient, []string{"admin"}, jwt.HasRolesOptions{})Read feature flags from the token, with type-specific accessors:
flags := token.GetFeatureFlags()
flag, ok := token.GetFeatureFlag("theme")boolVal, ok := token.GetFeatureFlagBool("is_dark_mode")strVal, ok := token.GetFeatureFlagString("theme")intVal, ok := token.GetFeatureFlagInt("competitions_limit")
// API-aware variantsflagValue, err := token.GetFlag(ctx, accountClient, "theme", false)hasFlags, err := token.HasFeatureFlags(ctx, accountClient, []string{"theme"}, jwt.HasFeatureFlagsOptions{})Entitlements describe what a customer is allowed to use based on their billing plan. These helpers read from the Kinde Account API:
result, err := token.GetEntitlements(ctx, accountClient)entitlement, err := token.GetEntitlement(ctx, accountClient, "seats")ok, err := token.HasBillingEntitlements(ctx, accountClient, []string{"pro_plan"}, jwt.HasBillingEntitlementsOptions{})kindeAuthFlow, err := authorization_code.NewAuthorizationCodeFlow( "<YOUR_DOMAIN>", // kinde issuer URL or custom domain "<client_id>", "<client_secret>", "http://localhost:8080/callback", // application callback URL authorization_code.WithSessionHooks(sessionHooks), // your ISessionHooks implementation authorization_code.WithOffline(), // adds offline scope and manages refresh tokens authorization_code.WithAudience("<your API audience>"), authorization_code.WithTokenValidation( true, // validate signature via JWKS jwt.WillValidateAlgorithm(), // validate alg is RS256 jwt.WillValidateAudience("<your API audience>"), ),)Returns the full authorization URL to redirect the user to in order to start authentication. Includes a CSRF-safe state parameter generated automatically.
Params:
GetAuthURL()
Usage:
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, kindeAuthFlow.GetAuthURL(), http.StatusFound)})Output: string
Returns the full authorization URL, with optional invitation code support. If invitationCode is provided, it appends invitation_code and is_invitation=true to the URL, directing the user into a pre-filled invitation flow.
Params:
GetAuthURLWithInvitation(invitationCode string)
invitationCode — invitation code to pre-fill; pass an empty string to behave identically to GetAuthURL()Usage:
http.HandleFunc("/invite", func(w http.ResponseWriter, r *http.Request) { code := r.URL.Query().Get("invitation_code") http.Redirect(w, r, kindeAuthFlow.GetAuthURLWithInvitation(code), http.StatusFound)})Output: string
Exchanges the authorization code returned by Kinde for an access token. Validates the state parameter to prevent CSRF attacks, then stores the token using your session hooks.
Params:
ExchangeCode(ctx context.Context, authorizationCode string, receivedState string)
ctx — request contextauthorizationCode — the code query parameter from the Kinde callback URLreceivedState — the state query parameter from the Kinde callback URL; validated against the stored state to prevent CSRFUsage:
err := kindeAuthFlow.ExchangeCode(r.Context(), r.URL.Query().Get("code"), r.URL.Query().Get("state"))if err != nil { http.Error(w, "token exchange failed", http.StatusInternalServerError) return}Output: error
A ready-made HTTP handler for your callback route. Internally calls ExchangeCode and handles error responses. Use this when you don’t need custom logic in the callback.
Params:
AuthorizationCodeReceivedHandler(w http.ResponseWriter, r *http.Request)
w — HTTP response writerr — incoming HTTP request containing the code and state query parametersUsage:
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { kindeAuthFlow.AuthorizationCodeReceivedHandler(w, r)})Output: none — writes the HTTP response directly.
Checks whether a valid, non-expired token exists in session storage for the current request context.
Params:
IsAuthenticated(ctx context.Context)
ctx — request contextUsage:
ok, err := kindeAuthFlow.IsAuthenticated(r.Context())if err != nil || !ok { http.Redirect(w, r, kindeAuthFlow.GetAuthURL(), http.StatusFound) return}Output: (bool, error)
Returns the current session’s parsed *jwt.Token. Reads from session storage and refreshes the token if it has expired (requires WithOffline()).
Params:
GetToken(ctx context.Context)
ctx — request contextUsage:
token, err := kindeAuthFlow.GetToken(r.Context())if err != nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return}
subject := token.GetSubject()profile := token.GetUserProfile()Output: (*jwt.Token, error)
Returns an *http.Client pre-configured with the user’s access token. If WithOffline() was set and the token has expired, it automatically refreshes before returning the client.
Params:
GetClient(ctx context.Context)
ctx — request contextUsage:
client, err := kindeAuthFlow.GetClient(r.Context())if err != nil { http.Error(w, "unauthorized", http.StatusUnauthorized) return}
resp, err := client.Get("https://api.example.com/data")Output: (*http.Client, error)
Clears the locally stored tokens from session storage. Does not end the session on Kinde’s side — redirect the user to your allowed logout redirect URL to complete sign-out fully.
Params:
Logout()
Usage:
http.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) { if err := kindeAuthFlow.Logout(); err != nil { http.Error(w, "logout failed", http.StatusInternalServerError) return } http.Redirect(w, r, os.Getenv("KINDE_LOGOUT_REDIRECT_URI"), http.StatusFound)})Output: error
HTTP middleware that reads the current token from session storage and injects it into the request context, making it available to downstream handlers. Pair with TokenFromContext to read the token in a handler.
Params:
InjectTokenMiddleware(next http.Handler)
next — the downstream http.Handler to call after injecting the token into the request contextUsage:
mux := http.NewServeMux()mux.HandleFunc("/protected", protectedHandler)
http.ListenAndServe(":8080", kindeAuthFlow.InjectTokenMiddleware(mux))Output: http.Handler
Package-level helper that retrieves the token previously injected by InjectTokenMiddleware from the request context. Returns nil, false if no token is present.
Params:
authorization_code.TokenFromContext(ctx context.Context)
ctx — the request context, typically r.Context()Usage:
func protectedHandler(w http.ResponseWriter, r *http.Request) { token, ok := authorization_code.TokenFromContext(r.Context()) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } userID := token.GetSubject() w.Write([]byte("Hello, " + userID))}Output: (*jwt.Token, bool)
deviceFlow, err := authorization_code.NewDeviceAuthorizationFlow( "<YOUR_DOMAIN>", // kinde issuer URL or custom domain authorization_code.WithClientID("<YOUR_CLIENT_ID>"), // application client ID authorization_code.WithSessionHooks(cliSession), // your session implementation authorization_code.WithOffline(), // adds offline scope and manages refresh tokens authorization_code.WithTokenValidation( true, jwt.WillValidateAlgorithm(), ),)Starts the device authorization flow. Returns a DeviceAuthResponse containing the user_code and verification_uri to display to the user, plus the device_code used to poll for a token.
Params:
StartDeviceAuth(ctx context.Context)
ctx — request contextUsage:
da, err := deviceFlow.StartDeviceAuth(context.Background())if err != nil { log.Fatalf("failed to start device auth: %v", err)}
fmt.Printf("Go to %s and enter code: %s\n", da.VerificationURI, da.UserCode)Output: (*oauth2.DeviceAuthResponse, error)
Polls Kinde until the user completes authorization on their other device, then exchanges the device code for an access token and stores it using your session hooks.
Params:
ExchangeDeviceAccessToken(ctx context.Context, da *oauth2.DeviceAuthResponse, opts ...oauth2.AuthCodeOption)
ctx — request contextda — the *oauth2.DeviceAuthResponse returned by StartDeviceAuthopts — optional additional oauth2.AuthCodeOption values (variadic)Usage:
err := deviceFlow.ExchangeDeviceAccessToken(context.Background(), da)if err != nil { log.Fatalf("token exchange failed: %v", err)}Output: error
Returns the current session’s parsed *jwt.Token. Reads from session storage and refreshes the token if it has expired (requires WithOffline()).
Params:
GetToken(ctx context.Context)
ctx — request contextUsage:
token, err := deviceFlow.GetToken(context.Background())if err != nil { log.Fatalf("failed to get token: %v", err)}
subject := token.GetSubject()Output: (*jwt.Token, error)
Returns an *http.Client pre-configured with the device flow’s access token. Refreshes automatically when the token expires if WithOffline() was set.
Params:
GetClient(ctx context.Context)
ctx — request contextUsage:
client, err := deviceFlow.GetClient(context.Background())if err != nil { log.Fatalf("failed to get client: %v", err)}
resp, err := client.Get("https://api.example.com/data")Output: (*http.Client, error)
Checks whether a valid, non-expired token is present in session storage.
Params:
IsAuthenticated(ctx context.Context)
ctx — request contextUsage:
ok, err := deviceFlow.IsAuthenticated(context.Background())if err != nil || !ok { log.Println("not authenticated")}Output: (bool, error)
Clears the locally stored tokens from session storage.
Params:
Logout()
Usage:
err := deviceFlow.Logout()Output: error
kindeClient, err := client_credentials.NewClientCredentialsFlow( "<YOUR_DOMAIN>", // kinde issuer URL or custom domain "<YOUR_CLIENT_ID>", // application client ID "<YOUR_CLIENT_SECRET>", // application client secret // Use ONE of the following — they are mutually exclusive: client_credentials.WithKindeManagementAPI("<YOUR_DOMAIN>"), // for Kinde Management API access // client_credentials.WithAudience("<YOUR_API_AUDIENCE>"), // for your own API instead client_credentials.WithTokenValidation(true, jwt.WillValidateAlgorithm()),)Returns an *http.Client pre-configured with a valid access token. Reads the token from session storage if present, fetches a new one if missing, and re-fetches automatically as it expires.
Params:
GetClient(ctx context.Context)
ctx — request contextUsage:
client, err := kindeClient.GetClient(context.Background())if err != nil { log.Fatalf("failed to init client: %v", err)}
resp, err := client.Get("https://api.example.com/resource")Output: (*http.Client, error)
Returns the raw *jwt.Token for the current M2M session. Reads from session storage and fetches a new token when expired.
Params:
GetToken(ctx context.Context)
ctx — request contextUsage:
token, err := kindeClient.GetToken(context.Background())if err != nil { log.Fatalf("failed to get token: %v", err)}
accessToken, ok := token.GetAccessToken()Output: (*jwt.Token, error)