Skip to content

Commit 16536d5

Browse files
committed
Set cookies for OAuth flows when SendJWTHeader is enabled
This fixes umputun/remark42#1877 where OAuth authentication fails when the send-jwt-header option is enabled. The problem occurred because: 1. When SendJWTHeader is enabled, the auth service only sends the JWT as a header without setting cookies 2. During OAuth flows, the authentication involves redirects between the app and the provider 3. HTTP headers don't persist through redirects, so the authentication state was lost The solution: - Modified the jwt.go token Set method to always set cookies during OAuth handshake phases (when claims.Handshake != nil), even when SendJWTHeader is enabled - For normal authentication (non-handshake), maintain the original behavior where SendJWTHeader=true will only set headers - This ensures the OAuth flow works properly while maintaining the correct behavior for API requests
1 parent 4f9f2c5 commit 16536d5

File tree

4 files changed

+62
-20
lines changed

4 files changed

+62
-20
lines changed

token/jwt.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,23 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
247247
return Claims{}, fmt.Errorf("failed to make token token: %w", err)
248248
}
249249

250-
if j.SendJWTHeader {
251-
w.Header().Set(j.JWTHeaderKey, tokenString)
252-
return claims, nil
253-
}
254-
255250
cookieExpiration := 0 // session cookie
256251
if !claims.SessionOnly && claims.Handshake == nil {
257252
cookieExpiration = int(j.CookieDuration.Seconds())
258253
}
259254

255+
// For OAuth handshake, always set cookies regardless of SendJWTHeader flag
256+
// This allows the OAuth flow to complete successfully
257+
needsCookies := claims.Handshake != nil
258+
259+
// Set the JWT in the header if requested,
260+
// skip setting cookies unless this is part of OAuth handshake
261+
if j.SendJWTHeader && !needsCookies {
262+
w.Header().Set(j.JWTHeaderKey, tokenString)
263+
return claims, nil
264+
}
265+
266+
// Set cookies (always for OAuth handshake, or when SendJWTHeader is false)
260267
jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: tokenString, HttpOnly: true, Path: "/", Domain: j.JWTCookieDomain,
261268
MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
262269
http.SetCookie(w, &jwtCookie)

token/jwt_test.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,27 @@ func TestJWT_SendJWTHeader(t *testing.T) {
258258
SendJWTHeader: true,
259259
})
260260

261-
rr := httptest.NewRecorder()
262-
_, err := j.Set(rr, testClaims)
263-
assert.NoError(t, err)
264-
cookies := rr.Result().Cookies()
265-
t.Log(cookies)
266-
require.Equal(t, 0, len(cookies), "no cookies set")
267-
assert.Equal(t, testJwtValid, rr.Result().Header.Get("X-JWT"))
261+
t.Run("with handshake", func(t *testing.T) {
262+
rr := httptest.NewRecorder()
263+
_, err := j.Set(rr, testClaims)
264+
assert.NoError(t, err)
265+
cookies := rr.Result().Cookies()
266+
t.Log(cookies)
267+
require.Equal(t, 2, len(cookies), "cookies are set for handshake")
268+
assert.Equal(t, testJwtValid, rr.Result().Header.Get("X-JWT"))
269+
})
270+
271+
t.Run("without handshake", func(t *testing.T) {
272+
rr := httptest.NewRecorder()
273+
claimsNoHandshake := testClaims
274+
claimsNoHandshake.Handshake = nil
275+
_, err := j.Set(rr, claimsNoHandshake)
276+
assert.NoError(t, err)
277+
cookies := rr.Result().Cookies()
278+
t.Log(cookies)
279+
require.Equal(t, 0, len(cookies), "no cookies set for non-handshake")
280+
assert.Equal(t, testJwtValidNoHandshake, rr.Result().Header.Get("X-JWT"))
281+
})
268282
}
269283

270284
func TestJWT_SetProlonged(t *testing.T) {

v2/token/jwt.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,13 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
264264
return Claims{}, fmt.Errorf("failed to make token token: %w", err)
265265
}
266266

267-
if j.SendJWTHeader {
267+
// For OAuth handshake, always set cookies regardless of SendJWTHeader flag
268+
// This allows the OAuth flow to complete successfully
269+
needsCookies := claims.Handshake != nil
270+
271+
// Set the JWT in the header if requested,
272+
// skip setting cookies unless this is part of OAuth handshake
273+
if j.SendJWTHeader && !needsCookies {
268274
w.Header().Set(j.JWTHeaderKey, tokenString)
269275
return claims, nil
270276
}
@@ -274,6 +280,7 @@ func (j *Service) Set(w http.ResponseWriter, claims Claims) (Claims, error) {
274280
cookieExpiration = int(j.CookieDuration.Seconds())
275281
}
276282

283+
// Set cookies (always for OAuth handshake, or when SendJWTHeader is false)
277284
jwtCookie := http.Cookie{Name: j.JWTCookieName, Value: tokenString, HttpOnly: true, Path: "/", Domain: j.JWTCookieDomain,
278285
MaxAge: cookieExpiration, Secure: j.SecureCookies, SameSite: j.SameSite}
279286
http.SetCookie(w, &jwtCookie)

v2/token/jwt_test.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,27 @@ func TestJWT_SendJWTHeader(t *testing.T) {
258258
SendJWTHeader: true,
259259
})
260260

261-
rr := httptest.NewRecorder()
262-
_, err := j.Set(rr, testClaims)
263-
assert.NoError(t, err)
264-
cookies := rr.Result().Cookies()
265-
t.Log(cookies)
266-
require.Equal(t, 0, len(cookies), "no cookies set")
267-
assert.Equal(t, testJwtValid, rr.Result().Header.Get("X-JWT"))
261+
t.Run("with handshake", func(t *testing.T) {
262+
rr := httptest.NewRecorder()
263+
_, err := j.Set(rr, testClaims)
264+
assert.NoError(t, err)
265+
cookies := rr.Result().Cookies()
266+
t.Log(cookies)
267+
require.Equal(t, 2, len(cookies), "cookies are set for handshake")
268+
assert.Equal(t, testJwtValid, rr.Result().Header.Get("X-JWT"))
269+
})
270+
271+
t.Run("without handshake", func(t *testing.T) {
272+
rr := httptest.NewRecorder()
273+
claimsNoHandshake := testClaims
274+
claimsNoHandshake.Handshake = nil
275+
_, err := j.Set(rr, claimsNoHandshake)
276+
assert.NoError(t, err)
277+
cookies := rr.Result().Cookies()
278+
t.Log(cookies)
279+
require.Equal(t, 0, len(cookies), "no cookies set for non-handshake")
280+
assert.Equal(t, testJwtValidNoHandshake, rr.Result().Header.Get("X-JWT"))
281+
})
268282
}
269283

270284
func TestJWT_SetProlonged(t *testing.T) {

0 commit comments

Comments
 (0)