The problem it solves
Yoursk_* secret key grants full access to your whole organisation, so it can never ship in a frontend bundle. The usual way around this is to build and operate a proxy backend: it holds the secret key, logs each of your users in, forwards their calls to Struct, and enforces a per-user rate limit so one user can’t burn your whole quota. That proxy is real infrastructure to write, host, and pay for, and it adds a network hop to every request.
JWT public key auth removes that layer. You create a publishable pk_jwt_* key, point it at your auth provider’s public keys, and your frontend talks to Struct directly. Struct verifies each end user’s JWT, bills your organisation, and applies the per-user rate limit for you.
What you get
- No secret key in the browser. The
pk_jwt_*key is safe to hardcode in a frontend bundle. On its own it does nothing; every request also needs a valid JWT signed by your configured auth provider. - No proxy to build or run. Skip the auth-and-forward backend and call Struct directly from the client. Less infrastructure to maintain and host, lower cost.
- Per-user rate limits. Set a
per_session_rpmcap that Struct enforces per end user (keyed on the JWTsubclaim), on top of your parent key’s limits, so a single user can’t exhaust your quota. - Any auth provider. Works with Privy, Auth0, Clerk, Turnkey, Google, or your own issuer, as long as it signs JWTs with an asymmetric key.
Wire Format
Every request requires two credentials: yourpk_jwt_* key (hardcoded in your app) and the user’s JWT (obtained at login).
REST
WebSocket
Auth Flow
Key lookup
The
pk_jwt_ prefix triggers the JWT public key auth flow. The key record is loaded from the database (cached 60 seconds). If the key is disabled or not found, the request returns 401.JWT verification
JWKS URL keys: The JWKS is fetched from the configured URL (cached 5 minutes). If the JWT’s
kid is not found in the cached set, the JWKS is re-fetched once to handle key rotation.Inline public key: The PEM or JWK JSON key is used directly to verify the JWT signature.Supported algorithms: RS256, RS384, RS512, ES256, ES384, EdDSA.Claims validation
aud and iss claims are validated if configured on the key. If the JWT is expired or the signature is invalid, the request returns 401.Rate limiting and billing
The
sub claim is extracted as the session identifier. The parent API key’s rate limit and credit cap apply. If per_session_rpm is set, an additional per-user rate limit is checked (keyed on pk_jwt_xxx:sub).Supported Algorithms
Only asymmetric JWT signing algorithms are supported:| Family | Algorithms |
|---|---|
| RSA | RS256, RS384, RS512 |
| Elliptic Curve | ES256, ES384 |
| Edwards Curve | EdDSA |
Key Verification Methods
JWKS URL
Provide a JWKS endpoint URL and Struct will fetch the public keys automatically. This is the standard approach for most auth providers. The JWKS is cached for 5 minutes, and if a JWT arrives with an unknownkid, the JWKS is re-fetched once to handle key rotation.
Inline Public Key
Alternatively, provide the public key directly in PEM format (-----BEGIN PUBLIC KEY-----) or as JWK JSON ({"kty":"RSA", ...}). The key is used directly to verify JWT signatures without any external fetch.