Skip to content

Commit 9e14a78

Browse files
Accept common cookie attributes when deleting a cookie (#10671)
* Accept common cookie attributes when deleting a cookie * Fix AstroCookieSetOptions IDE annotations * Use AstroCookieSetOptions to construct AstroCookieDeleteOptions * Update .changeset/shaggy-cats-film.md Co-authored-by: Florian Lefebvre <[email protected]> --------- Co-authored-by: Florian Lefebvre <[email protected]>
1 parent cdd0c7c commit 9e14a78

File tree

3 files changed

+57
-22
lines changed

3 files changed

+57
-22
lines changed

.changeset/shaggy-cats-film.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro": minor
3+
---
4+
5+
Adds the `httpOnly`, `sameSite`, and `secure` options when deleting a cookie

packages/astro/src/core/cookies/cookies.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@ import type { CookieSerializeOptions } from 'cookie';
22
import { parse, serialize } from 'cookie';
33
import { AstroError, AstroErrorData } from '../errors/index.js';
44

5-
export interface AstroCookieSetOptions {
6-
domain?: string;
7-
expires?: Date;
8-
httpOnly?: boolean;
9-
maxAge?: number;
10-
path?: string;
11-
sameSite?: boolean | 'lax' | 'none' | 'strict';
12-
secure?: boolean;
13-
encode?: (value: string) => string;
14-
}
5+
export type AstroCookieSetOptions = Pick<
6+
CookieSerializeOptions,
7+
'domain' | 'path' | 'expires' | 'maxAge' | 'httpOnly' | 'sameSite' | 'secure' | 'encode'
8+
>;
159

1610
export interface AstroCookieGetOptions {
1711
decode?: (value: string) => string;
1812
}
1913

20-
type AstroCookieDeleteOptions = Pick<AstroCookieSetOptions, 'domain' | 'path'>;
14+
type AstroCookieDeleteOptions = Omit<AstroCookieSetOptions, 'expires' | 'maxAge' | 'encode'>;
2115

2216
interface AstroCookieInterface {
2317
value: string;
@@ -78,17 +72,22 @@ class AstroCookies implements AstroCookiesInterface {
7872
* @param options Options related to this deletion, such as the path of the cookie.
7973
*/
8074
delete(key: string, options?: AstroCookieDeleteOptions): void {
75+
/**
76+
* The `@ts-expect-error` is necessary because `maxAge` and `expires` properties
77+
* must not appear in the AstroCookieDeleteOptions type.
78+
*/
79+
const {
80+
// @ts-expect-error
81+
maxAge: _ignoredMaxAge,
82+
// @ts-expect-error
83+
expires: _ignoredExpires,
84+
...sanitizedOptions
85+
} = options || {};
8186
const serializeOptions: CookieSerializeOptions = {
8287
expires: DELETED_EXPIRATION,
88+
...sanitizedOptions,
8389
};
8490

85-
if (options?.domain) {
86-
serializeOptions.domain = options.domain;
87-
}
88-
if (options?.path) {
89-
serializeOptions.path = options.path;
90-
}
91-
9291
// Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
9392
this.#ensureOutgoingMap().set(key, [
9493
DELETED_VALUE,

packages/astro/test/units/cookies/delete.test.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,57 @@ describe('astro/src/core/cookies', () => {
4747
assert.equal(cookies.has('foo'), false);
4848
});
4949

50-
it('can provide a path', () => {
50+
it('deletes a cookie with attributes', () => {
5151
let req = new Request('http://example.com/');
5252
let cookies = new AstroCookies(req);
53+
5354
cookies.delete('foo', {
55+
domain: 'example.com',
5456
path: '/subpath/',
57+
priority: 'high',
58+
secure: true,
59+
httpOnly: true,
60+
sameSite: 'strict',
5561
});
62+
5663
let headers = Array.from(cookies.headers());
5764
assert.equal(headers.length, 1);
65+
assert.equal(/foo=deleted/.test(headers[0]), true);
66+
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
67+
assert.equal(/Domain=example.com/.test(headers[0]), true);
5868
assert.equal(/Path=\/subpath\//.test(headers[0]), true);
69+
assert.equal(/Priority=High/.test(headers[0]), true);
70+
assert.equal(/Secure/.test(headers[0]), true);
71+
assert.equal(/HttpOnly/.test(headers[0]), true);
72+
assert.equal(/SameSite=Strict/.test(headers[0]), true);
73+
});
74+
75+
it('ignores expires option', () => {
76+
let req = new Request('http://example.com/');
77+
let cookies = new AstroCookies(req);
78+
79+
cookies.delete('foo', {
80+
expires: new Date(),
81+
});
82+
83+
let headers = Array.from(cookies.headers());
84+
assert.equal(headers.length, 1);
85+
assert.equal(/foo=deleted/.test(headers[0]), true);
86+
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
5987
});
6088

61-
it('can provide a domain', () => {
89+
it('ignores maxAge option', () => {
6290
let req = new Request('http://example.com/');
6391
let cookies = new AstroCookies(req);
92+
6493
cookies.delete('foo', {
65-
domain: '.example.com',
94+
maxAge: 60,
6695
});
96+
6797
let headers = Array.from(cookies.headers());
6898
assert.equal(headers.length, 1);
69-
assert.equal(/Domain=\.example\.com/.test(headers[0]), true);
99+
assert.equal(/foo=deleted/.test(headers[0]), true);
100+
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
70101
});
71102
});
72103
});

0 commit comments

Comments
 (0)