## Protect your backend APIs with Go SDK The Frontegg Go SDK lets you validate Frontegg JWTs, protect HTTP routes, check feature entitlements, and call the Frontegg API with idiomatic Go ergonomics. Check out the repo on GitHub br Prerequisites Go ≥ 1.24 and a Frontegg workspace with a **Client ID** and **API Key** (Frontegg Portal → **Settings → API Tokens**). ### Install the SDK ``` go get github.com/frontegg/go-sdk ``` ### Initialize Initialize the SDK once at application startup with your workspace credentials. The package-level `Init` sets up a default client used by the HTTP middleware. Regions The SDK defaults to Frontegg's EU region. If you're running in another region, set `FRONTEGG_API_GATEWAY_URL` to your region's URL instead of `api.frontegg.com`. br ```go import "github.com/frontegg/go-sdk" func main() { frontegg.Init(frontegg.Credentials{ ClientID: "", APIKey: "", }) // ... } ``` If you prefer an explicit, dependency-injected client instead of the package-level default, construct one and build sub-clients from it: ```go client := frontegg.New(frontegg.Credentials{ ClientID: "", APIKey: "", }) identity := client.Identity() entitlements := client.Entitlements() ``` Environment variables Credentials can also be provided through the `FRONTEGG_CLIENT_ID` and `FRONTEGG_API_KEY` environment variables. Values passed in code take precedence. ### Protect backend routes Use the `WithAuthentication` middleware to protect your routes. It reads the token from the `Authorization: Bearer ` header or the `x-api-key` header, validates it, optionally enforces roles and permissions, and stores the decoded user on the request context. ```go import ( "net/http" "github.com/frontegg/go-sdk" "github.com/frontegg/go-sdk/middleware" ) func main() { frontegg.Init(frontegg.Credentials{ ClientID: "", APIKey: "", }) // This route is accessible only to authenticated users with the "admin" role. protected := frontegg.WithAuthentication(middleware.Options{ Roles: []string{"admin"}, Permissions: []string{"fe.secure.read"}, }) mux := http.NewServeMux() mux.Handle("/protected", protected(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, _ := middleware.UserFromContext(r.Context()) // user.ID(), user.Email, user.TenantID, user.Roles, user.Permissions _, _ = w.Write([]byte("hello " + user.Email)) }))) _ = http.ListenAndServe(":8080", mux) } ``` **Behavior** - No credentials → `401 Unauthenticated`. - Invalid token, or missing required role/permission → the error's status code (`401` for authentication failures, `403` for authorization failures). - On success, the decoded entity is available via `middleware.UserFromContext(ctx)`. `Roles` and `Permissions` are satisfied when the token contains **at least one** of the listed values. The middleware works with the standard library and any router built on `net/http` (chi, gorilla, `http.ServeMux`, and others). #### Using an explicit client If you don't want to use the package-level default, pass an identity client directly: ```go guard := middleware.WithAuthentication(client.Identity(), middleware.Options{ Roles: []string{"admin"}, }) ``` ### Use access tokens When you use M2M authentication, access tokens are cached automatically. The in-memory cache is the default. To share a cache across multiple instances, use the Redis backend (only applications that import it pull in the `go-redis` dependency): ```go import ( "github.com/redis/go-redis/v9" "github.com/frontegg/go-sdk/cache/redisstore" ) rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"}) store := redisstore.New[MyType](rdb) // implements cache.Cache[MyType] ``` ### Frontegg clients Frontegg provides various clients for seamless integration with the Frontegg APIs. The entitlements client can be used to determine whether an authorized user or tenant is entitled to a particular feature or permission. This client is discussed in detail in [Entitlements quickstart](/ciam/sdks/backend/go/entitlements). Frontegg's Managed Audit Logs feature allows collecting custom audit logs that are specific for your application and displaying these in Frontegg's [self-service component](/ciam/guides/admin-portal/intro). #### Create a new audits client ```go import "github.com/frontegg/go-sdk/audits" ac := client.Audits() if err := ac.Init(ctx, "", ""); err != nil { log.Fatal(err) } ``` #### Sending audits ```go _ = ac.SendAudit(ctx, audits.SendAuditParams{ TenantID: "my-tenant-id", Severity: audits.SeverityMedium, Fields: map[string]any{ "user": "info@frontegg.com", "resource": "Portal", "action": "Login", "ip": "1.2.3.4", }, }) ``` #### Fetching audits ```go page, err := ac.GetAudits(ctx, audits.GetAuditsParams{ TenantID: "my-tenant-id", Filter: "any-text-filter", SortBy: "createdAt", SortDirection: "desc", Offset: 0, Count: 50, }) // page.Data, page.Total ``` The audits client also exposes `GetAuditsStats`, `GetAuditsMetadata`, and `SetAuditsMetadata`. #### Events ```go import "github.com/frontegg/go-sdk/events" ev := client.Events(auth) // auth: an initialized authenticator eventID, err := ev.Send(ctx, "my-tenant-id", events.EventTrigger{ EventKey: "user.invited", Data: events.EventProperties{ Title: "You're invited", Description: "Join the Acme team", }, }) status, err := ev.GetStatus(ctx, eventID) ``` ### Working with the REST API Frontegg provides a comprehensive REST API for your application. In order to use the API from your backend it is required to initialize the client and the authenticator which maintains the backend to backend session. ```go import "github.com/frontegg/go-sdk/httpclient" auth := client.NewAuthenticator() if err := auth.Init(ctx, "", ""); err != nil { log.Fatal(err) } api := client.HTTPClient(auth, httpclient.WithBaseURL("https://api.frontegg.com")) resp, err := api.Post(ctx, "identity/resources/auth/v1/user", map[string]string{ "email": "johndoe@acme.com", "password": "my-super-duper-password", }, // Optional per-request headers. "frontegg-vendor-host" rewrites the host. map[string]string{"frontegg-vendor-host": "acme.frontegg"}, ) if err != nil { log.Fatal(err) } var user map[string]any _ = resp.JSON(&user) ``` `Get`, `Post`, `Put`, `Patch`, and `Delete` are available; each returns a `*Response` with `StatusCode`, `Header`, and a `JSON(&v)` helper. ### Hosted login (OAuth 2.1 + PKCE) The hosted-login flow uses PKCE, as required by OAuth 2.1. `RequestAuthorize` returns a `code_verifier` that you must persist (for example, in the user's session keyed by `state`) and pass back to `CodeExchange`. ```go import "github.com/frontegg/go-sdk/hostedlogin" hl := client.HostedLogin("https://your-app.com/oauth/callback") // 1. Build the authorization URL and keep the verifier. authReq, err := hl.RequestAuthorize(ctx, "csrf-state-token") // Redirect the user to authReq.URL. // Store authReq.CodeVerifier, keyed by authReq.State. // 2. On the callback (with ?code= and ?state= from the query string): res, err := hl.CodeExchange(ctx, code, state, savedCodeVerifier) // res.User → the decoded user profile // res.AccessToken → access token // res.RefreshToken → refresh token ``` Redirect URI The `redirect_uri` you pass to `HostedLogin` must exactly match an allowed callback configured on your Frontegg application. ### Validating JWT Frontegg provides an identity client that can be utilized for validating Frontegg JWTs outside the middleware flow. ```go import "github.com/frontegg/go-sdk/identity" ident := client.Identity() user, err := ident.ValidateToken(ctx, bearerToken, &identity.ValidateTokenOptions{ Roles: []string{"admin"}, Permissions: []string{"fe.secure.read"}, WithRolesAndPermissions: true, // hydrate roles & permissions on the entity }, identity.JWTHeader) if err != nil { // handle authentication / authorization failure } ``` `ValidateToken` takes the header type as its last argument: - `identity.JWTHeader` — a Frontegg JWT (from the `Authorization` header). - `identity.AccessTokenHeader` — an API key (from the `x-api-key` header). Permissions in JWTs Some JWTs do not include permissions in their payload. Permissions are required for entitlement checks, so set `WithRolesAndPermissions: true` when you need them. #### Step-up authentication Verify that a user has completed step-up (MFA) authentication, optionally within a maximum session age: ```go // Require step-up. user, err := ident.ValidateToken(ctx, token, &identity.ValidateTokenOptions{ StepUp: &identity.StepUpOptions{}, }, identity.JWTHeader) // Require step-up performed within the last hour. user, err = ident.ValidateToken(ctx, token, &identity.ValidateTokenOptions{ StepUp: &identity.StepUpOptions{MaxAge: 3600}, }, identity.JWTHeader) ``` ### Configuration Service URLs default to the public Frontegg gateway and can be overridden via environment variables — useful for EU/regional or self-hosted deployments. | Variable | Default | Description | | --- | --- | --- | | `FRONTEGG_CLIENT_ID` | — | Vendor client ID (used when not passed in code) | | `FRONTEGG_API_KEY` | — | Vendor API key (used when not passed in code) | | `FRONTEGG_API_GATEWAY_URL` | `https://api.frontegg.com` | Base URL for all services | | `FRONTEGG_AUTHENTICATOR_NUMBER_OF_TRIES` | `3` | Number of authentication retry attempts | | `FRONTEGG_IDENTITY_SERVICE_URL` | `/identity` | Identity service URL | | `FRONTEGG_ENTITLEMENTS_SERVICE_URL` | `/entitlements` | Entitlements service URL | | `FRONTEGG_AUDITS_SERVICE_URL` | `/audits/` | Audits service URL | | `FRONTEGG_EVENT_SERVICE_URL` | `/event` | Events service URL | | `FRONTEGG_OAUTH_SERVICE_URL` | `/oauth` | OAuth service URL | | `FRONTEGG_VENDORS_SERVICE_URL` | `/vendors` | Vendors service URL | | `FRONTEGG_METADATA_SERVICE_URL` | `/metadata/` | Metadata service URL | ### Error handling Errors are returned as values and are inspectable with `errors.Is` and `errors.As`. Identity errors also carry an HTTP status code. ```go import "errors" _, err := ident.ValidateToken(ctx, token, opts, identity.JWTHeader) switch { case errors.Is(err, identity.ErrInsufficientRole): // 403 case errors.Is(err, identity.ErrInsufficientPermission): // 403 case errors.Is(err, identity.ErrFailedToAuthenticate): // 401 } // Inspect the status code directly: var sce *identity.StatusCodeError if errors.As(err, &sce) { http.Error(w, sce.Message, sce.StatusCode) } ``` ### Conventions - **Context:** every method that performs I/O takes a `context.Context` as its first argument. - **Concurrency:** all clients are safe for concurrent use. - **Errors:** returned as values; no panics for expected failures. - **Token refresh:** the authenticator refreshes the vendor token automatically before it expires. ### Related documentation - [Go Entitlements](/ciam/sdks/backend/go/entitlements) — feature and permission checks - [Source & issues](https://github.com/frontegg/go-sdk) — Go SDK on GitHub