fix: allow custom cookie tld via env (COOKIE_TLDS)
This commit is contained in:
@@ -175,6 +175,27 @@ COOKIE_SECRET=your-random-secret-here
|
|||||||
Never use the default value in production! Always generate a unique secret.
|
Never use the default value in production! Always generate a unique secret.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
|
### COOKIE_TLDS
|
||||||
|
|
||||||
|
**Type**: `string` (comma-separated)
|
||||||
|
**Required**: No
|
||||||
|
**Default**: None
|
||||||
|
|
||||||
|
Custom multi-part TLDs for cookie domain handling. Use this when deploying on domains with public suffixes that aren't recognized by default (e.g., `.my.id`, `.web.id`, `.co.id`).
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```bash
|
||||||
|
# For domains like abc.my.id
|
||||||
|
COOKIE_TLDS=my.id
|
||||||
|
|
||||||
|
# Multiple TLDs
|
||||||
|
COOKIE_TLDS=my.id,web.id,co.id
|
||||||
|
```
|
||||||
|
|
||||||
|
<Callout>
|
||||||
|
This is required when using domain suffixes that are public suffixes (like `.co.uk`). Without this, the browser will reject authentication cookies. Common examples include Indonesian domains (`.my.id`, `.web.id`, `.co.id`).
|
||||||
|
</Callout>
|
||||||
|
|
||||||
### DEMO_USER_ID
|
### DEMO_USER_ID
|
||||||
|
|
||||||
**Type**: `string`
|
**Type**: `string`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { parseCookieDomain } from './parse-cookie-domain';
|
import { parseCookieDomain } from './parse-cookie-domain';
|
||||||
|
|
||||||
describe('parseCookieDomain', () => {
|
describe('parseCookieDomain', () => {
|
||||||
@@ -399,4 +399,100 @@ describe('parseCookieDomain', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('custom multi-part TLDs via COOKIE_TLDS', () => {
|
||||||
|
const originalEnv = process.env.COOKIE_TLDS;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset the environment variable before each test
|
||||||
|
delete process.env.COOKIE_TLDS;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Restore original value
|
||||||
|
if (originalEnv !== undefined) {
|
||||||
|
process.env.COOKIE_TLDS = originalEnv;
|
||||||
|
} else {
|
||||||
|
delete process.env.COOKIE_TLDS;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle my.id domains when COOKIE_TLDS includes my.id', () => {
|
||||||
|
process.env.COOKIE_TLDS = 'my.id';
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.abc.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle subdomains of my.id domains correctly', () => {
|
||||||
|
process.env.COOKIE_TLDS = 'my.id';
|
||||||
|
expect(parseCookieDomain('https://api.abc.my.id')).toEqual({
|
||||||
|
domain: '.abc.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple custom TLDs', () => {
|
||||||
|
process.env.COOKIE_TLDS = 'my.id,web.id,co.id';
|
||||||
|
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.abc.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(parseCookieDomain('https://abc.web.id')).toEqual({
|
||||||
|
domain: '.abc.web.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(parseCookieDomain('https://abc.co.id')).toEqual({
|
||||||
|
domain: '.abc.co.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle custom TLDs with extra whitespace', () => {
|
||||||
|
process.env.COOKIE_TLDS = ' my.id , web.id ';
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.abc.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle case-insensitive custom TLDs', () => {
|
||||||
|
process.env.COOKIE_TLDS = 'MY.ID';
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.abc.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not affect domains when env variable is empty', () => {
|
||||||
|
process.env.COOKIE_TLDS = '';
|
||||||
|
// Without the custom TLD, my.id is treated as a regular TLD
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not affect domains when env variable is not set', () => {
|
||||||
|
delete process.env.COOKIE_TLDS;
|
||||||
|
// Without the custom TLD, my.id is treated as a regular TLD
|
||||||
|
expect(parseCookieDomain('https://abc.my.id')).toEqual({
|
||||||
|
domain: '.my.id',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should still work with built-in multi-part TLDs when custom TLDs are set', () => {
|
||||||
|
process.env.COOKIE_TLDS = 'my.id';
|
||||||
|
// Built-in TLDs should still work
|
||||||
|
expect(parseCookieDomain('https://example.co.uk')).toEqual({
|
||||||
|
domain: '.example.co.uk',
|
||||||
|
secure: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,26 @@ const MULTI_PART_TLDS = [
|
|||||||
/go\.\w{2}$/,
|
/go\.\w{2}$/,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function getCustomMultiPartTLDs(): string[] {
|
||||||
|
const envValue = process.env.COOKIE_TLDS || '';
|
||||||
|
if (!envValue.trim()) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return envValue
|
||||||
|
.split(',')
|
||||||
|
.map((tld) => tld.trim().toLowerCase())
|
||||||
|
.filter((tld) => tld.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMultiPartTLD(potentialTLD: string): boolean {
|
||||||
|
if (MULTI_PART_TLDS.some((pattern) => pattern.test(potentialTLD))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const customTLDs = getCustomMultiPartTLDs();
|
||||||
|
return customTLDs.includes(potentialTLD.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
export const parseCookieDomain = (url: string) => {
|
export const parseCookieDomain = (url: string) => {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return {
|
return {
|
||||||
@@ -36,7 +56,7 @@ export const parseCookieDomain = (url: string) => {
|
|||||||
// Handle multi-part TLDs like co.uk, com.au, etc.
|
// Handle multi-part TLDs like co.uk, com.au, etc.
|
||||||
if (parts.length >= 3) {
|
if (parts.length >= 3) {
|
||||||
const potentialTLD = parts.slice(-2).join('.');
|
const potentialTLD = parts.slice(-2).join('.');
|
||||||
if (MULTI_PART_TLDS.some((tld) => tld.test(potentialTLD))) {
|
if (isMultiPartTLD(potentialTLD)) {
|
||||||
// For domains like example.co.uk or subdomain.example.co.uk
|
// For domains like example.co.uk or subdomain.example.co.uk
|
||||||
// Use the last 3 parts: .example.co.uk
|
// Use the last 3 parts: .example.co.uk
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user