# Authenticating SPA requests to your own API
## Same origin vs cross origin
Clerk's rule: if the JS and the API share an origin, the session cookie goes along automatically and the request is authenticated by default. If they are on different origins (app on `foo.com`, API on `api.foo.com`), you must attach the session token yourself.
## The cross-origin pattern
```js
const { getToken } = useAuth()
const authenticatedFetch = async (...args) => {
const token = await getToken()
return fetch(...args, {
headers: { Authorization: 'Bearer ' + token },
}).then((res) => res.json())
}
```
Three things agents get wrong:
1. `getToken()` returns a Promise. `Authorization: 'Bearer ' + getToken()` without await sends the string "Bearer [object Promise]" and the backend 401s.
2. Build the header on every request, not once at startup. Tokens refresh; a captured token goes stale.
3. The backend must actually verify the token (verifyToken / authenticateRequest with authorizedParties set). Accepting the header without verifying it is theater.
## Backend side
On the API, prefer `authenticateRequest()` over hand-rolled JWT parsing; it handles cookies and the Authorization header and the JWKS lookup. Set `authorizedParties` to your frontend origins. Clerk's docs call this out explicitly because leaving it unset opens CSRF-style attacks where another site's token is accepted.
## Checklist
- Same origin: plain `fetch()` works, cookies ride along. Do not add the header anyway; it is redundant.
- Cross origin: `await getToken()`, Bearer header, and the API verifies with authorized parties configured.
- `useAuth()` only works inside `ClerkProvider`. The fetcher hook must be called from a component under the provider.