In this chapter, we're going to go ahead and implement social provider login for GitHub and Google. We're also going to learn how to protect our non-auth pages. Let's go ahead and check if you're running your app. So npm run dev. After that, go to localhost 3000 slash sign in so you are viewing the same forum as I am.
After that, let's go to BetterAuth. I would highly suggest that you follow the documentation alongside with me. Go inside of the documentation, authentication, and find GitHub. The reason I told you to follow alongside is So you can copy the code snippets, but also use the very useful links they have added. So in here they have prepared the redirect URL for us.
So go ahead and copy this. Click on GitHub Developer Portal. It's located in your settings, developer settings. Click new OAuth app, let's call it Meet AI. And authorization callback URL is the redirect URL.
For the homepage URL, you can just set the localhost 3000. If you are unsure if the protocol or the port are correct, you can double check here. When you run npm run dev, you can see your local URL. If it's anything different, for example, HTTPS or 3001, no problem, just modify it here and here. After you are ready go ahead and click register application.
This will allow you to copy the client ID so go to dot environment and add github client ID and prepare github secret my apologies github client secret go ahead and click generate a new client secret and copy it go ahead and paste it here and that's all you need now what we have to do is configure the provider. So let's go ahead and copy the social providers here. Now, let's go inside of source, lib, auth. And go ahead and add the social providers here. Make sure that you have added the client id and the client secret for the github social provider.
As always, what I like to do is copy from here and then paste here just to double check. So copy from here and paste here. The reason I always advise doing this is in case you misspell this, for example like this, you can't really see it immediately but this is a different variable, right? I missed the letter T, for example. So that's why you always copy from here and paste here.
Great, now let's go to sign in view and find your or continue with part, find your two buttons, Google and GitHub. And go ahead and add on click here and simply call out client sign in social and add the provider and set it to GitHub like this. So now if you go to your sign-in view here and click on GitHub regardless if you are trying to register or log in all out works the same way if the user doesn't exist it will create it if it does exist it will just log in So it does not matter that we are doing this from the sign-in page. Also, double check that you didn't accidentally register with your existing GitHub email. If you have, go ahead and remove that user from your database.
You can visit Drizzle Studio or Neon Tables. So let's click GitHub here and in a few seconds we should see the authorization screen. Let's authorize and I should now be redirected back to the home page here. If you for whatever reason were not redirected you can manually go to local host 3000 here, wait a second and you will see your GitHub username right here. Great.
Now let's go ahead and let's sign out. Let's go back to sign in here. So what I want to do now is I want to copy this here and I want to go to sign up view. Find the GitHub button here and add the onclick here. So now both from the sign up and from the sign in, GitHub should work.
What we have to do now is the Google login. Let's go ahead and click on Google here. And let's go inside of the Google Cloud Console. So before you do anything here what you have to do is create a new project. Using your nav bar you can select your current project and in here you can click new project.
I'm gonna go ahead and call this meet AI. You don't have to select any organization and go ahead and click create. And now just wait for your project to be created. It should be done in a second and click Select Project. Now you will see your project name here in the project picker.
And if you try clicking on these credentials or OAuth consent screens, you will probably at one point be redirected to this Google Auth platform. That's because I've noticed that Google is in some kind of migration phase where they are introducing this Google Auth platform. So to make this easier for you to follow, let's follow from the actual BetterAuth documentation. So let's together click on Google Cloud Console. That seems to take us to APIs and services here.
And usually what you would do is you would go and create the credentials. But in here I have this warning. Remember to configure the OAuth screen before we create the credentials. So usually you would click here, right? And as you can see, that takes me to Google Auth platform and tells me that the Google Out platform is not configured yet.
So basically you need to find a way to configure the Out platform. I just showed you one way of getting to here. And then click Get Started here. And that basically takes you to the overview. So let's call this app Meet AI.
Let's use whatever you have for the user support email and click next. For your audience, select external so that anyone can test the app. Go ahead and click next and enter your contact email address here and click next agree to the terms and service and continue and click create and this will set up the Google Alph platform right here. So what I would suggest you do now is click on branding just to see everything's okay. So the app name is Meet AI.
This is the support email and here's an advice do not add a logo. If you add a logo you will need to submit your app for verification which can take some time. So don't add any logos, you don't have to add anything here, I'm just checking that the name is correct. Now let's click through audience here and this is important. Before you create the credential click on the publish app push to production so here here it is if your app's configuration has more than 10 domains or if it has a logo or requests sensitive or restricted scopes you will need to submit for verification.
So that's why I told you to not add a logo because this way you can just push the production like this. That's it. That's all you have to do. Make sure this says external here. And now let's go inside of the clients here and now we can finally click create client in here go ahead and select a web application and go ahead and call this meet AI and now in here select add authorized redirect URIs and in here you can just copy this one.
Let's go here and let's add it here. So double check the Protocol and the port and click Create. And this will now show you your client ID and your client secret. So let's go ahead and add to Google Client ID and Google Client Secret. There we go.
Make sure you have both of them here and click OK. And now you're ready to go inside of your sign in view here. Copy the on click, go to Google and add the Google provider here. Go to the sign up view and do the same thing. Now let's go ahead and try it out.
So I'm gonna go ahead and click Google. It doesn't matter from sign up or from sign in. Let's click Google and let's wait a second. This should open up the Google login. Let me try refreshing or maybe there's an error maybe we did something incorrectly well we did something incorrectly most certainly and what I don't like is that we are not getting any oops I just clicked GitHub no no no I don't want to log in with GitHub.
So basically, we're not getting the errors here. So what I want to do here is set error to be null. And then in here, I'm going to do OnError. How about we... Let's standardize this, shall we?
So, okay. Go inside of SignUpView right here. And you can copy the onSubmit method entirely. Paste it below itself and rename this onSocial. Change this to be provider.
Change it to GitHub or Google. Set the error to null and the pending to true. Change this from .email to social and simply pass the provider. As you can see, the provider, it has to be signin.social. So even in the signup view, you have to use the sign in for the on social.
So the provider type can be all of these. As you can see, when I hover, it's an enum. Not an enum, sorry, it's this type of type. So this actually matches. So I can just set the provider prop here and the error goes away.
And in here, you can set the pending false here and set the error message here, like this. And then you can use the onSocial in the buttons below. So let's try it like that. Or sign in with and let's do on social Google and in here on social github. So now what I would like to do is copy the on social here, go to sign in view here and keep it like this and then just change these as well.
So on social Google and in here on social GitHub. So now The reason I did this is because when you click on Google, it should now say provider not found. That's because we actually forgot to add the provider here. So let's quickly do that. Let's add Google to our alph.ts where we just added GitHub.
So let's just add Google below this. And as always, let's double check this. So Google client ID. Oops. So copy this, paste it here.
Copy this, paste it here. And now let's go ahead and try again. Google. There we go. Let's click on this one.
Let's click continue. And I should now be logged in as John Doe. There we go. So what I want to do now is I don't know if you've noticed, but even before I selected my account, I already got redirected to the root page. That's probably because of the way this onSocial here handles the onSuccess.
So I think onSuccess here might be just successfully making the OAuth request. So it already pushes me here, which is not good, because at this point, the user would not be logged in. So what we can do here is add a callback URL like this. And I just want to confirm. Let me just check inside of better out.
Can I search for callback URL? As you can see, we can also do it like this. I'm trying to find the callback URL. Okay, so we can use a relative imports, a relative URL. I was wondering if I need to do the whole localhost or not.
And then I'm going to remove the router.push from here. So that's in the sign in view. Let's do the same thing for the sign up view. So remove router.push here and add callback URL to forward slash. And how about we do the same thing for sign up with email.
So we can add callback URL, So we don't have to do router.push, which means we don't have to use router, which means we don't need the next navigation import. And do the same thing in the sign-in view. So in the onSubmit, sign in with email, remove router.push and add a callback URL to be a forward slash. Remove use router and remove next navigation. So let's go ahead back to slash sign in now and let's try logging in with GitHub.
So I should now only be redirected in the callback. There we go. Perfect. So I think that now we can officially mark this as completed. Now let's learn how to protect our non-auth pages here.
So I think we can finally now go inside of source app folder page and remove this entire thing. Div home page. We can remove all of these imports. The only thing I would actually like to leave here. Yeah, let me just revert it a bit.
Sorry for telling you to remove it. I mean, I'm just using it so I can copy these two things. This is the only thing I care about. There we go. So I just want this to be here, which means that I need to get data session from out client use session.
And I need to mark this as use client and I need to import button from components UI button and in here if there is no session return loading okay So make sure you mark this as use client, and I just want this, I don't need the entire form or anything. So The problem now is, as you can see, well, I didn't really handle this correctly, right? Because there is no session here. So I just return loading. But I actually want to handle it a little bit differently.
So this is how we would do it. Let's go inside of source modules, and I will just create a home module and inside of the home module I will create a UI folder and then I will create views and this will be a home view dot TSX. So that will actually be this page here. Let's paste it here and let's just export const home view here. And we can remove the default export entirely.
So this can be the useClient part, right? In our home module here. Home view. So now we are free to use our server component here as an actual server component so we can remove everything here and just use it as a server component so inside of here what we would do is we would return HomeView like this. So right now, it should work exactly the same.
It's displaying loading because I didn't do any out here to... Basically, if there's no session, I just say it's loading. So what I want to do instead is use a server component to redirect the user back to my auth pages. And the reason I want to use a server component for that is, well, so we learn how to use the server util of better auth. And simply because it's better to redirect from the server side than from a client component if we already have server components, right?
Why not use them for what they're good at? So let's go ahead and get the session here using await, which means that we have to turn this into an asynchronous component. Let's import out from libauth, so not auth client, auth directly, .api.getSession. And inside of the session here we have to pass the headers so this can be await headers from next headers and execute them it's a function. Make sure you don't have useClient here in the page.
And then if there is no session, redirect using next navigation to slash sign in. There we go. So now you don't really have to worry about this because never will a user see the HomeView client component even being rendered if they don't have a session. So yes, this is technically correct way of doing it now, because in here, we are going to detect that there is no session sooner than we detected that there is no session here, because this is a fetch request, whereas this is an actual deconstruction of the header parameters. So if I try manually going to localhost 3000 now, you can see that I am redirected back.
I cannot see the homepage anymore. But if I click on GitHub, for example, let's wait, let's wait. There we go, logged in as Antonio. So now I can visit the home page. What I want to do now is I want to do the reverse of this.
So I'm gonna go ahead and just copy this pattern and I'm gonna go inside of source app out sign in page dot dsx and I'm going to add this here and turn this into an asynchronous method and I'm going to import out from lib out and I'm going to import headers from next headers And I'm going to import redirect from next navigation. And in here I will do the opposite logic if the session already exists. So what I can do is turn it into a Boolean by adding double exclamation points. This will redirect the user back to the root page or to the home page, right? So now I will just indent this properly.
I will copy this again. I will go instead up here and I will do the same thing here. Async. Auth. Lib.
Auth. Headers. Oops. Next. Headers and redirect.
There we go. Like this. So now, if you are logged in like I am, you should not be able to visit slash sign in for example. You can see I'm immediately redirected back to my homepage. But if I sign out...
Oh, yes, I didn't properly do this. So This will not actively look for my session. So that's why this did not redirect after I signed out. But thankfully, that's why our sign out offers us a onSuccessFetchOptions, which is an object, onSuccess, and then in here we can do router, which we have to add, const router, useRouter from the next navigation. And let me just open this so you can see better better how it looks like.
So FetchOptions, router.push, signIn, Like this. So let's try this again. So I can now visit sign up and visit sign in, but I cannot visit the homepage. I immediately get redirected back here. But if I log in, I should get redirected back to the home page.
When I am logged in, I cannot visit sign in and I cannot visit sign up. Both of them are protected. But if I sign out, I should now get redirected. There we go. So that's how we're going to use this onSuccess method here.
Perfect. So You probably have a question here. Can we somehow reuse this? Can we somehow put this maybe in a layout and then this way we can protect the entire auth route group? Please do not do that.
It is much, much safer to explicitly protect routes rather than use the middleware or try and find a way to protect multiple things at once. It's always better to have explicit protection. There's been a number of issues in the middleware, in the layout, and those were security issues. So basically, people were able to bypass middlewares and bypass the layout protection. But the thing is, you never should have relied on things like that.
So using Auth in the middleware or in the layout is not inherently wrong if you want to improve user experience, if you want to show them, hey, you're not logged in. So it's not wrong to do that. But that shouldn't be your last line of defense, right? You should always have another explicit redirect in the actual page you're doing. In our case, we don't even need these redirects because we're going to be implementing tRPC in this tutorial and we're going to have protected procedures.
So our entire backend and API will be strictly protected. The only reason I'm even doing this redirects are for good user experience, right? So if you're asking, can I use the middleware? Can I use the layouts? You probably can.
There's probably documentation, But I would not suggest doing that because people then completely avoid doing any kind of actual authentication protection. So I will be teaching direct explicit authentication like this in pages. Great. So now that we have that ready, what I just want to do is add the icons here. And also, you know, just for I want to check if my actual normal registration is working.
So let's try john. Johndemo.com password, password. I want to see if these things are working or not. Let's see. So it looks like I was registered, but it did not redirect me.
Only once I refresh did it redirect me that's interesting okay so looks like that in mind this is why it's always good to test so go inside of your sign up view here and to fix that you should go inside of sign up by email here you can leave the callback url to be this but looks like we are going to need the router which we removed so const router use router from next navigation Let's go ahead and add it here. And simply on success, after set is pending, router.push to the root page, like this. And you don't need to add it for the social login because that seems to be working correctly. Perfect. So that will now work.
Go inside of sign in and you have to do the same thing. Next navigation. Go ahead and add the router here. Oops. And in the normal on submit here, do router push and go here, like this.
So I think that now this should be working. So if I sign out here and if I go into john-demo.com and password and sign in, I should get redirected and I am perfect. If I go instead of sign up here and try a new one And sign in, I should get redirected as well. There we go. Looks like everything is working nicely.
GitHub will use the callback and my social and my credential will use the router push. This seems to be working just fine. Perfect. All that's left to do is to add the icons and we can close this chapter. So go ahead and do npm install react icons dash dash legacy here that's The reason I'm adding these icons is simply because they have Google and GitHub icons.
If you're wondering about the version of this package, it is 5.5.0. Let's go inside of the sign-in view and let's import FA GitHub and FA Google from React icons. Copy this immediately inside of the sign-up view. Go back inside of the sign-in view, find your GitHub button and add fa-github here. And replace the Google text with FA Google and then do the same thing let's just see what the error is about oops slash FA that's the full import same thing here slash FA Go ahead and find the Google here or GitHub, FA GitHub.
And FA Google here. And there we go. Now we have nice icons and everything is working. Social login, redirects, routes are protected. Everything is working just fine.
Amazing, amazing job. So we learned how to protect non-alph pages and now let's go ahead and merge this pull request. So what I'm going to do is the following. I have nine unsaved changes here. I mean uncommitted changes.
So I'm gonna do is I'm going to click on my branch here. I will click create new branch and I will call this 05 Authentication Socials. And I will just confirm that that's the name here it is. I will add all of my changes. Just confirm you are on the new branch.
I will add all changes, so now they become staged changes. And I will add 05 authentication socials. And I will click Commit and Publish Branch. Now we can go to GitHub right here and we can open this pull request. Let's create the pull request here and let's wait for our AI reviewer to review our code.
And here we have our code summary. So new features. We added social login options via Google and GitHub on sign in and sign up pages. We have introduced a new home view displaying user information and sign out functionality. We improved authentication flow with server-side detection and automatic redirects for authenticated users.
We have updated UI to use icon buttons for social login options. So in here, of course, we have a more detailed walkthrough and we also have a very big sequence diagram explaining how our new session detection works, how our social login works. So you can pause and take a look at this if you are interested or need further explanation about that. Now in here we have some usual comments. CodeRabbit, well, very intelligently suggests that we should add a runtime validation for required environment variables.
So if you want to, yeah, you can go ahead and, you know, implement this kind of get environment variable function, which will check if it doesn't exist and then throw an error on time. But I'm pretty confident in doing it this way. I don't think there's anything we need to change here. At least not for, you know, development here. In here I'm not exactly sure that we use invalid fetch options on success.
Maybe this was in the previous versions of BetterAuth, but in the newer versions of BetterAuth, I'm pretty sure that sign out here offers Fetch options on success. So we are definitely doing a proper redirect here. So that's fine. So I will resolve this conversation. Now in here, it is telling us that on social success handle should redirect and this is a completely valid you know thing to to comment on I'm pretty sure a human reviewer would notice that that's missing here as well that's because we noticed that when using social it is smarter to rely on the callback URL rather than the router.push in the onSuccess because the onSuccess with the social sign-in is you have successfully sent an OAuth request but you were not actually authenticated just yet.
That's why it's better to use callback URL. So this is also fine. I will resolve this. And in here, I don't think it has the new API. Headers are now asynchronous.
This was a recent change in Next.js so that is okay as well but still very very smart suggestions it's good to have a sanity check on our changes here so I will just resolve everything there we go let's go ahead and merge this. Confirm merge. And then let's go back here. Let's go inside of main. Again, you can use main or you can use origin main.
It won't matter because you will click on this little button here and you will push and pull commits from Origin Main and this way if you go ahead and look at your graph you will see that you have just merged 0.5 authentication socials into your main branch here and of course you can check it yourself for example go inside of app page and in here we have the redirect which we just developed in that branch. Amazing, amazing job that marks the end of this chapter and see you in the next one.