fix: explicitly set SameSite to Lax for cookies

This commit is contained in:
Elias Schneider
2026-08-03 22:58:01 +02:00
parent e3e0ddc192
commit 69d55ada6c
2 changed files with 56 additions and 5 deletions
+11 -5
View File
@@ -1,28 +1,34 @@
package cookie
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func AddAccessTokenCookie(c *gin.Context, maxAgeInSeconds int, token string) {
c.SetCookie(AccessTokenCookieName, token, maxAgeInSeconds, "/", "", true, true)
addCookie(c, AccessTokenCookieName, token, maxAgeInSeconds, "/")
}
func AddSessionIdCookie(c *gin.Context, maxAgeInSeconds int, sessionID string) {
c.SetCookie(SessionIdCookieName, sessionID, maxAgeInSeconds, "/", "", true, true)
addCookie(c, SessionIdCookieName, sessionID, maxAgeInSeconds, "/")
}
func AddDeviceTokenCookie(c *gin.Context, deviceToken string) {
c.SetCookie(DeviceTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), "/api/one-time-access-token", "", true, true)
addCookie(c, DeviceTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), "/api/one-time-access-token")
}
func AddDeviceLoginTokenCookie(c *gin.Context, requestID, deviceToken string) {
path := "/api/device-login/requests/" + requestID + "/exchange"
c.SetCookie(DeviceLoginTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), path, "", true, true)
addCookie(c, DeviceLoginTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), path)
}
func AddReauthenticationTokenCookie(c *gin.Context, reauthenticationToken string) {
c.SetCookie(ReauthenticationTokenCookieName, reauthenticationToken, int(3*time.Minute.Seconds()), "/", "", true, true)
addCookie(c, ReauthenticationTokenCookieName, reauthenticationToken, int(3*time.Minute.Seconds()), "/")
}
func addCookie(c *gin.Context, name, value string, maxAge int, path string) {
c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie(name, value, maxAge, path, "", true, true)
}
+45
View File
@@ -32,6 +32,51 @@ test('Authorize existing client', async ({ page }) => {
);
});
test('Authorize existing client with POST while signed in', async ({ page }, testInfo) => {
const oidcClient = oidcClients.nextcloud;
const formFields = Array.from(createUrlParams(oidcClient));
const authorizeURL = new URL('/authorize', testInfo.project.use.baseURL).toString();
const clientPageURL = new URL('/post-authorize-test', oidcClient.callbackUrl).toString();
await page.route(clientPageURL, async (route) => {
await route.fulfill({ status: 200, contentType: 'text/html', body: '<!doctype html>' });
});
await page.goto(clientPageURL);
const authorizeRequestPromise = page.waitForRequest(
(request) => request.method() === 'POST' && new URL(request.url()).pathname === '/authorize'
);
await expectCallbackRedirect(page, oidcClient.callbackUrl, async () => {
// Create and submit a form with the OIDC parameters to simulate a POST request to the /authorize endpoint
await page.evaluate(
({ fields, target }) => {
const doc = (globalThis as any).document;
const form = doc.createElement('form');
form.method = 'POST';
form.action = target;
for (const [name, value] of fields) {
const input = doc.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
form.append(input);
}
doc.body.append(form);
form.submit();
},
{ fields: formFields, target: authorizeURL }
);
// Since the access token isn't sent with the POST request because of SameSite cookie restrictions,
// /authorize thinks the user is not signed in and shows the login page.
// The interaction page has the access token so the user just needs to click "Sign in" to continue the flow.
await page.getByRole('button', { name: 'Sign in' }).click();
});
await authorizeRequestPromise;
});
test('Authorize existing client while not signed in', async ({ page }) => {
const oidcClient = oidcClients.nextcloud;
const urlParams = createUrlParams(oidcClient);