Bearer-token authentication
How Authorization: Bearer works, where to keep tokens, and why the URL is the wrong place.
Bearer-token authentication is the simplest and most common way modern web APIs identify the caller. The name says exactly what it does: whoever bears (presents) the token is treated as the owner. There is no password to compare, no nonce to verify, no challenge-response. Just send the token, and the server knows who you are.
The header format
The token goes in the Authorization request header, prefixed with Bearer and a single space. The exact wire format is fixed by RFC 6750:
Authorization: Bearer 4f8c9b...e2a1Two things to remember:
- The word "Bearer" is part of the value, not the header name. Some clients let you pass just the token and add the prefix for you; others do not. When in doubt, pass the full
Bearer <token>string yourself. - The token is opaque to the client. Do not parse it, do not split it on dots, do not try to decode it as JWT. Treat it as a string of bytes the server gives you and you send back.
Why not in the URL?
A common shortcut is putting the token in the URL as a query parameter: ?api_key=.... This works, but it leaks the token in four places you do not control:
- Browser history. Every page the user visits is saved with its full URL. Anyone with access to the browser profile can read your tokens.
- Server access logs. Web servers, reverse proxies, and CDNs all log the request line (including the query string) by default. Even if you do not log it, the upstream might.
- Referer headers. When the page contains a link or an
<img>to a third-party site, the browser sends the source URL as theReferer. The third party now has your token. - Shared screens. URL bars are visible in screenshots, screencasts, and pair-programming sessions. Headers are not.
Headers do not appear in any of those places by default. Use the Authorization header.
Where to keep the token
The "right" place depends on what kind of code is calling the API.
Server-side code (Node, Python, Ruby, Go, etc.)
An environment variable read at boot: process.env.GOREST_TOKEN, os.environ['GOREST_TOKEN'], ENV['GOREST_TOKEN']. Never commit it to the repo. Never log it. In a Twelve-Factor app, it lives in your secrets manager and gets injected at runtime.
Browser apps
This is harder because the browser is not a safe place: anything in the page can be read by any script that runs in the page (XSS, third-party widgets, browser extensions). The practical options:
- Server-side proxy. Your frontend talks to your backend. The token lives only on the backend. This is the safe default.
- HttpOnly cookie. The browser sends it automatically; JS cannot read it. Works well when your own backend issues the cookie and proxies the API call through to GoRest. The cookie holds the session for your app, and the bearer token stays server-side. GoRest itself reads the token from the
Authorizationheader or the?access-token=<token>query param; it does not read cookies. - Local storage. Easiest, but a single XSS gets every token. Only use this for tokens you can rotate cheaply and that have a tight scope.
Mobile apps
Use the platform's secure storage: Keychain on iOS, EncryptedSharedPreferences on Android. Plain UserDefaults / SharedPreferences is not enough, since those are readable by anyone with file-system access on a rooted device.
What the server actually checks
When a request arrives at GoRest, the API does roughly this:
- Pull the
Authorizationheader out of the request. - Strip the
Bearerprefix, hash the rest with SHA-256, and look up the hash. Only the hash is stored, so even a copy of the database holds no working token. - If found, attach the owning user to the request, increment the rate-limit counter for that token, and let the request through.
- If not found, return 401 with
{ "message": "Invalid token" }. If the token has an expiry that has passed, the body is{ "message": "Token expired" }instead.
There is no signature, no role, no claims inside the token itself; it is an opaque server-side identifier, not a JWT. Tokens can carry an expires field on the server side; if you set one, requests after that timestamp get a 401 with "Token expired". The advantage of opaque tokens is that revoking is instant: delete the row and the next request gets a 401. The disadvantage is that the API must hit its database on every request.
Rotation and revocation
Assume your tokens will leak some day. The /my/tokens page lets you generate per-project tokens; using one token per environment (production, staging, your laptop) means a leak in one place does not put the others at risk. Regenerate any token that has been shown in a screenshot, sent in an email, or pasted into a Slack channel. Regenerating is one click and breaks any old copies.
Common pitfalls
- Trailing newline. Echoing
$TOKENinto a header in a shell script can leave a newline at the end. The server will reject the request as malformed. Useprintfinstead ofechoif you suspect this. - Wrong case. HTTP header names are case-insensitive, but a few buggy proxies normalize them inconsistently. The standard form is
Authorizationwith a capital A. - Pre-flight CORS. When a browser makes cross-origin requests, the
Authorizationheader triggers a pre-flightOPTIONSrequest. The GoRest API answers pre-flights, but if you are tunnelling through your own proxy, make sure it does too.