From OIDC Client to Backend for Frontend (BFF):
A Journey of Migration and Modernization
Transforming Legacy Authentication into a Scalable Future
As software engineers, we often find ourselves tasked with solving complex problems that push us to rethink our approaches and embrace new technologies. Over the past couple of months, I tackled one such challenge: migrating from an OIDC client implementation to a Backend for Frontend (BFF) pattern in a project I’ve been working on for over two years. In this article, I’ll share the journey of this migration: what motivated the change, how the process unfolded, and the valuable lessons I learned along the way.
Why Migrate to BFF?
For those unfamiliar with the concept, OpenID Connect (OIDC) clients are commonly used in Single Page Applications (SPAs) to manage authentication and authorization flows. They handle interactions with an Identity Provider (IdP), allowing the front end to obtain access and refresh tokens for API calls. While effective, this setup comes with certain limitations:
1. Security Concerns:
● Access and refresh tokens are often stored in the browser (e.g., local storage or cookies), leaving them vulnerable to XSS attacks.
● APIs rely on these tokens, increasing the risk of token misuse.
2. Complex Front-End Logic:
● The front end must handle token management, including silent renewals and error handling, which can bloat the codebase and make maintenance more challenging.
The BFF pattern addresses these issues by introducing a server-side intermediary—the BFF—that manages authentication flows and token handling on behalf of the client. Here’s why we made the switch:
●
Security:
Sensitive tokens remain on the server, reducing exposure to XSS attacks.
●
Simplified Front End:
Authentication logic shifts to the backend, streamlining the SPA.
●
Future-Proofing:
The BFF serves as a single integration point for various front-end clients.
How We Migrated
Step 1: Setting Up the BFF Layer
The BFF acts as a proxy between the SPA and the IdP. It handles all token exchanges and securely stores tokens on the server side. In our case, we used the
Duende BFF
library for seamless integration with our existing .NET backend.
Here’s a simplified flow:
1. The SPA sends a login request to the BFF (/bff/login endpoint).
2. The BFF redirects the user to the IdP for authentication.
3. The IdP responds with an authorization code, which the BFF exchanges for tokens.
4. The BFF stores tokens securely and establishes a session with the SPA using HTTP-only cookies.
Step 2: Updating API Calls
The SPA no longer interacts directly with the APIs. Instead:
● All requests are sent to the BFF.
● The BFF attaches the access token and forwards the request to the relevant API.
● The API responds to the BFF, which then sends the data back to the SPA.
This proxying mechanism ensures that tokens never leave the server.
Step 3: Handling Token Refresh and Logout
Token Refresh: The BFF handles token renewal transparently, ensuring uninterrupted user sessions.
Logout: The SPA sends a logout request to the BFF (/bff/login?sid=session-id endpoint), which clears the session and notifies the IdP to revoke tokens.
Key Configuration for BFF
Implementing the BFF pattern required significant configuration to ensure security, functionality, and performance. Below are some of the key parts of the setup.
1. Configuring Authentication
Here’s how the authentication flow is configured:
2. Securing Tokens
To securely forward access tokens for API calls, the following middleware was added:
3. Reverse Proxy with YARP
The BFF also serves as a reverse proxy for securely forwarding requests to backend APIs:
Lessons Learned
Here are some key takeaways from this migration:
1. Security is Paramount: Shifting token handling to the backend significantly reduced vulnerabilities.
2. Keep It Simple: Centralizing authentication logic in the BFF reduced front-end complexity and improved maintainability.
3. Plan for Scale: The BFF architecture provides a solid foundation for scaling applications with multiple front-end clients.
Conclusion
Migrating from an OIDC client to a BFF implementation wasn’t just about solving immediate challenges; it was about creating a secure, scalable, and maintainable system for the future. This journey reaffirmed the value of embracing modern architectural patterns, even when they require significant changes.
If you’re considering a similar migration or have questions about the BFF pattern, feel free to reach out. Let’s exchange ideas and keep building innovative solutions!