Token Formats
Opaque tokens vs JWT, claims structure, OIDC compatibility, JWKS endpoint, and per-app token configuration.
Authsome supports two token formats for session tokens: opaque tokens and JSON Web Tokens (JWT). The format can be configured globally and overridden per application.
Opaque tokens
Opaque tokens are the default format. They are cryptographically random strings with no embedded data. To validate an opaque token, the resource server must call Authsome's session lookup endpoint (or query the session store directly).
Authorization: Bearer a3f8c9d4e5f2g7h1j2k3m4n5p6q7r8s9Advantages:
- Immediate revocation — deleting the session record instantly invalidates the token
- No token size overhead
- No cryptographic key management required
- Tokens cannot be decoded by clients (no information leakage)
Trade-offs:
- Every request requires a session store lookup
- Not self-contained — cannot be verified without contacting the auth server
Configuration
Session: authsome.SessionConfig{
TokenFormat: "opaque", // default
}JWT tokens
JWT tokens are self-contained, signed tokens that embed claims about the user and session. Resource servers can verify them locally using the public signing key, without contacting Authsome.
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0yMDI0LTAxIn0...Advantages:
- Stateless verification — no session store lookup needed
- Self-contained user claims reduce API calls
- Compatible with OIDC-compliant resource servers
- Can be verified by any service with access to the public key
Trade-offs:
- Tokens are valid until expiry, even after session revocation (eventual consistency)
- Larger token size (typically 800-1200 bytes)
- Requires cryptographic key management
Configuration
Session: authsome.SessionConfig{
TokenFormat: "jwt",
JWT: authsome.JWTConfig{
SigningAlgorithm: "RS256", // RS256, RS384, RS512, ES256, ES384, ES512
Issuer: "https://auth.myapp.com",
Audience: []string{"https://api.myapp.com"},
KeyID: "key-2024-01",
PrivateKeyPath: "/path/to/private.pem",
},
}JWT claims structure
When using JWT format, session tokens contain the following claims:
Standard claims
{
"iss": "https://auth.myapp.com",
"sub": "ausr_01j9...",
"aud": ["https://api.myapp.com"],
"exp": 1730455200,
"iat": 1730451600,
"jti": "ases_01j9..."
}| Claim | Description |
|---|---|
iss | Issuer — your Authsome instance URL |
sub | Subject — the user ID |
aud | Audience — intended token recipients |
exp | Expiration time (Unix timestamp) |
iat | Issued at time (Unix timestamp) |
jti | JWT ID — the session ID (unique per token) |
Authsome custom claims
{
"app_id": "aapp_01j9...",
"env_id": "aenv_01j9...",
"email": "alice@example.com",
"email_verified": true,
"name": "Alice Liddell",
"username": "alice",
"org_id": "aorg_01j9...",
"roles": ["admin", "member"],
"session_id": "ases_01j9..."
}| Claim | Description |
|---|---|
app_id | Application the session belongs to |
env_id | Environment the session belongs to |
email | User's email address |
email_verified | Whether the email has been verified |
name | User's display name |
username | User's username |
org_id | Organization scope (if session is org-scoped) |
roles | User's RBAC roles |
session_id | The Authsome session ID |
OIDC-compatible claims
When the openid scope is requested (via the OAuth2 provider), ID tokens include standard OIDC claims:
{
"iss": "https://auth.myapp.com",
"sub": "ausr_01j9...",
"aud": "aoc2_01jb...",
"exp": 1730455200,
"iat": 1730451600,
"auth_time": 1730451600,
"nonce": "random_nonce_value",
"at_hash": "fUHyO2r2Z3Dz10...",
"email": "alice@example.com",
"email_verified": true,
"name": "Alice Liddell",
"preferred_username": "alice",
"picture": "https://cdn.example.com/alice.jpg",
"phone_number": "+15551234567",
"phone_number_verified": true,
"updated_at": 1730451600
}Claims are included based on the requested scopes:
| Scope | Claims included |
|---|---|
openid | sub, iss, aud, exp, iat, auth_time |
profile | name, preferred_username, picture, updated_at |
email | email, email_verified |
phone | phone_number, phone_number_verified |
JWKS endpoint
The JWKS (JSON Web Key Set) endpoint publishes the public keys used to verify JWT tokens. Resource servers fetch this endpoint to obtain the verification keys.
GET /.well-known/jwks.json
{
"keys": [
{
"kty": "RSA",
"kid": "key-2024-01",
"use": "sig",
"alg": "RS256",
"n": "0vx7agoebGcQSuuPiLJXZ...",
"e": "AQAB"
}
]
}Key rotation
To rotate signing keys without downtime:
- Generate a new key pair with a new
kid - Add the new public key to the JWKS endpoint
- Update the engine to sign with the new key
- After the maximum token lifetime has elapsed, remove the old public key
Both old and new keys are available simultaneously during the rotation window, allowing resource servers to verify tokens signed with either key.
Per-app token format overrides
Different applications can use different token formats. For example, your main web application might use opaque tokens (for immediate revocation), while a public API uses JWT (for stateless verification).
import "github.com/xraph/authsome/appsessionconfig"
// Web app: opaque tokens with short TTL
webConfig := &appsessionconfig.Config{
AppID: webAppID,
TokenFormat: "opaque",
TokenTTLSeconds: intPtr(1800), // 30 minutes
}
err := engine.SetAppSessionConfig(ctx, webConfig)
// Public API: JWT tokens with longer TTL
apiConfig := &appsessionconfig.Config{
AppID: apiAppID,
TokenFormat: "jwt",
TokenTTLSeconds: intPtr(3600), // 1 hour
}
err := engine.SetAppSessionConfig(ctx, apiConfig)The per-app config structure:
type Config struct {
ID id.AppSessionConfigID `json:"id"`
AppID id.AppID `json:"app_id"`
TokenTTLSeconds *int `json:"token_ttl_seconds,omitempty"`
RefreshTokenTTLSeconds *int `json:"refresh_token_ttl_seconds,omitempty"`
MaxActiveSessions *int `json:"max_active_sessions,omitempty"`
RotateRefreshToken *bool `json:"rotate_refresh_token,omitempty"`
BindToIP *bool `json:"bind_to_ip,omitempty"`
BindToDevice *bool `json:"bind_to_device,omitempty"`
TokenFormat string `json:"token_format,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}Fields set to nil inherit from the global engine configuration.
Token expiration configuration
Global defaults
Session: authsome.SessionConfig{
TokenTTL: 1 * time.Hour, // session token
RefreshTokenTTL: 30 * 24 * time.Hour, // refresh token (30 days)
}Recommended settings by use case
| Use case | Token TTL | Refresh TTL | Format | Notes |
|---|---|---|---|---|
| Web application | 30m-1h | 7-30d | opaque | Short tokens + refresh for UX |
| Mobile app | 1h | 90d | jwt | Longer refresh for offline use |
| Single-page app | 15m | 7d | opaque | Short for security, refresh on tab focus |
| Machine-to-machine | 1h | N/A | jwt | No refresh, re-authenticate |
| High-security | 15m | 1d | opaque | Short tokens, frequent re-auth |
Verifying tokens in Go
Opaque tokens
// Look up session by token
sess, err := engine.ValidateToken(ctx, token)
if err != nil {
// Token is invalid, expired, or revoked
}
// sess.UserID, sess.AppID, etc. are availableJWT tokens
import "github.com/xraph/authsome/jwt"
// Parse and verify a JWT token
claims, err := jwt.Verify(token, jwt.VerifyOptions{
PublicKey: publicKey,
Issuer: "https://auth.myapp.com",
Audience: "https://api.myapp.com",
})
if err != nil {
// Token is invalid, expired, or has wrong issuer/audience
}
// claims.Subject → user ID
// claims.SessionID → session ID