A minimal Go SDK for integrating backend and SSR applications with an Authara authentication server.
This SDK is intentionally small and infrastructure-focused. Its primary responsibility is to verify Authara-issued access tokens and expose authentication facts to your application in a safe, explicit way.
It does not perform authentication itself and does not own session or security policy.
This SDK is designed to:
- expose facts, not policy
- avoid hidden behavior
- keep authentication and authorization concerns separate
- require explicit configuration for any network behavior
Authara itself remains the single source of truth for authentication, sessions, refresh logic, CSRF enforcement, and security invariants.
- Verifies Authara-issued access tokens (JWT)
- Validates token properties:
- issuer (
iss) - audience (
aud) - expiry (
exp) - signature and key ID (
kid)
- issuer (
- Injects authentication facts into
context.Context - Provides HTTP middleware for common auth patterns
- Exposes helpers for reading authentication facts from context
- Optionally checks Authara's shared Redis revocation entries
- Provides generated, side-effect-free HTTP helpers for calling Authara endpoints from backend or SSR applications
- Forwards existing authentication context (access cookie) only
- Exposes API calls generated from the Authara OpenAPI contract
These helpers are strict by design:
- no token refresh
- no retries
- no cookie mutation
- no redirect behavior
- Does not authenticate users
- Does not manage sessions
- Does not enforce authorization policy
- Does not perform background or implicit network calls
Exceptions:
RequireAuthWithRefreshcan perform a best-effort refresh call, and middleware can query Redis for access-token revocations. Both behaviors must be explicitly enabled through configuration.
All authentication, session management, refresh logic, and CSRF enforcement live exclusively in Authara itself, not in this SDK.
go get github.com/authara-org/authara-gosdk, err := authara.New(authara.Config{
Issuer: "https://example.com/auth",
Audience: "app",
Keys: map[string][]byte{
"key-id": []byte("secret"),
},
})
if err != nil {
// handle configuration error
}All fields are required:
Issuermust exactly match the issuer configured in AutharaAudiencemust match the intended token audienceKeysmaps JWTkidvalues to their signing secrets
Multiple keys may be provided to support key rotation.
For applications using environment variables, the SDK provides a helper:
cfg, err := authara.ConfigFromEnv()
if err != nil {
log.Fatal(err)
}
sdk, err := authara.New(cfg)
if err != nil {
log.Fatal(err)
}Expected environment variables:
AUTHARA_AUDIENCE(default:app)AUTHARA_ISSUER(default:authara)AUTHARA_JWT_KEYS(required)AUTHARA_BASE_URL(optional, enables refresh)AUTHARA_INTERNAL_API_TOKEN(optional, enables internal API helpers)AUTHARA_ACCESS_TOKEN_REVOCATION_ENABLED(default:false)AUTHARA_REDIS_HOST(default:localhost, used when revocation checks are enabled)AUTHARA_REDIS_PORT(default:6379, used when revocation checks are enabled)AUTHARA_REDIS_PASSWORD(optional, used when revocation checks are enabled)AUTHARA_REDIS_DB(default:0, used when revocation checks are enabled)
This helper is intentionally minimal and does not introduce implicit behavior.
The SDK can optionally perform a single best-effort refresh when an access token is missing or invalid.
To enable refresh, configure the Authara base URL:
sdk, err := authara.New(authara.Config{
Issuer: "https://example.com/auth",
Audience: "app",
Keys: map[string][]byte{
"key-id": []byte("secret"),
},
AutharaBaseURL: "http://authara:8080",
})Notes:
- Refresh is performed by calling Authara’s refresh endpoint and forwarding the incoming request cookies.
- Authara remains the only component that knows refresh semantics. The SDK only triggers refresh and re-verifies the new access token.
- If
AutharaBaseURLis not set, refresh behavior is disabled.
By default, middleware verifies access-token JWTs locally. To also reject tokens that Authara Core has revoked, connect the SDK to the same Redis database as Core:
AUTHARA_ACCESS_TOKEN_REVOCATION_ENABLED=true
AUTHARA_REDIS_HOST=redis
AUTHARA_REDIS_PORT=6379
AUTHARA_REDIS_PASSWORD=
AUTHARA_REDIS_DB=0Every middleware token verification checks the exact token, session, user, and
organization-membership revocation entries. Redis connection and lookup
failures fail closed: New fails if Redis is unavailable at startup, and
protected requests are treated as unauthenticated if a runtime lookup fails.
TryAuth continues without attaching an identity in that case.
Call sdk.Close() during shutdown.
The Redis formats are synchronized from Authara Core's versioned
contract/access-token-revocations.json contract.
r.Use(sdk.RequireAuth)- Browser → redirect
- HTMX →
HX-Redirect - API →
401
r.Use(sdk.RequireAuthWithRefresh)- Attempts one refresh if token missing/invalid
- Forwards
Set-Cookie - Continues request if refresh succeeds
r.Use(sdk.TryAuth)Never blocks — attaches auth if available.
userID, ok := authara.UserIDFromContext(r.Context())
roles, _ := authara.RolesFromContext(r.Context())
organizationID, _ := authara.OrganizationIDFromContext(r.Context())
organizationRole, _ := authara.OrganizationRoleFromContext(r.Context())client := authara.NewClient("https://auth.example.com")user, err := client.CallGetCurrentUser(ctx, r)client := authara.NewClient(
"https://auth.example.com",
authara.WithInternalAPIToken(os.Getenv("AUTHARA_INTERNAL_API_TOKEN")),
)
role := authara.APIOrganizationInvitationRoleAdmin
metadata := map[string]any{"baufunk": map[string]any{"role": "manager"}}
invite, err := client.CallCreateInternalOrganizationInvitation(
ctx,
organizationID,
authara.APIInternalCreateInvitationRequest{
ActorUserID: actorUserID,
Email: "teammate@example.com",
Role: &role,
Metadata: &metadata,
},
)
// After your backend has checked subscriptions and product data:
err = client.CallRemoveInternalOrganizationMember(
ctx,
organizationID,
userID,
authara.APIInternalOrganizationActorRequest{ActorUserID: actorUserID},
)
err = client.CallDeleteInternalOrganization(
ctx,
organizationID,
authara.APIInternalOrganizationActorRequest{ActorUserID: actorUserID},
)
err = client.CallDeleteInternalUser(ctx, userID)token, ok := authara.CSRFToken(r)
authara.AttachCSRF(req, token)The SDK provides helpers for handling Authara webhooks.
Webhook verification requires the shared secret configured in Authara:
AUTHARA_WEBHOOK_SECRET=your-shared-secretThis secret must match the webhook secret used by Authara when signing outgoing webhook requests.
If you want to load the secret from the environment:
handler, err := authara.RequireWebhookHandlerFromEnv()
if err != nil {
log.Fatal(err)
}
evt, err := handler.Handle(w, r)
if err != nil {
return
}
log.Println("event:", evt.Type)You can also construct the handler manually:
handler := &authara.WebhookHandler{
Secret: os.Getenv("AUTHARA_WEBHOOK_SECRET"),
}data, _ := authara.DecodeWebhookData[authara.UserCreatedData](evt)authara.UserCreatedDataauthara.UserUpdatedDataauthara.UserDeletedDataauthara.OrganizationDataauthara.OrganizationMembershipCreatedDataauthara.OrganizationMembershipDataauthara.OrganizationInvitationCreatedDataauthara.OrganizationInvitationAcceptedDataauthara.OrganizationInvitationRevokedData
- Signature is always verified first
- Payload is explicit (raw JSON + decode)
- No retries or queues in SDK
- Webhook delivery behavior is owned by Authara, not the SDK
MIT