3

I'm exploring the possibility of implementing OpenID Connect (OIDC) with an HTTP-only cookie to keep my frontend code completely authentication-agnostic, instead of passing the Authorization header around through Javascript code. The idea behind this is that front-end developers don't have to worry about authentication.

  • My frontend is a SPA build with Javascript and HTML and consists only of static files.
  • My backend is a REST API, with no UI.
  • I want to use OIDC Implicit Flow but my Authorization Server sets the HTTP-Only cookie with access_token JWT, instead of passing the token back as a query parameter.
  • I believe that by using an HTTP-Only cookie, I can make Implicit flow as secure as Authorization Code with PKCE flow because the token is not exposed as a URL parameter when redirecting back to the client after a successful login.

I think HTTP-only Cookie as a JWT carrier is possible because all my client applications, resource servers, and the login screen are on the same origin. HTTP-Only cookies are also invisible to Javascript code, therefore they are inherently secure against XSS attacks. Especially I want my frontend-applications not to use auth-token grant, replacing it with access_code being set in the JWT cookie already, so that first call to /token endpoint could be skipped, which I believe removes a lot of complexity from the frontend part.

The only thing my front-end could do is intercept handling HTTP responses from AJAX calls using Fetch API (I could achieve it with a simple wrapper around fetch() function, I could provide it in a mini JS library that frontend developers should use). So that 401 and 403 status codes would redirect users to the login screen.

What are the security implications of such a setup? Specifically, I am interested in how to handle:

  • CSRF-tokens, I see some resources recommend using SameSite=Strict cookie attribute, but is it enough? I probably still want to use CSRF-token, but how could I validate on the resource-server side?
  • Similarly, how should I deal with Refresh-Tokens? Should I still have a /token endpoint with refresh_token grant-type support? Should I also keep the refresh tokens in HTTP-only cookie in that case?

Please indicate any security issues this kind of setup has, or suggest alternative ways. Please consider that my application frontend is completely static, and there is no way to generate HTML or Javascript in the backend.

7
  • 1
    can you motivate why you want to deviate from the Authentication Bearer token. Especially since its part of the standards for authentication, and using cookies isn't. I suspect you are less agnostic with your choice than you surmise.
    – LvB
    Commented Dec 11, 2023 at 16:38
  • As I updated in the question "HTTP-Only cookies are also invisible to Javascript code, therefore they are inherently secure against XSS attacks". So my JS code cannot see the JWT, and this enforces delegation of security to the Backend. Also, I want to be able to switch between Opaque token and JWT whenever needed, so I need to prevent frontend developers from accessing and parsing JWT from their JS because I don't want them to assume that the JWT payload is there.
    – Luke 10X
    Commented Dec 11, 2023 at 17:02
  • Also I some online resources advice: "don’t store a JWT in local storage (or session storage). If any of the third-party scripts you include in your page is compromised, it can access all your users’ tokens. To keep them secure, you should always store JWTs inside an httpOnly cookie." source: linkedin.com/pulse/jwt-token-where-store-browser-deepak-jha
    – Luke 10X
    Commented Dec 11, 2023 at 17:09
  • Answer on how to store JWT in browser also suggests that HTTP-Only cookies are "likely the best option you get if taken care of properly".
    – Luke 10X
    Commented Dec 11, 2023 at 17:17
  • 1
    If "all my client applications, resource servers, and the login screen are on the same origin" then why use OIDC at all? Is this just for future-proofing in case you want to use external auth (or provide auth to external apps) at some point? Otherwise, this sounds like the typical case for classic local authentication, which is much less complicated (and thus at less risk of being poorly implemented) than OIDC. OIDC is for external auth (federated authentication or SSO), which doesn't seem to apply here.
    – CBHacking
    Commented Dec 11, 2023 at 21:25

1 Answer 1

1

When you talk about JWT, I guess you are talking about the id_token which is the only token which is required to be a JWT by OpenID Connect.

You can actually do better than that. Your frontend actually does not need to keep the id_token and, when using the authorization code flow, does not need to ever see the id_token, access_token and refresh_token at all. All your frontend has to see is the authorization code.

What you can do is store a (HTTP only) session cookie in the frontend (eg. JSESSIONID, PHPSESSID):

  1. When you start the OpenID Connect dance, the backend generates a state, nonce, code_verifier and code_challenge and associates all of that to the session.
  2. Your backend generates an authentication request.
  3. At some point, the OpenID provider sends back the state parameter and authorization code to your application.
  4. Your backend checks that the state is the one which is stored in the session, does several additional verifications.
  5. Your backend exchanges the code it received with the id_token, access_token and refresh_token.
  6. Your backend generates a new session and associates these tokens with this new session.

As you see, in this workflow, the frontend:

  • never sees any id_token, access_token, refresh_token;
  • sees the authorization code;
  • might see the state and code_challenge (unless Pushed Authorization Requests which you should use if you can).

In this example, the session ID was stored as a HTTP only cookie but it could be stored through some other mechanism if needed.

I think HTTP-only Cookie as a JWT carrier is possible because all my client applications, resource servers

You are talking about resource servers, are you really talking about OpenID Connect of about OAuth in general?

2
  • This answer is for a scenario where the backend and frontend are the same app. My backend is a REST API with no UI, and the frontend is a Javascript-based SPA, which acts as a client in OIDC Implicit flow. However, I am willing to make certain deviations from the standard in the frontend, taking advantage of having all my applications on the same origin host. My backend acts as a resource-server and I want it to use access_token JWT to authorize to it. The only difference from a normal OIDC resource-server is that instead of using HTTP header it would use a cookie.
    – Luke 10X
    Commented Dec 12, 2023 at 14:09
  • Given that I am on single origin, I assume it is safe to use Implicit flow, therefore I don't need to exchange authorizatins_code to access_token as explained in step #5
    – Luke 10X
    Commented Dec 12, 2023 at 14:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .