-
-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathcreateServerClient.ts
More file actions
198 lines (188 loc) · 6.87 KB
/
createServerClient.ts
File metadata and controls
198 lines (188 loc) · 6.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import {
AuthChangeEvent,
createClient,
SupabaseClient,
SupabaseClientOptions,
} from "@supabase/supabase-js";
import { VERSION } from "./version";
import { createStorageFromOptions, applyServerStorage } from "./cookies";
import type {
CookieOptionsWithName,
CookieMethodsServer,
CookieMethodsServerDeprecated,
} from "./types";
import { memoryLocalStorageAdapter } from "./utils/helpers";
import { warnIfUsingDeprecatedAuthHelpersPackage } from "./warnDeprecatedPackage";
/**
* @deprecated Please specify `getAll` and `setAll` cookie methods instead of
* the `get`, `set` and `remove`. These will not be supported in the next major
* version.
*/
export function createServerClient<
Database = any,
SchemaName extends string & keyof Omit<Database, "__InternalSupabase"> =
"public" extends keyof Omit<Database, "__InternalSupabase">
? "public"
: string & keyof Omit<Database, "__InternalSupabase">,
>(
supabaseUrl: string,
supabaseKey: string,
options: SupabaseClientOptions<SchemaName> & {
cookieOptions?: CookieOptionsWithName;
cookies: CookieMethodsServerDeprecated;
cookieEncoding?: "raw" | "base64url";
},
): SupabaseClient<Database, SchemaName>;
/**
* Creates a Supabase Client for use on the server-side of a server-side
* rendering (SSR) framework.
*
* **Use in middlewares.**
*
* Middlewares are functions that run before any rendering logic and can
* inspect and modify both the incoming request and the outgoing response. In
* most SSR frameworks you *must set up a middleware* and call this function
* in it. The `cookies` option must implement both `getAll` **and** `setAll`
* so that token refreshes can be written back to the response. The deprecated
* `get`, `set`, and `remove` methods are not recommended — they miss
* important edge cases and will be removed in a future major version.
*
* **IMPORTANT:** Failing to implement `getAll` and `setAll` correctly **will
* cause significant and difficult to debug authentication issues**: random
* logouts, early session termination, JSON parsing errors, increased refresh
* token requests, or relying on garbage state.
*
* **Use in pages, routes or components.**
*
* *Always* create a new client with this function for each server render —
* never share a client across requests. Not all frameworks allow setting
* cookies or response headers from pages, routes or components — in those
* cases `setAll` can be omitted, but configure it if you can.
*
* **IMPORTANT:** If cookies cannot be set from pages or components,
* middleware *must* handle session updates — omitting it will cause
* significant and difficult to debug authentication issues.
*
* If `setAll` is not configured, the client emits a warning when it needs to
* write cookies. This usually means one of:
*
* - A middleware client was not configured.
* - There is a bug in your middleware.
* - You are calling `supabase.auth.updateUser()` server-side.
*
* Please consult the [Supabase SSR guides](https://supabase.com/docs/guides/auth/server-side)
* for your framework.
*
* **Session initialization.**
*
* This client uses lazy session initialization (`skipAutoInitialize: true`).
* The session is not loaded until the first call to `getSession()` or
* `getUser()`. Token refreshes write the updated session back to cookies via
* the `setAll` handler.
*
* @param supabaseUrl The URL of the Supabase project.
* @param supabaseKey The `anon` API key of the Supabase project.
* @param options Various configuration options.
*/
export function createServerClient<
Database = any,
SchemaName extends string & keyof Omit<Database, "__InternalSupabase"> =
"public" extends keyof Omit<Database, "__InternalSupabase">
? "public"
: string & keyof Omit<Database, "__InternalSupabase">,
>(
supabaseUrl: string,
supabaseKey: string,
options: SupabaseClientOptions<SchemaName> & {
cookieOptions?: CookieOptionsWithName;
cookies: CookieMethodsServer;
cookieEncoding?: "raw" | "base64url";
},
): SupabaseClient<Database, SchemaName>;
export function createServerClient<
Database = any,
SchemaName extends string & keyof Omit<Database, "__InternalSupabase"> =
"public" extends keyof Omit<Database, "__InternalSupabase">
? "public"
: string & keyof Omit<Database, "__InternalSupabase">,
>(
supabaseUrl: string,
supabaseKey: string,
options: SupabaseClientOptions<SchemaName> & {
cookieOptions?: CookieOptionsWithName;
cookies: CookieMethodsServer | CookieMethodsServerDeprecated;
cookieEncoding?: "raw" | "base64url";
},
): SupabaseClient<Database, SchemaName> {
warnIfUsingDeprecatedAuthHelpersPackage();
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 { storage, getAll, setAll, setItems, removedItems } =
createStorageFromOptions(
{
...options,
cookieEncoding: options?.cookieEncoding ?? "base64url",
},
true,
);
const client = createClient<Database, SchemaName>(supabaseUrl, supabaseKey, {
// TODO: resolve type error
...(options as any),
global: {
...options?.global,
headers: {
...options?.global?.headers,
"X-Client-Info": `supabase-ssr/${VERSION} createServerClient`,
},
},
auth: {
...(options?.cookieOptions?.name
? { storageKey: options.cookieOptions.name }
: null),
...options?.auth,
flowType: "pkce",
autoRefreshToken: false,
detectSessionInUrl: false,
persistSession: true,
skipAutoInitialize: true,
storage,
...(options?.cookies &&
"encode" in options.cookies &&
options.cookies.encode === "tokens-only"
? {
userStorage:
options?.auth?.userStorage ?? memoryLocalStorageAdapter(),
}
: null),
},
});
client.auth.onAuthStateChange(async (event: AuthChangeEvent) => {
// The SIGNED_IN event is fired very often, but we don't need to
// apply the storage each time it fires, only if there are changes
// that need to be set -- which is if setItems / removeItems have
// data.
const hasStorageChanges =
Object.keys(setItems).length > 0 || Object.keys(removedItems).length > 0;
if (
hasStorageChanges &&
(event === "SIGNED_IN" ||
event === "TOKEN_REFRESHED" ||
event === "USER_UPDATED" ||
event === "PASSWORD_RECOVERY" ||
event === "SIGNED_OUT" ||
event === "MFA_CHALLENGE_VERIFIED")
) {
await applyServerStorage(
{ getAll, setAll, setItems, removedItems },
{
cookieOptions: options?.cookieOptions ?? null,
cookieEncoding: options?.cookieEncoding ?? "base64url",
},
);
}
});
return client;
}