vercel / nextjs-subscription-payments

Clone, deploy, and fully customize a SaaS subscription application with Next.js.
https://subscription-payments.vercel.app/
MIT License
5.79k stars 1.19k forks source link

Next.js 14 & Supabase SSR #278

Closed dalkommatt closed 4 months ago

dalkommatt commented 7 months ago

Closes #115 #190 #200 #222 #244 #254 #262 #271 #248 #223 #274

This PR updates all packages and switches from the now deprecated auth helpers to Supabase SSR.

vercel[bot] commented 7 months ago

@dalkommatt is attempting to deploy a commit to the Vercel Solutions Team on Vercel.

A member of the Team first needs to authorize it.

dalkommatt commented 7 months ago

You've implemented a bunch of different bug fixes and features and opinionated refactorings in a single PR. Some great stuff here, but I would love to see this PR broken down a bit more for easier review.

It can definitely be broken down if needed. A lot of stuff is from existing PRs (including yours) just to close them out. I tried not to get too opinionated, rather just to deliver on originally intended features. The server actions for updating the name and email were already present, for example, just disabled. The main issue is that there wasn't a great way of providing success and error state messages. In the future I would advocate dropping the auth UI package and switching to server-side auth with toast success/error states to make the project more consistent.

chriscarrollsmith commented 7 months ago

I spent most of the day reviewing your first couple commits and sorting them into three different branches, in some cases with slight changes. (Let me know how or if you want me to handle opening PRs from these branches, because I want you to get credit for your excellent work.)

The supabase-auth stuff looks good, though I haven't comprehensively tested it yet. Required some reading of docs, and I learned some things. I added a few more comments than you, but otherwise my branch is in line with your code. Your ui-refactor also looks good to me, although I've placed Card.tsx into a Card subfolder and exported it from an index.ts file to match the rest of the ui components.

The stripe-checkout stuff I'm unsure about. When I try to change my API version in stripe.ts to match yours, I get a type error. The type is apparently set by my install of the latest version of the stripe library in node_modules. It's possible that the installation process is somehow pulling my account's API version from my Stripe dashboard. There probably needs to be some documentation in the README about how to make sure the API version specified in 'stripe.ts' matches the API version of one's Stripe account and the API version permitted by the stripe module's type definitions.

I also adjusted some of what you changed in the create-checkout route. I put the cancel_url back in, so that Stripe checkout will provide a 'back' button for the user to cancel checkout if they want. And instead of dropping all support for free trials, I used 'trial_period_days: price.trial_period_days,' which should dynamically allow for both trialing and non-trialing subscription, as controlled from the Stripe dashboard. (The Stripe docs say setting trial_end or trial_period_days is preferred over setting trial_from_plan, though I can't see why, as the latter seems more secure to me at a glance.)

I haven't looked at your toast or password recovery code yet at all, but I am excited to dive into that and also do some live testing tomorrow, because those are features I've been wanting to add myself, and you are going to save me a lot of time!

dalkommatt commented 7 months ago

I spent most of the day reviewing your first couple commits and sorting them into three different branches, in some cases with slight changes. (Let me know how or if you want me to handle opening PRs from these branches, because I want you to get credit for your excellent work.)

Thank you, yeah maybe let's see if Thor or Jon wants to take a look at the current PR first. My last PR was also quite big but did end up getting merged with some revisions. In the meantime are you able to suggest the changes here?

The supabase-auth stuff looks good, though I haven't comprehensively tested it yet. Required some reading of docs, and I learned some things. I added a few more comments than you, but otherwise my branch is in line with your code. Your ui-refactor also looks good to me, although I've placed Card.tsx into a Card subfolder and exported it from an index.ts file to match the rest of the ui components.

Not sure that I changed much as far as auth goes, it's still using supabase-provider.tsx with Auth UI. I think the main thing is that I switched sign out to a server action. Matching Card with the other components is a good change!

The stripe-checkout stuff I'm unsure about. When I try to change my API version in stripe.ts to match yours, I get a type error. The type is apparently set by my install of the latest version of the stripe library in node_modules. It's possible that the installation process is somehow pulling my account's API version from my Stripe dashboard. There probably needs to be some documentation in the README about how to make sure the API version specified in 'stripe.ts' matches the API version of one's Stripe account and the API version permitted by the stripe module's type definitions.

Come to think of it I may have updated my Stripe version from the Stripe dashboard at some point. The reason I changed the version in stripe.ts is because I was getting a type error Type '"2022-11-15"' is not assignable to type '"2023-10-16"'.ts(2322) Do you not get that?

I also adjusted some of what you changed in the create-checkout route. I put the cancel_url back in, so that Stripe checkout will provide a 'back' button for the user to cancel checkout if they want. And instead of dropping all support for free trials, I used 'trial_period_days: price.trial_period_days,' which should dynamically allow for both trialing and non-trialing subscription, as controlled from the Stripe dashboard. (The Stripe docs say setting trial_end or trial_period_days is preferred over setting trial_from_plan, though I can't see why, as the latter seems more secure to me at a glance.)

Dropped cancel_url is my mistake, definitely should be corrected. Nice idea on the trial period too.

I haven't looked at your toast or password recovery code yet at all, but I am excited to dive into that and also do some live testing tomorrow, because those are features I've been wanting to add myself, and you are going to save me a lot of time!

Yeah I think you'll find a useEffect in the toaster listening for URL param changes is quite an elegant solution for handling notifications globally with all the components going server side. Password recovery is not working like expected currently so be aware. I was hoping someone from Supabase could take a look at it. We're not supposed to @ people, right 😅

chriscarrollsmith commented 7 months ago

I'll see if I can help you debug the password recovery. Re: Stripe, I got exactly the opposite type error.

On Wed, Nov 29, 2023, 12:25 AM Matt @.***> wrote:

I spent most of the day reviewing your first couple commits and sorting them into three different branches, in some cases with slight changes. (Let me know how or if you want me to handle opening PRs from these branches, because I want you to get credit for your excellent work.)

Thank you, yeah maybe let's see if Thor or Jon wants to take a look at the current PR first. My last PR was also quite big but did end up getting merged with some revisions. In the meantime are you able to suggest the changes here?

The supabase-auth stuff looks good, though I haven't comprehensively tested it yet. Required some reading of docs, and I learned some things. I added a few more comments than you, but otherwise my branch is in line with your code. Your ui-refactor also looks good to me, although I've placed Card.tsx into a Card subfolder and exported it from an index.ts file to match the rest of the ui components.

Not sure that I changed much as far as auth goes, it's still using supabase-provider.tsx with Auth UI. I think the main thing is that I switched sign out to a server action. Matching Card with the other components is a good change!

The stripe-checkout stuff I'm unsure about. When I try to change my API version in stripe.ts to match yours, I get a type error. The type is apparently set by my install of the latest version of the stripe library in node_modules. It's possible that the installation process is somehow pulling my account's API version from my Stripe dashboard. There probably needs to be some documentation in the README about how to make sure the API version specified in 'stripe.ts' matches the API version of one's Stripe account and the API version permitted by the stripe module's type definitions.

Come to think of it I may have updated my Stripe version from the Stripe dashboard at some point. The reason I changed the version in stripe.ts is because I was getting a type error Type '"2022-11-15"' is not assignable to type '"2023-10-16"'.ts(2322) Do you not get that?

I also adjusted some of what you changed in the create-checkout route. I put the cancel_url back in, so that Stripe checkout will provide a 'back' button for the user to cancel checkout if they want. And instead of dropping all support for free trials, I used 'trial_period_days: price.trial_period_days,' which should dynamically allow for both trialing and non-trialing subscription, as controlled from the Stripe dashboard. (The Stripe docs say setting trial_end or trial_period_days is preferred over setting trial_from_plan, though I can't see why, as the latter seems more secure to me at a glance.)

Dropped cancel_url is my mistake, definitely should be corrected. Nice idea on the trial period too.

I haven't looked at your toast or password recovery code yet at all, but I am excited to dive into that and also do some live testing tomorrow, because those are features I've been wanting to add myself, and you are going to save me a lot of time!

Yeah I think you'll find a useEffect in the toaster listening for URL param changes is quite an elegant solution for handling notifications globally with all the components going server side. Password recovery is not working like expected currently so be aware. I was hoping someone from Supabase could take a look at it. We're not supposed to @ people, right 😅

— Reply to this email directly, view it on GitHub https://github.com/vercel/nextjs-subscription-payments/pull/278#issuecomment-1831244006, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASCYPGN5TJYXYGYJ4LRICTTYG3BLRAVCNFSM6AAAAAA75KVAIOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMZRGI2DIMBQGY . You are receiving this because you commented.Message ID: @.***>

leerob commented 7 months ago

I'm fine with one large PR 👍 Thank you for opening this!

vercel[bot] commented 7 months ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nextjs-subscription-payments ❌ Failed (Inspect) Nov 29, 2023 9:45pm
chriscarrollsmith commented 7 months ago

A few thoughts on the toasts.

1) They are pretty bulky, with several new module files, several new dependencies, and a couple new configs. That's not necessarily bad, but it's maybe not ideal for a starter template. I will defer to the Vercel folks on this one.

2) I like the look of the toasts, but if you close one by clicking the "x", the toast's ol container is "focused" and gets a pink border, which remains visible in the lower-right-hand corner of the page when the toast is closed. To fix this, change *:focus: in main.css to *:focus:not(ol).

3) Line 104 in 'app/account/page.tsx' should have status_description, not error_description. On the same page, I encountered at least one unhandled error condition. Instead of making custom error messages for every error condition, I suggest just passing error.message as the error_description.

For reference, my lightly edited 'toasts' feature branch is here: https://github.com/chriscarrollsmith/nextjs-subscription-payments/tree/toasts.

I'll review the password-reset feature next, and we'll see if we can get that up and running.

chriscarrollsmith commented 7 months ago

Regarding the Stripe type error, both the Stripe docs and the code editor hints claim that 'stripe-js' only allows the latest API version, but their versioning page claims that older versions of 'stripe-js' set the type based on your account's API version. Since I've made sure I'm on the latest version of 'stripe-js' and it's still only letting me use '2022-11-15', I have to conclude that the docs are wrong, and it's still the user's account version that's being exported as the StripeConfig type.

(Edit to add: Aha! I figured out that my 'stripe-js' was on the latest version, but my 'stripe' was on an older version. Running 'pnpm update --latest' rather than just 'pnpm update' was the key. Now my type matches yours and wants the latest API version.)

Interestingly, the documentation for setting a global apiVersion says, 'If you wish to remain on your account's default API version, you may pass null or another version instead of the latest version, and add a @ts-ignore comment here and anywhere the types differ between API versions.' For purposes of making this template user-friendly and preventing additional setup steps, maybe a null value and a @ts-ignore comment would indeed be the way to go.

chriscarrollsmith commented 7 months ago

I think what I'm going to do, once I finish my review, is open a pull request to your 'dalkommatt:nextjs-14-supabase-ssr' branch with my suggested adjustments. That way you don't have to make all these changes manually. Thanks again for all your hard work on this. It's impressive stuff.

dalkommatt commented 7 months ago

I think what I'm going to do, once I finish my review, is open a pull request to your 'dalkommatt:nextjs-14-supabase-ssr' branch with my suggested adjustments. That way you don't have to make all these changes manually. Thanks again for all your hard work on this. It's impressive stuff.

Sounds like a plan, I'll look forward to it!

chriscarrollsmith commented 7 months ago

@dijonmusters as the project is currently set up, the listener doesn't seem to be detecting any auth change events. I tried logging them, and got nada. However, the AuthUI component is using the context exported from supabase-provider.tsx, and I so far haven't been able to get it to work without it. (I admit I'm a total neophyte with both NextJS and Supabase, so I don't really know what I'm doing.)

chriscarrollsmith commented 7 months ago

Regarding password recovery:

The AuthUI package apparently just doesn't fully work as intended. It is also grossly under-documented, and the guy who runs it seems to reject sensible PRs. However, here's what I've been able to figure out.

First, many people have created issues indicating that the PASSWORD_RECOVERY event doesn't fire. I don't know exactly why not, but it seems like this feature isn't working. And even if it did work, I'm not sure it would be compatible with our current application flow, which redirects the user to 'auth/callback' for authentication by magic link.

Second, magic links work by taking the user to the Supabase server, which then redirects them to 'auth/callback' with an API request that contains a code we can exchange for a session authorizing the user. The magic link contains 'type=recovery', but this information unfortunately doesn't get passed along in the request, so there's no way to tell in our 'auth/callback' endpoint whether the user has come via a password recovery or just a regular magic link.

Third, the redirect destination (currently 'auth/callback') is controlled from the redirectTo prop in the Auth component. Unfortunately, this prop controls the redirect destination for both password recovery and magic links, and there aren't separate props for setting separate destinations for each.

However, it appears that there is a workaround. This isn't documented anywhere, but the AuthUI maintainer dropped enough hints that I was able to figure it out. It looks like you can set showLinks to false, which will remove the 'sign up' and 'forgot your password' links under the Auth component. Then you can add your own 'sign up' and 'forgot your password' links, and use them to manually toggle the Auth component's 'view' prop (to control whether it is a 'sign_in', 'sign_up', 'forgotten_password', 'update_password', or 'magic_link' view) as well as change its 'redirect_to' prop to set different redirect destinations for different views. And we can add a new route, in addition to 'auth/callback,' that redirects the user to an update password page.

It's late and I need to get to bed, but tomorrow I will take a crack at implementing this workaround.

dijonmusters commented 7 months ago

Regarding password recovery:

The AuthUI package apparently just doesn't fully work as intended. It is also grossly under-documented, and the guy who runs it seems to reject sensible PRs. However, here's what I've been able to figure out.

First, many people have created issues indicating that the PASSWORD_RECOVERY event doesn't fire. I don't know exactly why not, but it seems like this feature isn't working. And even if it did work, I'm not sure it would be compatible with our current application flow, which redirects the user to 'auth/callback' for authentication by magic link.

Second, magic links work by taking the user to the Supabase server, which then redirects them to 'auth/callback' with an API request that contains a code we can exchange for a session authorizing the user. The magic link contains 'type=recovery', but this information unfortunately doesn't get passed along in the request, so there's no way to tell in our 'auth/callback' endpoint whether the user has come via a password recovery or just a regular magic link.

Third, the redirect destination (currently 'auth/callback') is controlled from the redirectTo prop in the Auth component. Unfortunately, this prop controls the redirect destination for both password recovery and magic links, and there aren't separate props for setting separate destinations for each.

However, it appears that there is a workaround. This isn't documented anywhere, but the AuthUI maintainer dropped enough hints that I was able to figure it out. It looks like you can set showLinks to false, which will remove the 'sign up' and 'forgot your password' links under the Auth component. Then you can add your own 'sign up' and 'forgot your password' links, and use them to manually toggle the Auth component's 'view' prop (to control whether it is a 'sign_in', 'sign_up', 'forgotten_password', 'update_password', or 'magic_link' view) as well as change its 'redirect_to' prop to set different redirect destinations for different views. And we can add a new route, in addition to 'auth/callback,' that redirects the user to an update password page.

It's late and I need to get to bed, but tomorrow I will take a crack at implementing this workaround.

Auth UI doesn't play very nicely with SSR. It might be better to remove this as a dependency entirely and manually recreate a stable version of the functionality we actually want - rather than hacky workarounds to make it kind of work. Even if we stripped it back to just using a single OAuth provider - such as GitHub. This would mean we wouldn't need to have nicely styled form inputs with handling multiple states or implement resetting password flows. Boiling it down, this template is solving the problem of getting up and running with a SaaS project as quickly as possible. To solve that problem, we need a way to authenticate. But we don't need several ways to authenticate.

What are your thoughts?

chriscarrollsmith commented 7 months ago

I am inclined to use the hacky workaround, mostly because building a custom UI sounds like a lot of work, and I'd like this repo to demonstrate how to do authentication comorehensively, not just OAuth. (Also, Supabase OAuth is still a bit buggy.) In the absence of good documentation, a lot of frustrated developers may also benefit from having this repo as an example of how to use AuthUI. But I agree with you that it sucks to have to mix our SSR workflow throughout the rest of the application with client-side context in this one case, to support this one dumb dependency.

On Thu, Nov 30, 2023, 11:39 PM Jon Meyers @.***> wrote:

Regarding password recovery:

The AuthUI package apparently just doesn't fully work as intended. It is also grossly under-documented, and the guy who runs it seems to reject sensible PRs. However, here's what I've been able to figure out.

First, many people have created issues indicating that the PASSWORD_RECOVERY event doesn't fire. I don't know exactly why not, but it seems like this feature isn't working. And even if it did work, I'm not sure it would be compatible with our current application flow, which redirects the user to 'auth/callback' for authentication by magic link.

Second, magic links work by taking the user to the Supabase server, which then redirects them to 'auth/callback' with an API request that contains a code we can exchange for a session authorizing the user. The magic link contains 'type=recovery', but this information unfortunately doesn't get passed along in the request, so there's no way to tell in our 'auth/callback' endpoint whether the user has come via a password recovery or just a regular magic link.

Third, the redirect destination (currently 'auth/callback') is controlled from the redirectTo prop in the Auth component. Unfortunately, this prop controls the redirect destination for both password recovery and magic links, and there aren't separate props for setting separate destinations for each.

However, it appears that there is a workaround. This isn't documented anywhere, but the AuthUI maintainer dropped enough hints that I was able to figure it out. It looks like you can set showLinks to false, which will remove the 'sign up' and 'forgot your password' links under the Auth component. Then you can add your own 'sign up' and 'forgot your password' links, and use them to manually toggle the Auth component's 'view' prop (to control whether it is a 'sign_in', 'sign_up', 'forgotten_password', 'update_password', or 'magic_link' view) as well as change its 'redirect_to' prop to set different redirect destinations for different views. And we can add a new route, in addition to 'auth/callback,' that redirects the user to an update password page.

It's late and I need to get to bed, but tomorrow I will take a crack at implementing this workaround.

Auth UI doesn't play very nicely with SSR. It might be better to remove this as a dependency entirely and manually recreate a stable version of the functionality we actually want - rather than hacky workarounds to make it kind of work. Even if we stripped it back to just using a single OAuth provider - such as GitHub. This would mean we wouldn't need to have nicely styled form inputs with handling multiple states or implement resetting password flows. Boiling it down, this template is solving the problem of getting up and running with a SaaS project as quickly as possible. To solve that problem, we need a way to authenticate. But we don't need several ways to authenticate.

What are your thoughts?

— Reply to this email directly, view it on GitHub https://github.com/vercel/nextjs-subscription-payments/pull/278#issuecomment-1835447022, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASCYPGLZOMTJ5NPD4KIM2UTYHFNQTAVCNFSM6AAAAAA75KVAIOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMZVGQ2DOMBSGI . You are receiving this because you commented.Message ID: @.***>

dalkommatt commented 7 months ago

Auth UI doesn't play very nicely with SSR. It might be better to remove this as a dependency entirely and manually recreate a stable version of the functionality we actually want - rather than hacky workarounds to make it kind of work. Even if we stripped it back to just using a single OAuth provider - such as GitHub. This would mean we wouldn't need to have nicely styled form inputs with handling multiple states or implement resetting password flows. Boiling it down, this template is solving the problem of getting up and running with a SaaS project as quickly as possible. To solve that problem, we need a way to authenticate. But we don't need several ways to authenticate.

What are your thoughts?

Personally I'm all for going passwordless. How about one OAuth provider and a magic link option? That way you can still sign in with just email.

dijonmusters commented 7 months ago

I recommend these changes to:

dijonmusters commented 7 months ago

Also, Supabase OAuth is still a bit buggy

Do you have an example of this? There were some issues with cookie chunking when we launched the @supabase/ssr package, but to my knowledge it should be working correctly now 👍

In the absence of good documentation, a lot of frustrated developers may also benefit from having this repo as an example of how to use AuthUI

I think it is more like Auth UI is not entirely compatible with SSR and I'm not sure it ever will be. There are some hacky workarounds to get things mostly working, but the package would require significant work to make it properly compatible. Best to avoid using it in this template until it is fully compatible 👍

dalkommatt commented 7 months ago

@dijonmusters see what you think of the magic link login. If not I can always roll back 😉

chriscarrollsmith commented 7 months ago

Also, Supabase OAuth is still a bit buggy

Do you have an example of this? There were some issues with cookie chunking when we launched the @supabase/ssr package, but to my knowledge it should be working correctly now 👍

I keep seeing open Supabase issues related to OAuth failing if the user tries more than one OAuth provider that uses the same email address. Apparently Supabase hasn't yet solved how to link them. I haven't tested OAuth much myself, so this is all secondhand.

chriscarrollsmith commented 7 months ago

Took me a while to fully grok how this project works:

https://github.com/ElectricCodeGuy/SupabaseAuthWithSSR/

But that's just because I wasn't familiar enough with the NextJS page router, so I wasn't aware of the significance of a folder enclosed in square brackets. Learn something every day.

Basically he has separate api routes that handle the different types of authentication, and the password reset route redirects back to the signup page with an "id" param in the url that controls how the auth form is rendered. He built the whole thing with MaterialUI, so would be a bit of work to reproduce without that dependency. Since it seems like we're going passwordless for the template, I'm mostly putting this here for my own future reference.

chriscarrollsmith commented 7 months ago

@dalkommatt I see you made some changes to types_db.ts in your last commit. Were those changes auto-generated from the Supabase CLI, or did you make them manually?

I ask because that file is meant to be auto-generated from the database via the Supabase CLI. The database, in turn, is meant to be auto-generated from schema.sql. So make sure you support that workflow, that you don't make any manual changes to types_db.ts, and that any changes to types_db.ts are matched by corresponding changes in schema.sql.

dalkommatt commented 7 months ago

@dalkommatt I see you made some changes to types_db.ts in your last commit. Were those changes auto-generated from the Supabase CLI, or did you make them manually?

I ask because that file is meant to be auto-generated from the database via the Supabase CLI. The database, in turn, is meant to be auto-generated from schema.sql. So make sure you support that workflow, that you don't make any manual changes to types_db.ts, and that any changes to types_db.ts are matched by corresponding changes in schema.sql.

@chriscarrollsmith https://github.com/supabase/supabase-js/pull/918

chriscarrollsmith commented 7 months ago

Okay, great! I opened a PR to @dalkommatt's branch that incorporates the changes I suggested above in my code review, and also adds comprehensive support for server-side password login, including signup and password recovery.

There are probably still improvements that could be made the the UI, and several of the API routes could undoubtedly be consolidated. We could also add a simple boolean prop to toggle between password or passwordless login flow. But I feel comfortable saying that the template now contains all the fundamental building blocks for any developer to easily build on the Vercel/Supabase/Stripe stack with secure server-side auth flow.

Thanks to both @dalkommatt and @dijonmusters for the incredible work on this. I learned a ton about server-side rendering from working on this with you both.

chriscarrollsmith commented 7 months ago

Turns out I was making this much more complicated than it needs to be. Coming at it fresh this morning, I managed to greatly simplify and remove the API endpoints. All the magic now happens faster and more smoothly in server action helper functions.

chriscarrollsmith commented 7 months ago

I also added simple boolean switches to utils/auth-helpers.ts in my PR to control allowed sign-in methods, so you can go passwordless, or not, with the switch of a toggle.

rayne1066 commented 7 months ago

I want to just say, I really appreciate all of the time and effort that everyone has put into this project. I am just starting my own Full Stack Dev journey. I am learning so much, and it is wonderful to see such an active and selfless community!

chriscarrollsmith commented 7 months ago

@rayne1066 graciously did some testing and identified some bugs in URL handling, trial period handling, and Stripe checkout creation which I have fixed in my open PR. I've also ironed out bugs that were breaking magic link signup and login in certain edge cases.

I've also been adding error handling. The APIs for creating a Stripe checkout session and customer portal now catch all errors and return appropriate toast redirects. There are still some more unhandled errors being thrown in admin.ts that I want to catch and log to the console in the Stripe webhook endpoint. Will work on that tomorrow.

ivan20203 commented 7 months ago

I am inclined to use the hacky workaround, mostly because building a custom UI sounds like a lot of work, and I'd like this repo to demonstrate how to do authentication comorehensively, not just OAuth. (Also, Supabase OAuth is still a bit buggy.) In the absence of good documentation, a lot of frustrated developers may also benefit from having this repo as an example of how to use AuthUI. But I agree with you that it sucks to have to mix our SSR workflow throughout the rest of the application with client-side context in this one case, to support this one dumb dependency. On Thu, Nov 30, 2023, 11:39 PM Jon Meyers @.> wrote: Regarding password recovery: The AuthUI package apparently just doesn't fully work as intended. It is also grossly under-documented, and the guy who runs it seems to reject sensible PRs. However, here's what I've been able to figure out. First, many people have created issues indicating that the PASSWORD_RECOVERY event doesn't fire. I don't know exactly why not, but it seems like this feature isn't working. And even if it did work, I'm not sure it would be compatible with our current application flow, which redirects the user to 'auth/callback' for authentication by magic link. Second, magic links work by taking the user to the Supabase server, which then redirects them to 'auth/callback' with an API request that contains a code we can exchange for a session authorizing the user. The magic link contains 'type=recovery', but this information unfortunately doesn't get passed along in the request, so there's no way to tell in our 'auth/callback' endpoint whether the user has come via a password recovery or just a regular magic link. Third, the redirect destination (currently 'auth/callback') is controlled from the redirectTo prop in the Auth component. Unfortunately, this prop controls the redirect destination for both password recovery and magic links, and there aren't separate props for setting separate destinations for each. However, it appears that there is a workaround. This isn't documented anywhere, but the AuthUI maintainer dropped enough hints that I was able to figure it out. It looks like you can set showLinks to false, which will remove the 'sign up' and 'forgot your password' links under the Auth component. Then you can add your own 'sign up' and 'forgot your password' links, and use them to manually toggle the Auth component's 'view' prop (to control whether it is a 'sign_in', 'sign_up', 'forgotten_password', 'update_password', or 'magic_link' view) as well as change its 'redirect_to' prop to set different redirect destinations for different views. And we can add a new route, in addition to 'auth/callback,' that redirects the user to an update password page. It's late and I need to get to bed, but tomorrow I will take a crack at implementing this workaround. Auth UI doesn't play very nicely with SSR. It might be better to remove this as a dependency entirely and manually recreate a stable version of the functionality we actually want - rather than hacky workarounds to make it kind of work. Even if we stripped it back to just using a single OAuth provider - such as GitHub. This would mean we wouldn't need to have nicely styled form inputs with handling multiple states or implement resetting password flows. Boiling it down, this template is solving the problem of getting up and running with a SaaS project as quickly as possible. To solve that problem, we need a way to authenticate. But we don't need several ways to authenticate. What are your thoughts? — Reply to this email directly, view it on GitHub <#278 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASCYPGLZOMTJ5NPD4KIM2UTYHFNQTAVCNFSM6AAAAAA75KVAIOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMZVGQ2DOMBSGI . You are receiving this because you commented.Message ID: @.>

Can't we just have forgot password just be like send magic link. Is it possible to have the authui only send the magic link to confirmed emails.

And then in the user account profile allow them to change their password if they signed up with password.

chriscarrollsmith commented 7 months ago

Can't we just have forgot password just be like send magic link. Is it possible to have the authui only send the magic link to confirmed emails.

And then in the user account profile allow them to change their password if they signed up with password.

Not to worry; we already dropped the AuthUI dependency and fully rebuilt a working password login system, including password reset, from the ground up. You can pull the working version from here: https://github.com/chriscarrollsmith/nextjs-subscription-payments/tree/pr278.

Speaking of which, I've now completed all of my to-do items:

Next steps:

@dalkommatt to review/accept my PR @leerob or other Vercel team member to authorize/merge the consolidated PR

chriscarrollsmith commented 6 months ago

I have encountered what may be a limitation of server-side auth flow, or may just be a problem with how we implemented it in this PR: we are continuously making many requests to the Supabase auth API, such that it's possible to exceed rate limits with only a single user on the app! I haven't yet gone deep on this, but it seems like some combination of caching and passing down the tree might be needed to reduce API load. I need to experiment with adding console logs to see exactly where all the calls are being triggered from.

dijonmusters commented 6 months ago

I have encountered what may be a limitation of server-side auth flow, or may just be a problem with how we implemented it in this PR: we are continuously making many requests to the Supabase auth API, such that it's possible to exceed rate limits with only a single user on the app! I haven't yet gone deep on this, but it seems like some combination of caching and passing down the tree might be needed to reduce API load. I need to experiment with adding console logs to see exactly where all the calls are being triggered from.

Is this still happening if you pull down the recent changes that exports a matcher from middleware?

leerob commented 6 months ago

Love the teamwork here 😄 Looking forward to seeing this get merged.

chriscarrollsmith commented 6 months ago

I have encountered what may be a limitation of server-side auth flow, or may just be a problem with how we implemented it in this PR: we are continuously making many requests to the Supabase auth API, such that it's possible to exceed rate limits with only a single user on the app! I haven't yet gone deep on this, but it seems like some combination of caching and passing down the tree might be needed to reduce API load. I need to experiment with adding console logs to see exactly where all the calls are being triggered from.

Is this still happening if you pull down the recent changes that exports a matcher from middleware?

I had already pulled those changes. I will do some work this week to see what's going on.

PascalEugster commented 5 months ago

Nice one! When will we see it live?

dijonmusters commented 5 months ago

This looks great! Awesome work everyone! 🎉

I think it is ready to merge, so long as @chriscarrollsmith is no longer getting that API Auth Error with a matcher exported from middleware.ts?

nonamecodi commented 5 months ago

@dijonmusters @dalkommatt where is supabase.auth.onAuthStateChange listener? When user signs out from one browser window it's basically not redirecting from another windows. Should not we have supabase.auth.onAuthStateChange listener in middleware to log out user from other browser tabs/window.

gooodsoil commented 5 months ago

I agree with @nonamecodi, this question tags on to that one

dijonmusters commented 5 months ago

@dijonmusters @dalkommatt where is supabase.auth.onAuthStateChange listener? When user signs out from one browser window it's basically not redirecting from another windows. Should not we have supabase.auth.onAuthStateChange listener in middleware to log out user from other browser tabs/window.

onAuthStateChange is no longer required for the auth flow as we are doing everything server-side. We are explicitly redirecting after the user signs in in /app/auth/callback/route.ts and after the user signs out in /components/ui/Navbar/Navbar.tsx.

Previously the auth listener was required as it was the only way to know when the authentication process had completed. That is not the case with the code exchange route in the PKCE flow used by the auth-helpers and ssr packages 👍

tmanager2025 commented 5 months ago

Thanks for explanation @dijonmusters. As @nonamecodi said, but what will happen when

I thought onAuthStateChange handle this scenario. I verified the PR but above scenario does not work.

Side Note: @dijonmusters Thanks you for making great videos I learned a lot from them.

nonamecodi commented 5 months ago

@dijonmusters @dalkommatt where is supabase.auth.onAuthStateChange listener? When user signs out from one browser window it's basically not redirecting from another windows. Should not we have supabase.auth.onAuthStateChange listener in middleware to log out user from other browser tabs/window.

onAuthStateChange is no longer required for the auth flow as we are doing everything server-side. We are explicitly redirecting after the user signs in in /app/auth/callback/route.ts and after the user signs out in /components/ui/Navbar/Navbar.tsx.

Previously the auth listener was required as it was the only way to know when the authentication process had completed. That is not the case with the code exchange route in the PKCE flow used by the auth-helpers and ssr packages 👍

Have same question as @tmanager2025. How to log out users from all other logged in tabs, when they signed out from one tab. Should not we listen to onAuthStateChange event to do this.

chriscarrollsmith commented 5 months ago

Sorry I've been AWOL for a week or two. Have been down and out with the flu, and also preoccupied with another project. Will get back to this next week.

dijonmusters commented 5 months ago

@dijonmusters @dalkommatt where is supabase.auth.onAuthStateChange listener? When user signs out from one browser window it's basically not redirecting from another windows. Should not we have supabase.auth.onAuthStateChange listener in middleware to log out user from other browser tabs/window.

onAuthStateChange is no longer required for the auth flow as we are doing everything server-side. We are explicitly redirecting after the user signs in in /app/auth/callback/route.ts and after the user signs out in /components/ui/Navbar/Navbar.tsx. Previously the auth listener was required as it was the only way to know when the authentication process had completed. That is not the case with the code exchange route in the PKCE flow used by the auth-helpers and ssr packages 👍

Have same question as @tmanager2025. How to log out users from all other logged in tabs, when they signed out from one tab. Should not we listen to onAuthStateChange event to do this.

The onAuthStateChange function should work to solve that specific use case, but not sure this problem needs to be solved for this template - every line of code included increases the complexity and maintenance burden for everyone that uses it.

It is pretty common for auth state to be checked on navigation or refresh, so if someone has logged out in a different tab, it should be expected that the app running in another tab is in a different state until they refresh or navigate somewhere that requires authentication.

I would recommend leaving this out of the template, and up to the individual building the app if this is how they want to handle their logout flow 👍

dijonmusters commented 5 months ago

This looks great! Awesome work everyone! 🎉

I think it is ready to merge, so long as @chriscarrollsmith is no longer getting that API Auth Error with a matcher exported from middleware.ts?

@chriscarrollsmith if you get a chance, could you check whether this one is resolved for you with the matcher?

myorgmanager commented 5 months ago

onAuthStateChange

@dijonmusters I think this template should have basic best practices. We removed onAuthStateChange functionality which was previously there in the template.

I am confused how to do this. I took reference of this template so many times in past.

I think onAuthStateChange needs to be added to make the basic Login/Logout scenarios work properly which is main motive of this template.

Having that code will show how to do it in real world because this template is main reference for authentication and payment.

nonamecodi commented 5 months ago

@dijonmusters @dalkommatt where is supabase.auth.onAuthStateChange listener? When user signs out from one browser window it's basically not redirecting from another windows. Should not we have supabase.auth.onAuthStateChange listener in middleware to log out user from other browser tabs/window.

onAuthStateChange is no longer required for the auth flow as we are doing everything server-side. We are explicitly redirecting after the user signs in in /app/auth/callback/route.ts and after the user signs out in /components/ui/Navbar/Navbar.tsx. Previously the auth listener was required as it was the only way to know when the authentication process had completed. That is not the case with the code exchange route in the PKCE flow used by the auth-helpers and ssr packages 👍

Have same question as @tmanager2025. How to log out users from all other logged in tabs, when they signed out from one tab. Should not we listen to onAuthStateChange event to do this.

The onAuthStateChange function should work to solve that specific use case, but not sure this problem needs to be solved for this template - every line of code included increases the complexity and maintenance burden for everyone that uses it.

It is pretty common for auth state to be checked on navigation or refresh, so if someone has logged out in a different tab, it should be expected that the app running in another tab is in a different state until they refresh or navigate somewhere that requires authentication.

I would recommend leaving this out of the template, and up to the individual building the app if this is how they want to handle their logout flow 👍

@dijonmusters I agree with @myorgmanager, this is reference for many people for Login and Subscription. Having no way to Logout users on another tabs/window is bad once they logout.

Would love to see have this feature in this PR which is removed and we lost that functionality.

It is important to show how this done with RSC and supabase.

ivan20203 commented 5 months ago

for the website i built, i set onlyThirdPartyProviders to true and implemented clear all cookies when the user signs out. The website has no users though except for me so idk.

chriscarrollsmith commented 5 months ago

for the website i built, i set onlyThirdPartyProviders to true and implemented clear all cookies when the user signs out. The website has no users though except for me so idk.

I think @dijonmusters's point is that they are logged out in the other tab, though it won't be obvious unless they refresh or navigate somewhere. I will look into how hard it would be to implement onAuthStateChange; I haven't used this before.

chriscarrollsmith commented 5 months ago

This looks great! Awesome work everyone! 🎉 I think it is ready to merge, so long as @chriscarrollsmith is no longer getting that API Auth Error with a matcher exported from middleware.ts?

@chriscarrollsmith if you get a chance, could you check whether this one is resolved for you with the matcher?

Sorry for taking a while to get back to you. When I look in my Supabase logs, I see two things. First, there is some redundancy in the API calls. Here I navigated to the front page, which triggered, apparently, two API calls:

Screenshot 2024-02-04 173607

The other thing I'm seeing here is that 401 'invalid sub claim' error. That's not triggering any kind of problem on-page or any error in my console/terminal, and it seems to only occur when the user is logged out, so maybe it's expected behavior; I don't know.

Here's another example of the redundant API burden. Logging in triggers four API requests:

Screenshot 2024-02-04 174002

I'm uncertain what's causing this, but I think it could add up and exceed rate limits pretty fast.

chriscarrollsmith commented 5 months ago

By the way, on my PR, I updated the README to document how to develop locally with Supabase.