2

I run a multi-tenant single page application (SPA) and I am implementing a backend for frontend (BFF) for it. The BFF handles the OIDC login/logout flows, stores tokens in a session and proxies requests to a backend server w/ the token attached to each request.

I use keycloak as my identity provider. The BFF is a node.js application using expressjs and express-session. The latter stores the user's session id in a cookie.

In this SO answer on securing the code_verifier in the OIDC authorization code flow, Gary Archer recommends creating the code_verifier server-side.

Now, in order to keep the SPA lean, I want to create the code_verifier server-side and handle the identity provider's redirect server-side as well. The problem I am facing w/ the latter is that, I am loosing the user's session context and thus cannot retrieve the code_verifier to finish the authentication flow.

Is my approach bad practice? Is there a way to pass a sort of context into keycloak's authorization flow? E.g., by passing the user's session id to the initial authorization request which could then be appended to the identity provider's redirect_uri as a query param? Is there another way to share session state between the user's and the identity provider's requests to the BFF that I don't see?

tom
  • 307
  • 1
  • 8

1 Answers1

2

I want to [...] handle the identity provider's redirect server-side as well

A redirect always goes to the user's browser first, and then the browser requests the URL. I'm assuming you are aware of this, and with "handle the identity provider's redirect" you mainly mean exchanging the authorization code for the access token.

If so, Gary Archer's SO answer and the "login dance" should work for you and fit your requirements. The redirect is first processed in the SPA and then a separate request is made to the BFF. For that reason, all relevant cookies, even with SameSite=Strict attribute, will be included. The heavy lifting, in particular the token request to the IdP, is made from the BFF.

Doesn't this work for you?

The approach can also be generalized:

  • The IdP redirects to a static HTML page (served by the BFF).
  • The page executes Javascript and either redirects to a new URL of the BFF or makes a form post to the BFF.
  • The second request includes the original request data (state, authorization code etc.) as well as the cookies (session ID in your case).
  • When processing the second request, the BFF uses the data to request the access token from the IdP.
Codo
  • 75,595
  • 17
  • 168
  • 206
  • thanks for your answer. i guess there were and still are several misconceptions on my side. a) i do not understand the concept of the IDP redirect fully. i thought it's a separate request if i provide my BFF endpoint as a redirect url to the IDP. i didnt know it's still happening in the browser... that's why i figured at first there is no access to the cookie and the user session. b) my BFF is not serving static content. it's merely providing endpoints the SPA interacts with. both run on the same domain but different subdomains, though. – tom Oct 19 '22 at 16:08
  • i omitted the fact that i run everything in a dev environment locally with docker-compose. the way i made it work now is that i made sure that the IDP, the BFF/auth proxy and the SPA are behind a reverse proxy and run on the same domain. e.g. idp.mydomain.test, spa.mydomain.test and bff.mydomain.test. that way my setup works w/o redirecting to a static callback page. so the idp now redirects to `bff.mydomain.test/login/callback?session_state=xxx&code=yyy` and i handle the code to token exchange in the `login/callback` controller of the BFF. – tom Oct 19 '22 at 16:18