mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
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 <contact@florian-lefebvre.dev> --------- Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
This commit is contained in:
parent
cdd0c7ca36
commit
9e14a78cb0
3 changed files with 57 additions and 22 deletions
5
.changeset/shaggy-cats-film.md
Normal file
5
.changeset/shaggy-cats-film.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"astro": minor
|
||||
---
|
||||
|
||||
Adds the `httpOnly`, `sameSite`, and `secure` options when deleting a cookie
|
|
@ -2,22 +2,16 @@ import type { CookieSerializeOptions } from 'cookie';
|
|||
import { parse, serialize } from 'cookie';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
|
||||
export interface AstroCookieSetOptions {
|
||||
domain?: string;
|
||||
expires?: Date;
|
||||
httpOnly?: boolean;
|
||||
maxAge?: number;
|
||||
path?: string;
|
||||
sameSite?: boolean | 'lax' | 'none' | 'strict';
|
||||
secure?: boolean;
|
||||
encode?: (value: string) => string;
|
||||
}
|
||||
export type AstroCookieSetOptions = Pick<
|
||||
CookieSerializeOptions,
|
||||
'domain' | 'path' | 'expires' | 'maxAge' | 'httpOnly' | 'sameSite' | 'secure' | 'encode'
|
||||
>;
|
||||
|
||||
export interface AstroCookieGetOptions {
|
||||
decode?: (value: string) => string;
|
||||
}
|
||||
|
||||
type AstroCookieDeleteOptions = Pick<AstroCookieSetOptions, 'domain' | 'path'>;
|
||||
type AstroCookieDeleteOptions = Omit<AstroCookieSetOptions, 'expires' | 'maxAge' | 'encode'>;
|
||||
|
||||
interface AstroCookieInterface {
|
||||
value: string;
|
||||
|
@ -78,17 +72,22 @@ class AstroCookies implements AstroCookiesInterface {
|
|||
* @param options Options related to this deletion, such as the path of the cookie.
|
||||
*/
|
||||
delete(key: string, options?: AstroCookieDeleteOptions): void {
|
||||
/**
|
||||
* The `@ts-expect-error` is necessary because `maxAge` and `expires` properties
|
||||
* must not appear in the AstroCookieDeleteOptions type.
|
||||
*/
|
||||
const {
|
||||
// @ts-expect-error
|
||||
maxAge: _ignoredMaxAge,
|
||||
// @ts-expect-error
|
||||
expires: _ignoredExpires,
|
||||
...sanitizedOptions
|
||||
} = options || {};
|
||||
const serializeOptions: CookieSerializeOptions = {
|
||||
expires: DELETED_EXPIRATION,
|
||||
...sanitizedOptions,
|
||||
};
|
||||
|
||||
if (options?.domain) {
|
||||
serializeOptions.domain = options.domain;
|
||||
}
|
||||
if (options?.path) {
|
||||
serializeOptions.path = options.path;
|
||||
}
|
||||
|
||||
// Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT
|
||||
this.#ensureOutgoingMap().set(key, [
|
||||
DELETED_VALUE,
|
||||
|
|
|
@ -47,26 +47,57 @@ describe('astro/src/core/cookies', () => {
|
|||
assert.equal(cookies.has('foo'), false);
|
||||
});
|
||||
|
||||
it('can provide a path', () => {
|
||||
it('deletes a cookie with attributes', () => {
|
||||
let req = new Request('http://example.com/');
|
||||
let cookies = new AstroCookies(req);
|
||||
|
||||
cookies.delete('foo', {
|
||||
domain: 'example.com',
|
||||
path: '/subpath/',
|
||||
});
|
||||
let headers = Array.from(cookies.headers());
|
||||
assert.equal(headers.length, 1);
|
||||
assert.equal(/Path=\/subpath\//.test(headers[0]), true);
|
||||
priority: 'high',
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
sameSite: 'strict',
|
||||
});
|
||||
|
||||
it('can provide a domain', () => {
|
||||
let req = new Request('http://example.com/');
|
||||
let cookies = new AstroCookies(req);
|
||||
cookies.delete('foo', {
|
||||
domain: '.example.com',
|
||||
});
|
||||
let headers = Array.from(cookies.headers());
|
||||
assert.equal(headers.length, 1);
|
||||
assert.equal(/Domain=\.example\.com/.test(headers[0]), true);
|
||||
assert.equal(/foo=deleted/.test(headers[0]), true);
|
||||
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
|
||||
assert.equal(/Domain=example.com/.test(headers[0]), true);
|
||||
assert.equal(/Path=\/subpath\//.test(headers[0]), true);
|
||||
assert.equal(/Priority=High/.test(headers[0]), true);
|
||||
assert.equal(/Secure/.test(headers[0]), true);
|
||||
assert.equal(/HttpOnly/.test(headers[0]), true);
|
||||
assert.equal(/SameSite=Strict/.test(headers[0]), true);
|
||||
});
|
||||
|
||||
it('ignores expires option', () => {
|
||||
let req = new Request('http://example.com/');
|
||||
let cookies = new AstroCookies(req);
|
||||
|
||||
cookies.delete('foo', {
|
||||
expires: new Date(),
|
||||
});
|
||||
|
||||
let headers = Array.from(cookies.headers());
|
||||
assert.equal(headers.length, 1);
|
||||
assert.equal(/foo=deleted/.test(headers[0]), true);
|
||||
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
|
||||
});
|
||||
|
||||
it('ignores maxAge option', () => {
|
||||
let req = new Request('http://example.com/');
|
||||
let cookies = new AstroCookies(req);
|
||||
|
||||
cookies.delete('foo', {
|
||||
maxAge: 60,
|
||||
});
|
||||
|
||||
let headers = Array.from(cookies.headers());
|
||||
assert.equal(headers.length, 1);
|
||||
assert.equal(/foo=deleted/.test(headers[0]), true);
|
||||
assert.equal(/Expires=Thu, 01 Jan 1970 00:00:00 GMT/.test(headers[0]), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue