remark42 icon indicating copy to clipboard operation
remark42 copied to clipboard

SendJWTHeader leads to {"error":"failed to get token"}

Open nmattia opened this issue 1 year ago • 6 comments

I'm not sure how to make send-jwt-header work. I'm enabling it as an env var:

      - AUTH_SEND_JWT_HEADER=true

but unfortunately after going through the GitHub auth flow, I end up on this page (https://remark42.example.com/auth/github/callback?code=...&state=...)

{"error":"failed to get token"}

This seems to come from AuthHandler which reads either a cookie or a header (which should get set in LoginHandler).

It looks like frontend support was added for send-jwt-header though it's unclear to me how this works when redirects are used (AFAICT the fetcher.ts module is not actually being used in this case).

Any help appreciated!

Note: I'm trying to enable send-jwt-header because -- as far as I understand -- it should allow me to have remark42 on a different domain than my website (and not rely on cookies which don't seem to work too well inside iframes on Safari due to SameSite not being supported).


EDIT: feel free to play around with https://comments.nmattia.com/web/, I have send-jwt-header currently enabled

nmattia avatar Dec 20 '24 21:12 nmattia

Based on my investigation, the problem is:

  1. The Remark42 backend correctly passes the SendJWTHeader option to the authentication service in server.go (line 1208):

    SendJWTHeader: s.Auth.SendJWTHeader,
    
  2. This setting is also exposed to the frontend via the /api/v1/config endpoint (line 464):

    SendJWTHeader: s.SendJWTHeader,
    
  3. In the authentication flow, when a user initiates OAuth (e.g., with GitHub), the JWT token is initially set during the login phase:

    // in oauth2.go LoginHandler
    if _, err := p.JwtService.Set(w, claims); err != nil {
        rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to set token")
        return
    }
    
  4. The problem is in the token/jwt.go implementation. When SendJWTHeader is true, it correctly sends the JWT as a header (line 261-264):

    if j.SendJWTHeader {
        w.Header().Set(j.JWTHeaderKey, tokenString)
        return claims, nil
    }
    

    But critically, it doesn't set a cookie in this case.

  5. Later, when the callback from GitHub is handled in AuthHandler, it tries to get the token:

    oauthClaims, _, err := p.JwtService.Get(r)
    if err != nil {
        rest.SendErrorJSON(w, r, p.L, http.StatusInternalServerError, err, "failed to get token")
        return
    }
    
  6. But the Get method in jwt.go tries to retrieve the token from the query, header, or cookie. When SendJWTHeader is true, the token was sent as a header in the initial request, but browsers don't maintain headers between redirects, so this token is lost.

The issue is that when SendJWTHeader is enabled, the authentication state is lost during OAuth redirects because:

  1. The token is initially set as a header instead of a cookie
  2. Headers don't persist through redirects like cookies do
  3. When returning from the OAuth provider, there's no header or cookie containing the state

Solution

The send-jwt-header feature is designed to work with direct API calls, not with OAuth flows that involve browser redirects. For OAuth to work with the JWT in a header:

  1. During OAuth flows, the system should still set cookies temporarily (even with SendJWTHeader enabled)
  2. After successful authentication, the final response should include the JWT header
  3. The frontend could then extract this JWT header and use it for subsequent API calls

To fix this issue, the jwt.go file in the go-pkgz/auth/v2/token package would need to be modified to handle OAuth flows differently when SendJWTHeader is enabled - specifically by still setting the cookie during the OAuth handshake, even if the final authentication will use JWT headers.

I'll do the change now.

paskal avatar Mar 24 '25 22:03 paskal

Note: I'm trying to enable send-jwt-header because -- as far as I understand -- it should allow me to have remark42 on a different domain than my website (and not rely on cookies which don't seem to work too well inside iframes on Safari due to SameSite not being supported).

This is a really bright idea, I like it a lot. I see that both frontend and backend doesn't work properly, working on a fix.

paskal avatar Apr 27 '25 21:04 paskal

I found what is wrong, request is reaching https://github.com/umputun/remark42/blob/242499787eaf075ae64cdf33d802c6cd5f3ee4d9/frontend/apps/remark42/templates/iframe.ejs#L10 and then we can either set the right cookie or set a header and we are setting a header. The fix I've implemented in #1929 works for non-oauth providers, but that code is not loaded at all for oauth providers and unless it's changed, there is no way to insert the header reading into that page which is closed right after user comes to it.

paskal avatar Apr 29 '25 23:04 paskal

The send-jwt-header feature is designed to work with direct API calls

Hi @paskal , I am trying to use remark42 REST API for my comments thread for my web app. I choose to build my own frontend and hence want to make use of SendJWTHeaders. I get the same error as this issue reports. But I see you wrote "The send-jwt-header feature is designed to work with direct API calls", now I am wondering how. Can you please shed some light on it?

Wrufesh avatar Jul 08 '25 20:07 Wrufesh

Everything is in a bit messy state at the moment, I'll review it and get back to this thread within a week or two with the next steps. I haven't merged go-pkgz/auth changes and I don't remember why by now.

paskal avatar Jul 08 '25 22:07 paskal

Current architecture does not looks like it supports REST API in headless mode. I just wanted to use the APIs and build my own front end but it looks like there is no way to get jwt and xsrf to cross domain with any of the providers.

I believe that to achieve my use case oauth callback has to land on frontend with token and later verified by remark42 and send back jwt and xsrf.

Please kindly correct me if I am not aligned.

Wrufesh avatar Jul 09 '25 21:07 Wrufesh