Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/smooth-papayas-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/shared': patch
---

Enahncing publishable key parsing and validation logic to validate expected format
Comment thread
jacekradko marked this conversation as resolved.
Outdated
85 changes: 68 additions & 17 deletions packages/shared/src/__tests__/keys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {

describe('buildPublishableKey(frontendApi)', () => {
const cases = [
['example.clerk.accounts.dev', 'pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk'],
['fake-clerk-test.clerk.accounts.dev', 'pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ='],
['foo-bar-13.clerk.accounts.dev', 'pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk'],
['clerk.boring.sawfly-91.lcl.dev', 'pk_test_Y2xlcmsuYm9yaW5nLnNhd2ZseS05MS5sY2wuZGV2JA=='],
['clerk.boring.sawfly-91.lclclerk.com', 'pk_test_Y2xlcmsuYm9yaW5nLnNhd2ZseS05MS5sY2xjbGVyay5jb20k'],
Expand All @@ -34,12 +34,8 @@ describe('parsePublishableKey(key)', () => {
['', null],
['whatever', null],
[
'pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk',
{ instanceType: 'production', frontendApi: 'example.clerk.accounts.dev' },
],
[
'pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk',
{ instanceType: 'development', frontendApi: 'foo-bar-13.clerk.accounts.dev' },
'pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=',
{ instanceType: 'production', frontendApi: 'fake-clerk-test.clerk.accounts.dev' },
],
[
'pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk',
Expand All @@ -54,6 +50,30 @@ describe('parsePublishableKey(key)', () => {
expect(result).toEqual(expectedPublishableKey);
});

it('returns null for keys with extra characters after $', () => {
expect(parsePublishableKey('pk_live_ZmFrZS1jbGVyay1tYWxmb3JtZWQuY2xlcmsuYWNjb3VudHMuZGV2JGV4dHJh')).toBeNull();
});

it('throws an error for keys with extra characters after $ when fatal: true', () => {
expect(() =>
parsePublishableKey('pk_live_ZmFrZS1jbGVyay1tYWxmb3JtZWQuY2xlcmsuYWNjb3VudHMuZGV2JGV4dHJh', { fatal: true }),
).toThrowError('Publishable key not valid.');
});

it('returns null for keys with multiple $ characters', () => {
expect(parsePublishableKey('pk_live_ZmFrZS1jbGVyay1tdWx0aXBsZS5jbGVyay5hY2NvdW50cy5kZXYkJA==')).toBeNull();
});

it('returns null for keys without proper domain format', () => {
expect(parsePublishableKey('pk_live_aW52YWxpZGtleSQ=')).toBeNull();
});

it('throws an error if the key cannot be decoded when fatal: true', () => {
expect(() => parsePublishableKey('pk_live_invalid!@#$', { fatal: true })).toThrowError(
'Publishable key not valid.',
);
});

it('throws an error if the key is not a valid publishable key, when fatal: true', () => {
expect(() => parsePublishableKey('fake_pk', { fatal: true })).toThrowError('Publishable key not valid.');
});
Expand All @@ -65,15 +85,22 @@ describe('parsePublishableKey(key)', () => {
});

it('applies the proxyUrl if provided', () => {
expect(parsePublishableKey('pk_live_Y2xlcmsuY2xlcmsuZGV2JA==', { proxyUrl: 'example.com/__clerk' })).toEqual({
expect(
parsePublishableKey('pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', {
proxyUrl: 'example.com/__clerk',
}),
).toEqual({
frontendApi: 'example.com/__clerk',
instanceType: 'production',
});
});

it('applies the domain if provided for production keys and isSatellite is true', () => {
expect(
parsePublishableKey('pk_live_Y2xlcmsuY2xlcmsuZGV2JA==', { domain: 'example.com', isSatellite: true }),
parsePublishableKey('pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', {
domain: 'example.com',
isSatellite: true,
}),
).toEqual({
frontendApi: 'clerk.example.com',
instanceType: 'production',
Expand All @@ -82,9 +109,12 @@ describe('parsePublishableKey(key)', () => {

it('ignores domain for production keys when isSatellite is false', () => {
expect(
parsePublishableKey('pk_live_Y2xlcmsuY2xlcmsuZGV2JA==', { domain: 'example.com', isSatellite: false }),
parsePublishableKey('pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', {
domain: 'example.com',
isSatellite: false,
}),
).toEqual({
frontendApi: 'clerk.clerk.dev',
frontendApi: 'fake-clerk-test.clerk.accounts.dev',
instanceType: 'production',
});
});
Expand All @@ -101,12 +131,33 @@ describe('parsePublishableKey(key)', () => {

describe('isPublishableKey(key)', () => {
it('returns true if the key is a valid publishable key', () => {
expect(isPublishableKey('pk_live_Y2xlcmsuY2xlcmsuZGV2JA==')).toBe(true);
expect(isPublishableKey('pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=')).toBe(true);
expect(isPublishableKey('pk_test_Y2xlcmsuY2xlcmsuZGV2JA==')).toBe(true);
});

it('returns false if the key is not a valid publishable key', () => {
expect(isPublishableKey('clerk.clerk.com')).toBe(false);
});

it('returns false if the key has invalid structure', () => {
expect(isPublishableKey('pk_live')).toBe(false);
expect(isPublishableKey('pk_live_')).toBe(false);
expect(isPublishableKey('pk_live_invalid')).toBe(false);
});

it('returns false if the decoded key has extra characters after $', () => {
expect(isPublishableKey('pk_live_ZmFrZS1jbGVyay1tYWxmb3JtZWQuY2xlcmsuYWNjb3VudHMuZGV2JGV4dHJh')).toBe(false);
expect(isPublishableKey('pk_test_Y2xlcmsuY2xlcmsuZGV2JGV4dHJh')).toBe(false);
});

it('returns false if the decoded key has multiple $ characters', () => {
expect(isPublishableKey('pk_live_ZmFrZS1jbGVyay1tdWx0aXBsZS5jbGVyay5hY2NvdW50cy5kZXYkJA==')).toBe(false);
expect(isPublishableKey('pk_live_JGZha2UtY2xlcmstcHJlZml4LmNsZXJrLmFjY291bnRzLmRldiQ=')).toBe(false);
});

it('returns false if the decoded key does not look like a domain', () => {
expect(isPublishableKey('pk_live_aW52YWxpZGtleSQ=')).toBe(false);
});
});

describe('isDevOrStagingUrl(url)', () => {
Expand Down Expand Up @@ -138,9 +189,9 @@ describe('isDevOrStagingUrl(url)', () => {

describe('isDevelopmentFromPublishableKey(key)', () => {
const cases: Array<[string, boolean]> = [
['pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', false],
['pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', false],
['pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', true],
['live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', false],
['live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', false],
['test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', true],
];

Expand All @@ -152,9 +203,9 @@ describe('isDevelopmentFromPublishableKey(key)', () => {

describe('isProductionFromPublishableKey(key)', () => {
const cases: Array<[string, boolean]> = [
['pk_live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', true],
['pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', true],
['pk_test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', false],
['live_ZXhhbXBsZS5jbGVyay5hY2NvdW50cy5kZXYk', true],
['live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', true],
['test_Zm9vLWJhci0xMy5jbGVyay5hY2NvdW50cy5kZXYk', false],
];

Expand Down Expand Up @@ -194,7 +245,7 @@ describe('isProductionFromSecretKey(key)', () => {

describe('getCookieSuffix(publishableKey, subtle?)', () => {
const cases: Array<[string, string]> = [
['pk_live_Y2xlcmsuY2xlcmsuZGV2JA', '1Z8AzTQD'],
['pk_live_ZmFrZS1jbGVyay10ZXN0LmNsZXJrLmFjY291bnRzLmRldiQ=', 'qReyu04C'],
['pk_test_Y2xlcmsuY2xlcmsuZGV2JA', 'QvfNY2dr'],
];

Expand Down
Loading
Loading