Authentication
This guide explains how to authenticate with our GraphQL API, obtain and use tokens, refresh sessions, and handle common errors.
- our API uses short-lived access tokens (JWT) and longer-lived refresh tokens.
- You can carry the access token in the
Authorizationheader or rely on secure cookies set during login.
Login
Use the login mutation with user credentials. On success you’ll receive:
token: access token (JWT)refreshToken: refresh tokenexpiresIn: access token lifetime in seconds (typically 900)
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
refreshToken
expiresIn
}
}Tip: If you are using a browser client, the server may also set secure cookies for you: _trp (access) and _trpr (refresh).
Sending Authenticated Requests
Include the access token as a bearer token in each request.
curl -X POST "$GRAPHQL_ENDPOINT" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"query": "query { user { id email } }"
}'If you are using cookies (browser flows), requests will be authenticated automatically when the cookies are present and valid.
Token Lifetimes
- Access token: ~15 minutes (returns
expiresIn: 900) - Refresh token: ~12 hours
Plan to refresh the access token before it expires, or handle 401/Unauthorized responses by triggering a refresh.
Refreshing the Access Token
Use the refreshToken mutation to obtain a new access token. You can supply the refresh token explicitly, or omit it and rely on the refresh cookie if set.
mutation RefreshToken($refreshToken: String) {
refreshToken(refreshToken: $refreshToken) {
token
refreshToken
expiresIn
}
}Notes:
- If you pass
refreshToken, it must match the stored refresh token for your user. - If omitted, the server will read the refresh token from the secure cookie when available.
- The refresh token rotates on every use. Each call to
refreshTokenreturns a new refresh token alongside the new access token. Always persist therefreshTokenvalue from the response and use it for the next refresh; do not keep reusing the original token fromlogin. - There is no session limit. Logging in again for the same account (for example, a second worker process or a second browser tab) does not invalidate the first session's tokens. Concurrent sessions for one account are supported.
Cookies vs Headers
- Headers: Works in any client. Send
Authorization: Bearer <token>. - Cookies: In browser flows, secure cookies (
_trpfor access,_trprfor refresh) may be set. They areSecureandSameSite=Strict. - You can mix approaches: use cookies in the browser and still attach the
Authorizationheader for API calls from your backend.
Error Handling
Common authentication errors and suggested actions:
Unauthorized: No token provided: Ensure theAuthorizationheader or access cookie is present.Unauthorized: Token verification failed: Access token is invalid or expired; refresh and retry.No refresh token provided: Pass the refresh token or ensure the refresh cookie is set.Invalid refresh token: Token mismatch or tampering detected; prompt for re-login.Refresh token expired: Refresh session has ended; prompt for re-login.Incorrect email or password(fromlogin); Credentials are wrong; prompt for re-entry. As observed today, this error is returned withextensions.code: INTERNAL_SERVER_ERROR, not a dedicatedUNAUTHENTICATEDcode. Match on the error message, not onextensions.code, when handling a failed login; do not branch client logic on that code for this mutation.
Sign-out
- Clients should remove stored tokens (and clear cookies if applicable). If you need server-side revocation of a refresh token, contact support for guidance.
Best Practices
- Prefer short-lived access tokens and refresh as needed.
- Store tokens securely; avoid exposing them to untrusted scripts.
- In SPAs, prefer secure, HTTP-only cookies for refresh; attach access tokens via header for API calls.
- Handle
401by attempting a single refresh, then redirect to login if it fails.
Updated 13 days ago