Search code examples
keycloakopenid-connectsession-statepkce

Is there a way to pass state across the OIDC code authorization flow?


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?


Solution

  • 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.