Have a way to set session in server-side setup
Feature request
Is your feature request related to a problem? Please describe.
As this library states "An isomorphic JavaScript client for Supabase." I would expect it to have a way how I can set session for supabase.auth part in the server-side setup. There is already client-side functionality like that which relies on URL hash: getSessionFromUrl](https://github.com/supabase/gotrue-js/blob/edcd2ffde6dfe81d02700a52a79e635a5ab8839d/src/GoTrueClient.ts#L404).
Describe the solution you'd like
I started using supabase with Remix and I have session information saved in http-only cookie. I consider this safer way, at least for XSS-based attacks (especially because the session contains refresh_token). But currently there is no easy way to set the session when initializing the supabase client on the server-side.
Describe alternatives you've considered
I implemented my own "hacky way" for now but it's a bit fragile, considering I want to keep the dependencies up-to-date. I solved it like this:
import { Session, SupabaseClient, SupabaseClientOptions } from '@supabase/supabase-js';
import { DEFAULT_HEADERS } from '@supabase/supabase-js/dist/main/lib/constants';
import { SupabaseAuthClient } from '@supabase/supabase-js/dist/main/lib/SupabaseAuthClient';
import invariant from 'tiny-invariant';
const supabaseUrl = process.env.PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.PUBLIC_SUPABASE_ANON_KEY;
class ServerSideSupabaseAuthClient extends SupabaseAuthClient {
setCurrentSession(currentSession: Session) {
this.currentSession = currentSession;
this.currentUser = currentSession.user;
}
}
const DEFAULT_OPTIONS = {
schema: 'public',
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
multiTab: true,
headers: DEFAULT_HEADERS,
};
export class ServerSideSupabaseClient extends SupabaseClient {
constructor(protected supabaseUrl: string, protected supabaseKey: string, options?: SupabaseClientOptions) {
super(supabaseUrl, supabaseKey, options);
const settings = { ...DEFAULT_OPTIONS, ...options };
this.auth = this._initSupabaseAuthClientOverride(settings);
}
setSession(session: Session) {
(this.auth as ServerSideSupabaseAuthClient).setCurrentSession(session);
}
private _initSupabaseAuthClientOverride({
autoRefreshToken,
persistSession,
detectSessionInUrl,
localStorage,
headers,
fetch,
}: SupabaseClientOptions) {
const authHeaders = {
Authorization: `Bearer ${this.supabaseKey}`,
apikey: `${this.supabaseKey}`,
};
return new ServerSideSupabaseAuthClient({
url: this.authUrl,
headers: { ...headers, ...authHeaders },
autoRefreshToken,
persistSession,
detectSessionInUrl,
localStorage,
fetch,
});
}
}
export function createSupabaseClient() {
invariant(typeof supabaseUrl === 'string', 'PUBLIC_SUPABASE_URL env var has to be configured');
invariant(typeof supabaseAnonKey === 'string', 'PUBLIC_SUPABASE_ANON_KEY env var has to be configured');
return new ServerSideSupabaseClient(supabaseUrl, supabaseAnonKey, {
autoRefreshToken: true,
persistSession: false,
detectSessionInUrl: false,
});
}
export function isSupabaseSession(value: any): value is Session {
return (
value != null &&
(value as Session).user !== undefined &&
(value as Session).user !== null &&
typeof (value as Session).token_type === 'string' &&
typeof (value as Session).access_token === 'string' &&
typeof (value as Session).refresh_token === 'string' &&
typeof (value as Session).expires_at === 'number'
);
}
With this I can easily set the session:
const supabase = createSupabaseClient();
...
if (sessionData) {
invariant(isSupabaseSession(sessionData), 'expected a valid Supabase session');
supabase.setSession(sessionData as unknown as SupabaseSession);
}
Additional context
I know there is setAuth but that only works with authToken, while I think I could keep all the info in supabase.auth.session.
First I checked https://github.com/mitchelvanbever/remix-auth-supabase/ but I didn't like it because it seems to be using (SUPABASE_SERVICE_KEY)[https://github.com/mitchelvanbever/remix-auth-supabase/blob/main/examples/email-password/.env.example].
I also checked https://github.com/one-aalam/remix-starter-kit but I would prefer to keep all the logic on server-side (this starter-kit has combination of both server/client-side logic, as far as I can see).
Similar issue here, did you work it out?
Please review our latest docs on server side rendering.
Re-open the issue or continue the discussion if you have further questions.