OAuth2 for Backend-for-Frontend
Most OAuth2 implementations and guides focus on SPAs and mobile apps, where the frontend directly handles tokens. While this approach is common, it introduces security concerns and additional complexity.
For Remix & React Router applications, a Backend-for-Frontend (BFF) architecture provides a more secure and controlled approach to authentication and API access.
What is a Backend-for-Frontend (BFF)?
A BFF is a small server that sits between the frontend and external APIs. Instead of handling OAuth2 tokens in the browser, the BFF takes care of authentication and API requests. This approach offers several advantages:
- Manages authentication server-side
- Stores tokens securely
- Handles API requests on behalf of the frontend
Why Use a BFF with OAuth2?
Handling OAuth2 in the browser often leads to issues such as:
- Storing tokens in localStorage/sessionStorage → Exposes them to XSS attacks.
- Dealing with CORS issues → When making direct API calls from the frontend.
- Complexity in token refresh & expiration → Managing tokens in the browser requires additional handling for refresh flows.
By using a BFF, the frontend never directly interacts with OAuth2 tokens. This improves security and simplifies authentication flows.
How Authentication Works in a BFF Architecture
With a BFF, authentication follows these steps:
- The user logs in via the BFF.
- The BFF obtains and securely stores tokens.
- The frontend makes requests to the BFF, not directly to external APIs.
- The BFF forwards requests to the API, attaching the necessary tokens.
Since the BFF handles authentication, it must store tokens securely.
- Access Token: Short-lived, can be stored in memory.
- Refresh Token: Stored in a secure session (e.g., HTTP-only cookies).
This approach allows the backend to handle token refresh automatically, reducing complexity on the frontend.
Secure Token Refresh in a BFF
Instead of exposing the refresh token to the frontend, the BFF follows this process:
- The frontend makes a request to the BFF.
- The BFF checks if the access token is valid.
- If expired, it uses the refresh token to obtain a new one.
- The BFF sends the updated response back to the frontend.
With this setup, no tokens ever touch the browser, ensuring a much safer authentication flow.
Example: Authenticating Requests via a BFF in Remix
In a Remix or React Router application, a loader function can authenticate requests using the BFF:
export async function loader({ request }: Route.LoaderArgs) {
let session = await getSession(request.headers.get("cookie"));
let accessToken = await refreshToken(session.get("refreshToken"));
let response = await fetch(new URL("/user", API_BASE_URL), {
headers: { Authorization: `Bearer ${accessToken}` },
});
return data({ user: UserSchema.promise().parse(response.json()) });
}
By keeping OAuth2 tokens off the frontend, this architecture enhances security and simplifies authentication logic.
Why OAuth2 and BFF Work Well Together
A BFF architecture pairs well with OAuth2 in Remix & React Router apps because it:
✅ Prevents tokens from being exposed on the frontend
✅ Centralizes authentication logic
✅ Simplifies secure API access
✅ Handles token refresh seamlessly
If you're building a web application with OAuth2, this is an approach worth considering. It strikes a balance between security, flexibility, and ease of implementation.
I'm currently writing a book called React Router OAuth2 Handbook, focused on implementing secure OAuth2 authentication in Remix and React Router apps—using patterns you can apply to any web app.
The landing page is live (book coming soon) at books.sergiodxa.com