rphlmr / supa-fly-stack Goto Github PK
View Code? Open in Web Editor NEWThe Remix Stack for deploying to Fly with Supabase, authentication, testing, linting, formatting, etc.
License: MIT License
The Remix Stack for deploying to Fly with Supabase, authentication, testing, linting, formatting, etc.
License: MIT License
I face this issue when I try to prisma migrate dev --create-only --skip-seed
.
The "fix" here works for me. Will try to update the stack asap.
Supabase updated doc to work with Prisma : https://supabase.com/docs/guides/integrations/prisma#configuring-the-project-to-use-postgresql
Charging
The error is ReferenceError: Response is not defined
.
I'd like to work on this issue.
I think that I can solve it with msw.
Just noticed CC BY-NC-SA 4.0
tacked on down the bottom of the README. A copyleft non-commercial license seems highly unusual for a template, however it's your project so absolutely at your discretion.
That said, could you please add a LICENSE
file so that Github prominently displays the license?
Originally posted by rphlmr February 17, 2023
Hello there,
I'll publish a v3 with some breaking changes on the "auth module".
I have launched a new stack with a new way to commit auth session and I want to have the same pattern on every stack I maintain :)
Nothing hard to understand but a major change: requireAuthSession
will no more magically refresh the session in loader
.
Currently:
loader
function, If the access_token expires, I refresh it and reload the loader
.action
function, because we can't reload it, you have to commit the session in every return json
.In future, you will have to commit the session in loader
and action
.
Because writing the full header is too long, I have imagined something and will provide a response
helper to handle that for you.
export async function loader({ request }: LoaderArgs) {
const authSession = await requireAuthSession(request); // maybe a refreshed authSession
const { userId } = authSession;
try {
const notes = await getNotes({ userId });
return response.ok(
{ notes },
{ authSession } // will commit it for you
);
} catch (cause) {
throw response.error(cause, { authSession });
}
}
export async function action({ request, params }: ActionArgs) {
const authSession = await requireAuthSession(request); // maybe a refreshed authSession
const { userId } = authSession;
try {
await deleteNote(params.id);
return response.ok(
{ success: true },
{ authSession } // will commit it for you
);
} catch (cause) {
return response.error(cause, { authSession });
}
}
Spoilers: authSession
will have a cookie
property that is nothing more than the result of sessionStorage.commitSession(session, { maxAge: SESSION_MAX_AGE, })
.
Then, response.ok
or response.error
will put this cookie in headers :)
You can check what is coming here: https://github.com/rphlmr/supa-stripe-stack
There's a (minor?) security issue and a few oddities I noticed with the way auth is being handled.
The action in oauth.callback.tsx
accepts a form data encoded AuthSession
from the client and trusts its contents. Consequently, you can submit a real email with a fake access token etc. and side effects will be triggered.
If an account exists for the email, the server will commit (sign) the bogus auth session data and store it in a cookie used for future requests. If a user doesn't exist for the email, one will be created and we'll also end up with bogus auth session cookie as above.
Whilst not great, this is fairly safe at present since requireAuthSession()
is being used on endpoints, and it calls through to verifyAuthSession()
, and consequently it'll catch the bad access token. That does assume APIs are calling through to verifyAuthSession()
. However, that leads into another concern, which is admittedly more of a query.
The Supabase JS library is already creating its own sb-access-token
cookie which contains all the same data. The main difference is it's created/verifiable with the Supabase instance's JWT secret key. Thus, in order to validate the token we need do one of:
verifyAuthSession()
does.I guess the advantage of our own session cookie is that we can validate it ourselves on the server without needing to grab the Supabase JWT secret and save on a round trip i.e. we can trust its contents. However, we're not doing that, we are performing round trips via requireAuthSession()
. Just as well because of the aforementioned OAuth Callback vulnerability. However, it does make the contents of the cookie redundant, since we can't trust the contents without verifying the access token, and in doing so, we're fetching the same data that's found in the cookie anyway.
Email addresses are being used as unique keys. This stack doesn't claim to support oauth beyond magic links, so it's fine. However, this approach becomes problematic when integrating social OAuth - which is what I was doing when I discovered all the above. Basically, users can change the email address associated with their OAuth provider. Say for example someone signs up via Github social auth whilst their email is [email protected]
. If they proceed to change their primary email on Github to [email protected]
, when that user returns to a site built on this stack and attempts to authenticate, no account will be found.
Again, the stack doesn't claim to support social auth, so it's by no means a bug. However, that part of the auth design could probably be shored up a bit to make it more robust/extensible.
After me ranting about problems, just wanted to say thanks for all the hard work! Despite the above, this is my first time using Remix and this repo is, at the very least, super informative.
I was having trouble getting realtime to work with RLS. I was able to get it working like this:
const getSupabaseClient = (supabaseKey: string, accessToken?: string) => {
const global = accessToken
? {
global: {
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
}
: {};
const client = createClient<Database>(SUPABASE_API_URL, supabaseKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
...global,
});
if (accessToken) {
client.realtime.accessToken = accessToken;
client.realtime.headers = {
Authorization: `Bearer ${accessToken}`,
};
}
return client;
};
Without this, the realtime claims in the realtime.subscriptions
table were equivalent to a user with "role":"anon"
Hello there,
Someone ask this and maybe it's a good idea to not scatter people with multiple ways to handle auth in Remix with Supabase.
Maybe I should rewrite auth with https://supabase.com/docs/guides/auth/auth-helpers/remix.
After that, it should be a breeze to replace Prisma with Supabase to query your database if this It what you need for your project.
I'll probably do some abstraction to keep helpers like requireAuthSession
and keep things simple to use in protected loaders/actions.
Is It something you are interested in or do you prefer to be able to modify what you want with the actual auth implementation?
Hello,
I am a little bit confused by the read.me file. Should I add prod or staging supabase secrets to github actions secret section?
When debugging in vs code, breakpoints are shown in ./build/index.js and not in the original files. Is this expected / Is there a way to get full source map support?
Hello there!
I hope this project helps you start something or learn something new.
V3 is in progress.
There will be significant and impactful changes:
I tried to use the supa-fly-stack template but it failed with the message shown below.
I'm running on Windows 10
npx --version = 10.2.5
node --version = v20.10.0
> npx create-remix --template rphlmr/supa-fly-stack
remix v2.4.0 ๐ฟ Let's build a better website...
dir Where should we create your new project?
./appname
โผ Template: Using rphlmr/supa-fly-stack...
โ Template copied
git Initialize a new git repository?
Yes
deps Install dependencies with npm?
Yes
init This template has a remix.init script. Do you want to run it?
Yes
โโโโโโ Dependencies installing with npm...
โฒ Oh no! Failed to install dependencies.
if I cd into the app-dir and run npm install I get the following output
โบ npm install
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: appname@undefined
npm ERR! Found: @remix-run/[email protected]
npm ERR! node_modules/@remix-run/react
npm ERR! @remix-run/react@"^2.4.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @remix-run/react@"^1.0.0" from [email protected]
npm ERR! node_modules/remix-i18next
npm ERR! remix-i18next@"^4.1.1" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! C:\Users\user.name\AppData\Local\npm-cache\_logs\2023-12-21T10_52_51_177Z-eresolve-report.txt
npm ERR! A complete log of this run can be found in: C:\Users\user.name\AppData\Local\npm-cache\_logs\2023-12-21T10_52_51_177Z-debug-0.log
Here's the output from npm install
npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: shelf@undefined npm ERR! Found: @remix-run/[email protected] npm ERR! node_modules/@remix-run/react npm ERR! @remix-run/react@"^2.3.0" from the root project npm ERR! npm ERR! Could not resolve dependency: npm ERR! peer @remix-run/react@"^1.0.0" from [email protected] npm ERR! node_modules/remix-i18next npm ERR! remix-i18next@"^4.1.1" from the root project npm ERR! npm ERR! Fix the upstream dependency conflict, or retry npm ERR! this command with --force or --legacy-peer-deps npm ERR! to accept an incorrect (and potentially broken) dependency resolution. npm ERR!
I guees this key was left out when changing in other places.
From:
if (!process.env.SUPABASE_SERVICE_KEY) {
throw new Error("SUPABASE_SERVICE_KEY is not set");
}
const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_KEY,
{ autoRefreshToken: false, persistSession: false }
);
To:
if (!process.env.SUPABASE_SERVICE_ROLE) {
throw new Error("SUPABASE_SERVICE_ROLE is not set");
}
const supabaseAdmin = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE,
{ autoRefreshToken: false, persistSession: false }
);
Just a heads up I updated prisma and @prisma/client to 3.12.0 and began getting a
An error occured while running the seed command:
Error: Command was killed with SIGKILL (Forced termination): ts-node --require tsconfig-paths/register prisma/seed.ts
I downgraded back to 3.11.1 and everything worked as expected.
The refreshAccessToken
function calls into supabase-js
using
supa-fly-stack/app/modules/auth/service.server.ts
Lines 60 to 63 in bd100e0
(note access_token: ""
)
However, inside supabase we hit the code path
/**
* Sets the session data from the current session. If the current session is expired, setSession will take care of refreshing it to obtain a new session.
* If the refresh token or access token in the current session is invalid, an error will be thrown.
* @param currentSession The current session that minimally contains an access token and refresh token.
*/
async setSession(currentSession: {
access_token: string
refresh_token: string
}): Promise<AuthResponse> {
try {
if (!currentSession.access_token || !currentSession.refresh_token) {
throw new AuthSessionMissingError()
}
So the code always errors (file can be found at node_modules/.pnpm/@[email protected]/node_modules/@supabase/gotrue-js/src/GoTrueClient.ts
lines 626 and following)
Will address this asap.
We are having an issue with our magic link. I have tested this both in my localhost as well as on a fly.io environment.
Some more context:
1 .Front-end works good, it sends the info
2. Email is received
3. Clicking on the email link gives a problem:
When I inspect the link that comes in the email, there is some strange stuff I notice:
redirect_to=shelf-webapp.fly.dev/
I dont understand why this is the redirect url. I have double checked and on my localhost the SERVER_URL is set to localhost:3000.
I also specifically debugged the function sendMagicLink
and the SERVER_URL
that is being sent is localhost:3000.
I am a bit lost and not sure why its not working.
Basically everything is the same like locahost except the url being wrong. The url in the link is correct, but we are still getting the same error in the browser, that no API key is found in the request.
Any help would be appreciated.
I have to test that everything works with Remix v2.
Then, this stack will no longer received updates.
A better one will replace it ;)
I'll start working on migrating this stack to supabase-js v2.
It's still a release candidate so it'll not be merged to master until it's stable ๐
I think it's possible to create a remix project from a stack's branch name, so, you'll be able to test this in advance.
Maybe some changes will happen on main to prepare for this upgrade.
I plan to :
No release date, doing my best ๐
Hi again, not sure if I'm doing anything wrong but when I run the headless cypress test, the mocking doesn't seem to work and it calls my Supabase API. I can see the users and notes being created and cleaned up in my Supabase dashboard during the tests.
And since it called the actual api, test cases were failing and I had to add this check to wait for the api finishes and route changed to make it pass.
Steps to reproduce:
npx create-remix --template rphlmr/supa-fly-stack
.env
filenpm run setup
npm run test:e2e:run
This is what I get:
Running seed command `ts-node --require tsconfig-paths/register app/database/seed.server.ts` ...
Error: Could not create or get user
at getUserId (/Users/kojo/kod/Egna/YouWish/youwish_app/app/database/seed.server.ts:35:9)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at async seed (/Users/kojo/kod/Egna/YouWish/youwish_app/app/database/seed.server.ts:39:14)
An error occurred while running the seed command:
Error: Command failed with exit code 1: ts-node --require tsconfig-paths/register app/database/seed.server.ts
ERROR: "setup:seed" exited with 1.
Any clues?
I followed every step and created the shadow db as described in supabase docs
Thank you for the awesome stack. I'm new to Prisma and Supabase and would like to understand if it's possible to use Supabase RLS with Prisma?
First off thank you for the application, I am using it as a reference for a solution I am creating.
I am running into a problem where I am getting an error saying
[1] { message: 'JWT expired', code: 'PGRST301', details: null, hint: null }
when doing queries in my application. I am confused because the call to getAuthSession
is returning a valid session but when get the supabase client
const supabaseClient = supabase(authSession?.accessToken as string);
and make a database call, it errors out
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.