Skip to content

Commit 1e9c879

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 1e9c879

File tree

4 files changed

+57
-16
lines changed

4 files changed

+57
-16
lines changed

token/jwt.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,14 @@ 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 {
250+
// For OAuth handshake, always set cookies regardless of SendJWTHeader flag
251+
// This allows the OAuth flow to complete successfully
252+
needsCookies := claims.Handshake != nil
253+
254+
if j.SendJWTHeader && !needsCookies {
251255
w.Header().Set(j.JWTHeaderKey, tokenString)
256+
// reset cookies in case they were set by OAuth handshake
257+
j.Reset(w)
252258
return claims, nil
253259
}
254260

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, 2, len(cookies), "empty 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,8 +264,14 @@ 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+
if j.SendJWTHeader && !needsCookies {
268272
w.Header().Set(j.JWTHeaderKey, tokenString)
273+
// reset cookies in case they were set by OAuth handshake
274+
j.Reset(w)
269275
return claims, nil
270276
}
271277

@@ -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, 2, len(cookies), "empty 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)