-
-
Notifications
You must be signed in to change notification settings - Fork 232
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Add
ssr
package to configure cookies for browser or server…
… clients (#630) * move client and server primitives to new ssr package * remove unused isSecureEnvironment function * Update with default cookie options * Fix types for cookie options * remove type narrowing for cookie options * Move cookie method types to the types file * Update cookie storage for browser client * Update cookie properties for browser client * Move cookie and ramda to dependencies --------- Co-authored-by: Andrew Smith <[email protected]>
- Loading branch information
1 parent
59b8970
commit 49f4043
Showing
16 changed files
with
378 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@supabase/ssr': patch | ||
--- | ||
|
||
The successor to the auth-helpers packages with sane defaults |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @supabase/ssr (BETA) | ||
|
||
This submodule provides convenience helpers for implementing user authentication in your favorite framework. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "@supabase/ssr", | ||
"version": "0.0.0", | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"lint": "tsc", | ||
"build": "tsup" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/supabase/auth-helpers.git" | ||
}, | ||
"keywords": [ | ||
"Supabase", | ||
"Auth", | ||
"Next.js", | ||
"Svelte Kit", | ||
"Remix", | ||
"Express" | ||
], | ||
"author": "Supabase", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/supabase/auth-helpers/issues" | ||
}, | ||
"homepage": "https://github.com/supabase/auth-helpers#readme", | ||
"dependencies": { | ||
"cookie": "^0.5.0", | ||
"ramda": "^0.29.0" | ||
}, | ||
"devDependencies": { | ||
"@supabase/supabase-js": "2.33.1", | ||
"@types/cookie": "^0.5.1", | ||
"@types/ramda": "^0.29.3", | ||
"tsconfig": "workspace:*", | ||
"tsup": "^6.7.0" | ||
}, | ||
"peerDependencies": { | ||
"@supabase/supabase-js": "^2.33.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// these variables are defined by tsup | ||
declare const PACKAGE_NAME: string; | ||
declare const PACKAGE_VERSION: string; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { createClient } from '@supabase/supabase-js'; | ||
import { mergeDeepRight } from 'ramda'; | ||
import { DEFAULT_COOKIE_OPTIONS, isBrowser } from './utils'; | ||
import { parse, serialize } from 'cookie'; | ||
|
||
import type { SupabaseClient } from '@supabase/supabase-js'; | ||
import type { | ||
GenericSchema, | ||
SupabaseClientOptions | ||
} from '@supabase/supabase-js/dist/module/lib/types'; | ||
import type { BrowserCookieMethods, CookieOptionsWithName } from './types'; | ||
|
||
let cachedBrowserClient: SupabaseClient<any, string> | undefined; | ||
|
||
export function createBrowserClient< | ||
Database = any, | ||
SchemaName extends string & keyof Database = 'public' extends keyof Database | ||
? 'public' | ||
: string & keyof Database, | ||
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema | ||
? Database[SchemaName] | ||
: any | ||
>( | ||
supabaseUrl: string, | ||
supabaseKey: string, | ||
options?: SupabaseClientOptions<SchemaName> & { | ||
cookies: BrowserCookieMethods; | ||
cookieOptions?: CookieOptionsWithName; | ||
isSingleton?: boolean; | ||
} | ||
) { | ||
if (!supabaseUrl || !supabaseKey) { | ||
throw new Error( | ||
`Your project's URL and Key are required to create a Supabase client!\n\nCheck your Supabase project's API settings to find these values\n\nhttps://supabase.com/dashboard/project/_/settings/api` | ||
); | ||
} | ||
|
||
let cookies: BrowserCookieMethods = {}; | ||
let isSingleton = true; | ||
let cookieOptions: CookieOptionsWithName | undefined; | ||
let userDefinedClientOptions; | ||
|
||
if (options) { | ||
({ cookies, isSingleton = true, cookieOptions, ...userDefinedClientOptions } = options); | ||
} | ||
|
||
const cookieClientOptions = { | ||
global: { | ||
headers: { | ||
'X-Client-Info': `${PACKAGE_NAME}@${PACKAGE_VERSION}` | ||
} | ||
}, | ||
auth: { | ||
flowType: 'pkce', | ||
autoRefreshToken: isBrowser(), | ||
detectSessionInUrl: isBrowser(), | ||
persistSession: true, | ||
storage: { | ||
getItem: async (key: string) => { | ||
if (typeof cookies.get === 'function') { | ||
return (await cookies.get(key)) ?? null; | ||
} | ||
|
||
if (isBrowser()) { | ||
const cookie = parse(document.cookie); | ||
return cookie[key]; | ||
} | ||
}, | ||
setItem: async (key: string, value: string) => { | ||
if (typeof cookies.set === 'function') { | ||
return await cookies.set(key, value, { | ||
...DEFAULT_COOKIE_OPTIONS, | ||
...cookieOptions | ||
}); | ||
} | ||
|
||
if (isBrowser()) { | ||
document.cookie = serialize(key, value, { | ||
...DEFAULT_COOKIE_OPTIONS, | ||
...cookieOptions | ||
}); | ||
} | ||
}, | ||
removeItem: async (key: string) => { | ||
if (typeof cookies.remove === 'function') { | ||
return await cookies.remove(key, { | ||
...DEFAULT_COOKIE_OPTIONS, | ||
maxAge: 0, | ||
...cookieOptions | ||
}); | ||
} | ||
|
||
if (isBrowser()) { | ||
document.cookie = serialize(key, '', { | ||
...DEFAULT_COOKIE_OPTIONS, | ||
maxAge: 0, | ||
...cookieOptions | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
// Overwrites default client config with any user defined options | ||
const clientOptions = mergeDeepRight( | ||
cookieClientOptions, | ||
userDefinedClientOptions | ||
) as SupabaseClientOptions<SchemaName>; | ||
|
||
if (isSingleton) { | ||
// The `Singleton` pattern is the default to simplify the instantiation | ||
// of a Supabase client in the browser - there must only be one | ||
|
||
const browser = isBrowser(); | ||
|
||
if (browser && cachedBrowserClient) { | ||
return cachedBrowserClient as SupabaseClient<Database, SchemaName, Schema>; | ||
} | ||
|
||
const client = createClient<Database, SchemaName, Schema>( | ||
supabaseUrl, | ||
supabaseKey, | ||
clientOptions | ||
); | ||
|
||
if (browser) { | ||
// The client should only be cached in the browser | ||
cachedBrowserClient = client; | ||
} | ||
|
||
return client; | ||
} | ||
|
||
// This allows for multiple Supabase clients, which may be required when using | ||
// multiple schemas. The user will be responsible for ensuring a single | ||
// instance of Supabase is used for each schema in the browser. | ||
return createClient<Database, SchemaName, Schema>(supabaseUrl, supabaseKey, clientOptions); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { createClient } from '@supabase/supabase-js'; | ||
import { mergeDeepRight } from 'ramda'; | ||
import { DEFAULT_COOKIE_OPTIONS, isBrowser } from './utils'; | ||
|
||
import type { | ||
GenericSchema, | ||
SupabaseClientOptions | ||
} from '@supabase/supabase-js/dist/module/lib/types'; | ||
import type { CookieOptionsWithName, ServerCookieMethods } from './types'; | ||
|
||
export function createServerClient< | ||
Database = any, | ||
SchemaName extends string & keyof Database = 'public' extends keyof Database | ||
? 'public' | ||
: string & keyof Database, | ||
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema | ||
? Database[SchemaName] | ||
: any | ||
>( | ||
supabaseUrl: string, | ||
supabaseKey: string, | ||
options: SupabaseClientOptions<SchemaName> & { | ||
cookies: ServerCookieMethods; | ||
cookieOptions?: CookieOptionsWithName; | ||
} | ||
) { | ||
if (!supabaseUrl || !supabaseKey) { | ||
throw new Error( | ||
`Your project's URL and Key are required to create a Supabase client!\n\nCheck your Supabase project's API settings to find these values\n\nhttps://supabase.com/dashboard/project/_/settings/api` | ||
); | ||
} | ||
|
||
const { cookies, cookieOptions, ...userDefinedClientOptions } = options; | ||
|
||
if (!cookies.get || !cookies.set || !cookies.remove) { | ||
// todo: point to helpful docs in error message, once they have been written! 😏 | ||
throw new Error( | ||
'The Supabase client requires functions to get, set, and remove cookies in your specific framework!' | ||
); | ||
} | ||
|
||
const cookieClientOptions = { | ||
global: { | ||
headers: { | ||
'X-Client-Info': `${PACKAGE_NAME}@${PACKAGE_VERSION}` | ||
} | ||
}, | ||
auth: { | ||
flowType: 'pkce', | ||
autoRefreshToken: isBrowser(), | ||
detectSessionInUrl: isBrowser(), | ||
persistSession: true, | ||
storage: { | ||
getItem: async (key: string) => (await cookies.get(key)) ?? null, | ||
setItem: async (key: string, value: string) => | ||
await cookies.set(key, value, { ...DEFAULT_COOKIE_OPTIONS, ...cookieOptions }), | ||
removeItem: async (key: string) => | ||
await cookies.remove(key, { ...DEFAULT_COOKIE_OPTIONS, maxAge: 0, ...cookieOptions }) | ||
} | ||
} | ||
}; | ||
|
||
// Overwrites default client config with any user defined options | ||
const clientOptions = mergeDeepRight( | ||
cookieClientOptions, | ||
userDefinedClientOptions | ||
) as SupabaseClientOptions<SchemaName>; | ||
|
||
return createClient<Database, SchemaName, Schema>(supabaseUrl, supabaseKey, clientOptions); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from './createBrowserClient'; | ||
export * from './createServerClient'; | ||
export * from './types'; | ||
export * from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { CookieSerializeOptions } from 'cookie'; | ||
|
||
export type CookieOptions = Partial<CookieSerializeOptions>; | ||
export type CookieOptionsWithName = { name?: string } & CookieOptions; | ||
export type ServerCookieMethods = { | ||
get: (key: string) => Promise<string | null | undefined> | string | null | undefined; | ||
set: (key: string, value: string, options?: CookieOptions) => Promise<void> | void; | ||
remove: (key: string, options?: CookieOptions) => Promise<void> | void; | ||
}; | ||
export type BrowserCookieMethods = { | ||
get?: (key: string) => Promise<string | null | undefined> | string | null | undefined; | ||
set?: (key: string, value: string, options?: CookieOptions) => Promise<void> | void; | ||
remove?: (key: string, options?: CookieOptions) => Promise<void> | void; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { CookieOptions } from '../types'; | ||
|
||
export const DEFAULT_COOKIE_OPTIONS: CookieOptions = { | ||
path: '/', | ||
maxAge: 60 * 60 * 24 * 365 * 1000, | ||
httpOnly: false | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { parse, serialize } from 'cookie'; | ||
|
||
export function isBrowser() { | ||
return typeof window !== 'undefined' && typeof window.document !== 'undefined'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './helpers'; | ||
export * from './constants'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"extends": "tsconfig/base.json", | ||
"include": ["src"], | ||
"exclude": ["node_modules"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { Options } from 'tsup'; | ||
import pkg from './package.json'; | ||
|
||
export const tsup: Options = { | ||
dts: true, | ||
entryPoints: ['src/index.ts'], | ||
external: ['react', 'next', /^@supabase\//], | ||
format: ['cjs', 'esm'], | ||
// inject: ['src/react-shim.js'], | ||
// ! .cjs/.mjs doesn't work with Angular's webpack4 config by default! | ||
legacyOutput: false, | ||
sourcemap: true, | ||
splitting: false, | ||
bundle: true, | ||
clean: true, | ||
define: { | ||
PACKAGE_NAME: JSON.stringify(pkg.name), | ||
PACKAGE_VERSION: JSON.stringify(pkg.version) | ||
} | ||
}; |
Oops, something went wrong.