Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create auth helpers for Remix #57

Closed
VictorPeralta opened this issue Mar 28, 2022 · 26 comments · Fixed by #333
Closed

Create auth helpers for Remix #57

VictorPeralta opened this issue Mar 28, 2022 · 26 comments · Fixed by #333
Assignees
Labels
enhancement New feature or request remix Remix specific functionality.

Comments

@VictorPeralta
Copy link

Like with Next.js which currently has easy to use helpers and routes, the same could be done for Remix.

I didn't find an open issue about this, although I heard it's planned, so it can be tracked here.

@VictorPeralta
Copy link
Author

If no one is working on this yet, I'd like work on it.

I have something like the next + supabase auth example for Remix here, so I feel like ai have enough context to accomplish this:

https://github.com/VictorPeralta/supabase-remix-auth

@alaister
Copy link
Member

Hi Victor,
PRs are most definitely welcome! Thanks for opening this issue to track.

One consideration we have with Remix and Supabase Auth is ensuring realtime still works on the client side. Remix mostly uses HttpOnly cookies for auth but these currently don't play well with the client side realtime. This is something we're looking to address in the future, but if you can get it working with the current constraints, that would be great!

@thorwebdev thorwebdev added the enhancement New feature or request label Mar 29, 2022
@thorwebdev
Copy link
Member

@VictorPeralta that would be awesome indeed. I know that @dijonmusters was planning to work on this once he's back from leave, but I am very certain he wouldn't mind you getting started on it 💚

@thorwebdev thorwebdev added the remix Remix specific functionality. label Mar 29, 2022
@MaxAst
Copy link

MaxAst commented Apr 4, 2022

Looks like @dijonmusters had already started working on this: 2a690e5

Might a good starting point Victor! Let me know if you need help, would be happy to contribute

@thorwebdev
Copy link
Member

Also worthwhile looking at https://github.com/rphlmr/supa-fly-stack and if there are things from there we can pull out into auth helpers cc @rphlmr

@niklasgrewe
Copy link

niklasgrewe commented Apr 20, 2022

@dijonmusters Hi, i tried the example from the branch feat/add-remix-run but i have trouble to use the withAuthRequired function. When i use it, i get the following application error:

Error: Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.

the problem is this line:

if (!user) return redirect('/login')

redirect comes from the @remix-run/node package so its not available in browser context. When i try to change withAuthRequired.js to withAuthRequired.server.js nothing changed. I get the same application error.

I also read something about the error in the remix docs: Server Code in Client Bundles but i didn't find any solution.

Does anyone have an idea how to solve this? I would like to use this wrapper because it makes things easier, but it doesn't seem to be possible...

@rphlmr
Copy link

rphlmr commented Apr 21, 2022

@niklasgrewe Hi,

It's because withAuthRequired is a high order function that creates a side effect.

Remix doesn't know what to do with that and add this code to client side bundle too.

https://remix.run/docs/en/v1/guides/constraints#higher-order-functions

You should rewrite it to use it in loader / action 😉

// withAuthRequired.server.js
export default async function withAuthRequired (request) => {
  const accessToken = await getAccessTokenFromRequest(request);
  const { user } = await supabase.auth.api.getUser(accessToken);

  if (!user) {
    throw redirect("/login");
  }

  return { accessToken, user };
};
// app/routes/channels.jsx
export const loader = async ({ request }) => {
  const { accessToken } = await withAuthRequired(request)
  supabase.auth.setAuth(accessToken);

  const { data: channels } = await supabase.from("channels").select("*");

  return json({
    channels,
  });
};

PS : return json() will be mandatory soon ;)

@niklasgrewe
Copy link

@rphlmr thank you for your quick help. Do you know when the auth helper for remix will be ready? I had already considered using remix-auth with the supabase strategy. However, I had seen that there is also a PR from you pending. Can you tell me where the differences are? Honestly don't know what is the best/easiest way to integrate supabase auth into remix. Can you recommend me something?

@rphlmr
Copy link

rphlmr commented Apr 21, 2022

@rphlmr thank you for your quick help. Do you know when the auth helper for remix will be ready? I had already considered using remix-auth with the supabase strategy. However, I had seen that there is also a PR from you pending. Can you tell me where the differences are? Honestly don't know what is the best/easiest way to integrate supabase auth into remix. Can you recommend me something?

The remix-auth-supabase is on standby 😅
With the maintainer we think to deprecate it, because we think Remix Stacks are more suitable.

I can recommend you to check "my remix stack" .
At least to have an overall idea about how it could be implemented, until supabase made an official way ;)

@niklasgrewe
Copy link

@rphlmr great thank you, I did not know that. I also think the remix stacks are more suitable. Your stack I have already looked at, but admittedly, I do not really understand everything yet 😅

What I would like to understand though is why do I need to return json({ accessToken, User }) instead of return { accessToken, User } Can you possibly explain this to me? Most importantly, I would like to use types. But when I return json, I can only specify Promise<Response> as the return type. Do you know how I can specify the types for accessToken and User anyway?

@rphlmr
Copy link

rphlmr commented Apr 21, 2022

I would like to understand though is why do I need to return json({ accessToken, User }) instead of return { accessToken, User }

@niklasgrewe
It's a Remix thing. At first, they had no opinion but to prevent future issues they advise to return json object with json.

It's an helper function equivalent to

new Response({ accessToken, User }, {
    headers: {
      "Content-Type": "application/json",
    },
  });

But when I return json, I can only specify Promise<Response> as the return type. Do you know how I can specify the types for accessToken and User anyway?

Typing a loader or action is not possible because they are just Fetch Response.
Response body (your { accessToken, user }) is just a readable stream of byte data in the end ;)
They are serialized thru the network and not directly used in your code like "normal" functions

But you can (and should) anyway use typing for useLoaderData and useActionData.

Example :

interface MyLoaderData {
  orders: {
    id: string;
    amount: number;
    ...
  };
}

export const loader = async ({ request }) => {
  const { accessToken } = await withAuthRequired(request);
  supabase.auth.setAuth(accessToken);

  const { data: orders } = await supabase.from("orders").select("*");

  return json({
    orders,
  });
};

export default function MyRoute () {
  const { orders } = useLoaderData() as MyLoaderData
  // or const { orders } = useLoaderData<MyLoaderData>()
  // its a matter of choice, Remix creator like using as ^^"

  return <div>...</div>
}

I hope it helps.
Don't worry, take your time, and play with it.
It's a new tech with lot of "old things" (HTTP standards re-implementation) coming back.

@niklasgrewe
Copy link

niklasgrewe commented Apr 21, 2022

@rphlmr thank you very much for the detailed info. However, could it be that return json({ accessToken, user }); is not currently supported? When i try this:

// withAuthRequired.server.js
export default async function withAuthRequired (request) => {
  const accessToken = await getAccessTokenFromRequest(request);
  const { user } = await supabase.auth.api.getUser(accessToken);

  if (!user) {
    throw redirect("/login");
  }

  return json({ accessToken, user }); // ❌ not working
  // return { accessToken, user } ✅ works
};
// app/routes/private.jsx
export const loader = async ({ request }) => {
  const { user } = await withAuthRequired(request)
  return json({ email: user.email })
};

i always get

Cannot read properties of undefined (reading 'email')

When i change return json({ accessToken, user }); to return { accessToken, user } it works

@rphlmr
Copy link

rphlmr commented Apr 21, 2022

@niklasgrewe I'm so sorry, I've edited my previous example => #57 (comment)

Of course, withAuthRequired have to return "classic json" and not json({}) that is a "http layer thing".
Sorry again for the confusion :(

@niklasgrewe
Copy link

Sorry again for the confusion :(

all good, no problem at all, I figured it out myself. Thanks again for your support and explanations

@413n
Copy link

413n commented May 27, 2022

Any news on Remix support?

@abhagsain
Copy link

Supabase 🤝 Remix?

image

@beppek
Copy link

beppek commented Oct 6, 2022

Chiming in to remind people that Cloudflare Pages/Workers is a supported deployment environment for Remix that doesn't support environment variables. Seems like all the stacks mainly use Fly. Hoping these helpers will keep that in mind and make sure it's compatible with Cloudflare.

@lsbyerley
Copy link

Has anyone used an oath supabase login with the remix auth helpers? I'm only seeing an email/password login example in the root https://github.com/supabase/auth-helpers/blob/4a709e24445d17b5bafae00ea06f562f5e052fcc/examples/remix/app/root.tsx

@thorwebdev
Copy link
Member

@lsbyerley I've added an OAuth example here: https://github.com/supabase/auth-helpers/pull/354/files

Thinking about changing the example to use the Auth UI. Would that be helpful?

@thorwebdev thorwebdev linked a pull request Nov 1, 2022 that will close this issue
@lsbyerley
Copy link

@lsbyerley I've added an OAuth example here: https://github.com/supabase/auth-helpers/pull/354/files

Thinking about changing the example to use the Auth UI. Would that be helpful?

Awesome, thank you for the updated oauth example! I personally don't use the Auth UI components but I'm sure someone else out there would find it useful. I think having both would be perfect.

@IHIutch
Copy link

IHIutch commented Nov 1, 2022

I'd second keeping an example without Auth UI. I don't tend to use it either, so having an example without is useful for me too.

@lsbyerley
Copy link

The new remix oauth example doesn't seem to be working for me. I see the access token in the url after auth and then the remix app does nothing with it.

@thorwebdev
Copy link
Member

@lsbyerley calling the auth methods in a server action wasn't the right approach unfortunately. I'm working on moving auth stuff to the client here: #356 . It currently uses the Auth UI but I'll also add a plain JS example.

This approach will simplify things and save you money (server function invocations).

@lsbyerley
Copy link

Thank you! I'm still not sure I understand how to perform a signInWithOAuth authentication without the Auth UI as it seems you've switched back to signInWithPassword.

The earlier example you had for oauth signin did not do anything with the accessToken after coming back to the remix

@thorwebdev
Copy link
Member

@lsbyerley by following the createBrowserClient and onAuthStatChange handler setup in the root route (https://github.com/supabase/auth-helpers/blob/main/examples/remix/app/root.tsx#L80-L96) you can now use the standard javascript methods: https://supabase.com/docs/reference/javascript/auth-signinwithoauth

<button
  onClick={() => {
    supabase?.auth.signInWithOAuth({
      provider: 'github',
      options: { redirectTo: 'http://localhost:3004' }
    });
  }}
>
  GitHub OAuth
</button>

I've added it to the example: #360

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request remix Remix specific functionality.
Projects
None yet
Development

Successfully merging a pull request may close this issue.