Skip to content

Repository files navigation

Frontegg

Frontegg Go SDK

Drop-in authentication, authorization, and entitlements for your Go backend.

Validate Frontegg JWTs, guard your routes, check feature entitlements, and call the Frontegg API β€” in a few lines of idiomatic Go.

Go Reference CI Go Report Card Go 1.24+ License: MIT

Full guide Β· Quickstart Β· Guides Β· Frontegg docs Β· Report a bug


Why this SDK

Frontegg gives SaaS teams production-grade auth, user management, and entitlements out of the box. This SDK brings that to your Go services with first-class Go ergonomics:

  • πŸ” Auth in ~5 lines β€” guard any net/http route with WithAuthentication. Bearer JWTs and API keys, roles, permissions, and step-up MFA all handled.
  • 🎟️ Entitlements at the edge β€” evaluate feature flags and plan rules locally, in-memory, with zero per-check network calls. A faithful port of Frontegg's entitlements engine.
  • ⚑ Built for production β€” goroutine-safe, context.Context-aware, automatic token refresh, pluggable in-memory or Redis caching.
  • 🧩 Idiomatic & unsurprising β€” errors as values (errors.Is/errors.As), functional options, standard-library HTTP. No magic, no globals you didn't ask for.
  • βœ… Battle-tested β€” ~89% test coverage, race-tested, CI on every push.

Idiomatic Go counterpart to the official @frontegg/client Node SDK, with feature parity.

Contents

Install

go get github.com/frontegg/go-sdk

Requires Go 1.24+.

Quickstart

Protect a route in one snippet β€” initialize once, then wrap any handler:

package main

import (
	"net/http"

	"github.com/frontegg/go-sdk"
	"github.com/frontegg/go-sdk/middleware"
)

func main() {
	// Initialize the package-level client once at startup.
	frontegg.Init(frontegg.Credentials{
		ClientID: "<YOUR_CLIENT_ID>",
		APIKey:   "<YOUR_API_KEY>",
	})

	// Guard a route: requires a valid token with the "admin" role.
	protected := frontegg.WithAuthentication(middleware.Options{
		Roles: []string{"admin"},
	})

	mux := http.NewServeMux()
	mux.Handle("/admin", 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)
}

The middleware reads the token from the Authorization: Bearer … header or the x-api-key header, validates the signature and claims, enforces any required roles/permissions, and puts the decoded user on the request context. Unauthorized requests get 401; insufficient role/permission gets 403.

Capabilities

Capability Package What it does
Auth middleware middleware net/http guard for Bearer JWT / API-key auth, roles & permissions
Identity identity Validate JWTs and access tokens, roles/permissions, step-up MFA
Entitlements entitlements Local feature-flag & plan evaluation with background snapshot refresh
Hosted login hostedlogin OAuth 2.1 authorize URL + PKCE code exchange
Audit logs audits Send and query Managed Audit Logs
Events events Trigger Frontegg events and read delivery status
REST client httpclient Authenticated client for the full Frontegg API
M2M auth authenticator Vendor token with automatic refresh
Caching cache, cache/redisstore In-memory (default) or Redis-backed token cache
Entry point frontegg One Client that builds all of the above

Design principles: errors are values (errors.Is/errors.As), every network call takes a context.Context, all clients are safe for concurrent use, and configuration is read from the standard FRONTEGG_* environment variables.

Guides

πŸ“– Prefer a single end-to-end walkthrough? See the full integration guide.

Protect HTTP routes

WithAuthentication works with the standard library and any router built on it (chi, gorilla, http.ServeMux, …):

guard := frontegg.WithAuthentication(middleware.Options{
	Roles:       []string{"admin", "owner"}, // any one is sufficient
	Permissions: []string{"fe.secure.read"}, // any one is sufficient
})

mux.Handle("/reports", guard(reportsHandler))

Need a dedicated client instead of the package-level default? Build one and pass its identity validator:

c := frontegg.New(frontegg.Credentials{ClientID: id, APIKey: key})
guard := middleware.WithAuthentication(c.Identity(), middleware.Options{Roles: []string{"admin"}})

Validate a token manually

c := frontegg.New(frontegg.Credentials{ClientID: id, APIKey: key})
ident := c.Identity()

user, err := ident.ValidateToken(ctx, bearerToken, &identity.ValidateTokenOptions{
	Roles:                   []string{"admin"},
	Permissions:             []string{"fe.secure.read"},
	WithRolesAndPermissions: true,                              // hydrate roles/permissions
	StepUp:                  &identity.StepUpOptions{MaxAge: 3600}, // require step-up MFA
}, identity.JWTHeader)
if err != nil {
	// errors.Is(err, identity.ErrInsufficientRole), etc.
}

Entitlements

Evaluate feature and permission entitlements locally β€” no network round-trip per check. The client keeps an in-memory snapshot fresh in the background.

ent := c.Entitlements()
if err := ent.Start(ctx); err != nil {
	log.Fatal(err)
}
defer ent.Close()

if err := ent.Ready(ctx); err != nil { // wait for the first snapshot
	log.Fatal(err)
}

// Scope to a user/tenant straight from their token …
scoped, err := ent.ForFronteggToken(ctx, token)
// … or from an already-validated entity:
//   scoped := ent.ForUser(entity)

if res := scoped.IsEntitledToFeature(ctx, "advanced-analytics", nil); res.Result {
	// entitled
} else {
	log.Printf("not entitled: %s", res.Justification) // missing-feature | bundle-expired | …
}

// Permissions, or the unified entry point:
_ = scoped.IsEntitledToPermission(ctx, "fe.secure.read", nil)
_, _ = scoped.IsEntitledTo(ctx, "advanced-analytics", "", nil)

Hosted login (OAuth 2.1 + PKCE)

The hosted-login flow uses PKCE, as required by OAuth 2.1. RequestAuthorize returns a code_verifier you must persist (e.g. in the user's session, keyed by state) and pass back to CodeExchange.

hl := c.HostedLogin("https://app.acme.com/oauth/callback")

// 1. Build the redirect and stash the verifier.
authReq, err := hl.RequestAuthorize(ctx, "csrf-state-token")
//    β†’ redirect the user to authReq.URL
//    β†’ save authReq.CodeVerifier, keyed by authReq.State

// 2. On the callback (?code=…&state=…), exchange the code.
res, err := hl.CodeExchange(ctx, code, state, savedCodeVerifier)
//    res.User, res.AccessToken, res.RefreshToken

The redirect_uri must exactly match an allowed callback configured on your Frontegg application.

Call the Frontegg REST API

auth := c.NewAuthenticator()
if err := auth.Init(ctx, id, key); err != nil {
	log.Fatal(err)
}
api := c.HTTPClient(auth, httpclient.WithBaseURL("https://api.frontegg.com"))

resp, err := api.Post(ctx, "identity/resources/auth/v1/user",
	map[string]string{"email": "john@acme.com", "password": "…"},
	map[string]string{"frontegg-vendor-host": "acme.frontegg"}, // optional per-request headers
)
// resp.StatusCode, resp.JSON(&v)

The client injects the vendor x-access-token on every request and refreshes it automatically before expiry.

Audits & events

// Managed Audit Logs
audits := c.Audits()
_ = audits.Init(ctx, "<CLIENT_ID>", "<AUDITS_KEY>")
_ = audits.SendAudit(ctx, audits.SendAuditParams{
	TenantID: "my-tenant",
	Severity: audits.SeverityMedium,
	Fields:   map[string]any{"user": "info@frontegg.com", "action": "Login", "ip": "1.2.3.4"},
})
page, _ := audits.GetAudits(ctx, audits.GetAuditsParams{TenantID: "my-tenant", Offset: 0, Count: 50})

// Events
ev := c.Events(auth)
id, _ := ev.Send(ctx, "my-tenant", events.EventTrigger{
	EventKey: "user.invited",
	Data:     events.EventProperties{Title: "You're invited", Description: "Join the team"},
})
status, _ := ev.GetStatus(ctx, id)

Caching access tokens

The in-memory cache is the default. To share a cache across instances, use the Redis backend (only consumers who import it pull in go-redis):

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) // satisfies cache.Cache[MyType]

Configuration

Credentials are passed explicitly, or fall back to the environment. Service URLs default to the public Frontegg gateway and can be overridden β€” handy for EU/regional or self-hosted deployments.

Variable Default Purpose
FRONTEGG_CLIENT_ID β€” Vendor client ID (fallback when not passed in code)
FRONTEGG_API_KEY β€” Vendor API key (fallback when not passed in code)
FRONTEGG_API_GATEWAY_URL https://api.frontegg.com Base URL for all services
FRONTEGG_AUTHENTICATOR_NUMBER_OF_TRIES 3 Auth retry attempts
FRONTEGG_IDENTITY_SERVICE_URL <base>/identity Override the identity service
FRONTEGG_ENTITLEMENTS_SERVICE_URL <base>/entitlements Override the entitlements service

Per-service overrides also exist for audits, events, metadata, vendors, and OAuth (FRONTEGG_*_SERVICE_URL). See config.

Error handling

Errors are typed and inspectable. Identity failures carry an HTTP status and match sentinels:

_, 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
}

var sce *identity.StatusCodeError
if errors.As(err, &sce) {
	http.Error(w, sce.Message, sce.StatusCode)
}

Testing

go test ./...            # unit tests (no network β€” fully stubbed with httptest)
go test -race ./...      # race detector
go test -tags e2e ./...  # end-to-end against a real tenant (see .env.e2e.example)

Contributing

Issues and pull requests are welcome. Before opening a PR:

gofmt -l .      # must be clean
go vet ./...    # must pass
go test ./...   # must pass

License

MIT Β© Frontegg

About

Official Go SDK for Frontegg. Secure your APIs, validate JWTs, manage machine-to-machine authentication, access Frontegg APIs, and integrate enterprise-ready identity, authorization, entitlements, and audit capabilities into Go applications.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages