JWT Claims
User Access Token
Section titled “User Access Token”{ "iss": "https://auth.beshoy.ai", "sub": "usr_abc123", "aud": "proj_gym", "email": "user@example.com", "email_verified": true, "role": "admin", "status": "active", "iat": 1716000000, "exp": 1716000300}Claims
Section titled “Claims”| Claim | Type | Description |
|---|---|---|
iss | string | Always "https://auth.beshoy.ai" |
sub | string | User ID (usr_ prefixed) |
aud | string | Project ID this token was issued for |
email | string | User’s email address |
email_verified | boolean | Whether email has been verified |
role | string | User’s role in the project ("admin" or "member") |
status | string | User’s status ("active" or "blocked") |
iat | number | Issued at (Unix timestamp) |
exp | number | Expires at (Unix timestamp, iat + 300) |
PIN Access Token
Section titled “PIN Access Token”{ "iss": "https://auth.beshoy.ai", "sub": "anon", "aud": "proj_trip", "role": "pin_member", "pin_id": "pin_xyz789", "privileges": ["view", "edit", "date-spots"], "iat": 1716000000, "exp": 1716000300}PIN-Specific Claims
Section titled “PIN-Specific Claims”| Claim | Type | Description |
|---|---|---|
sub | string | Always "anon" for PIN sessions |
role | string | Always "pin_member" |
pin_id | string | ID of the PIN used to authenticate |
privileges | string[] | Privileges assigned to this PIN |
OIDC ID Token (ES256)
Section titled “OIDC ID Token (ES256)”{ "iss": "https://auth.beshoy.ai", "sub": "usr_abc123", "aud": "cloudflare_sso", "email": "me@beshoy.eu", "email_verified": true, "iat": 1716000000, "exp": 1716000300}Signed with ES256 (P-256 ECDSA) instead of HS256. Public key available at /.well-known/jwks.json.
Signing
Section titled “Signing”| Token Type | Algorithm | Key |
|---|---|---|
| Access token | HS256 | Per-project signing_key (256-bit) |
| PIN token | HS256 | Same per-project signing_key |
| OIDC ID token | ES256 | Single EC P-256 key pair |
Important Notes
Section titled “Important Notes”- Tokens are project-scoped. A token for
proj_gymcannot be verified withproj_trip’s signing key. - Status is embedded. Check it on every request — don’t assume “active” from a previous check.
- No Hasura claims. Unlike some setups, there are no
x-hasura-*claims in the JWT. Hasura integration happens via the proxy pattern with admin-secret headers. - 5-minute lifetime. Access tokens expire quickly. Always implement refresh logic.