diff --git a/.storybook/preview.js b/.storybook/preview.js index afa9e37..ca48536 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,6 +1,8 @@ +// @ts-check import { RouterContext } from 'next/dist/shared/lib/router-context'; import * as NextImage from 'next/image'; import '../pages/globals.css'; +import { AuthProvider } from '../state/auth/AuthContext'; const BREAKPOINTS_INT = { xs: 375, @@ -34,6 +36,14 @@ Object.defineProperty(NextImage, 'default', { value: (props) => , }); +export const decorators = [ + (Story) => ( + + + + ), +]; + export const parameters = { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { diff --git a/README.app.md b/README.app.md index 988e3b0..ff78847 100644 --- a/README.app.md +++ b/README.app.md @@ -1523,7 +1523,9 @@ _(Note when I say all children I mean **ALL** children, even ones that don't use We will create a new button component called `AuthButton`. This component is going to be dependent on the context provided by `AuthContext`, so we need to remember that when we use this button somewhere up the component tree we will need an `AuthContext.Provider` component for it to work -- the trick is to remember that's not just for our app, that applies to Storybook as well! For now though, let's just build the component. -// [TODO] description copy base component rename to buttons storybook title +Copy our `BaseComponent` over again into the `/components/button` directory and rename it to `auth`. We're going to replace all instances of `BaseComponent` with `AuthButton` including the filename. Make sure you also change the story title to `buttons/AuthButton` and remove any most data from the template. + +The structure of the `AuthButton` already exists, we are going to extract it out of our `Header` component into its own component like so: `components/buttons/auth/AuthButton.tsx` @@ -1551,10 +1553,147 @@ const AuthButton: React.FC = ({ className, ...buttonProps }) => { export default AuthButton; ``` -// [TODO] description \_app.tsx +Pay attention to the `useContext` invocation, that is how twe consume the `` context that will be wrapping our entire application. We'll get to that part last. The next step is to take this new auth button use it in our `Header`: + +```tsx +import Link from 'next/link'; +import AuthButton from '../../buttons/auth/AuthButton'; + +export interface IHeader extends React.ComponentPropsWithoutRef<'header'> {} + +const Header: React.FC = ({ className, ...headerProps }) => { + return ( +
+
+ + Home + + + Store + +
+
+ + Gmail + + + Images + + +
+
+ ); +}; + +export default Header; +``` + +Finally we need to update `_app.tsx` which is the component that wraps our whole app. We want every piece of our app to have access to the Auth context, so right now that serves as the best place for it. + +Technically every time the auth updates the app will re-render, but that is okay since presumably a real user would only be signing in once per session. + +`pages/_app.tsx` + +```tsx +import type { AppProps } from 'next/app'; +import { AuthProvider } from '../state/auth/AuthContext'; +import './globals.css'; +import { NextPageWithLayout } from './page'; + +interface AppPropsWithLayout extends AppProps { + Component: NextPageWithLayout; +} + +function MyApp({ Component, pageProps }: AppPropsWithLayout) { + // Use the layout defined at the page level, if available + const getLayout = Component.getLayout || ((page) => page); + + return {getLayout()}; +} + +export default MyApp; +``` + +And finally, if we want to be able to access these context values for the components when we run them in Storybook, we need to create a default story template that includes that context. + +For that we use Storybook decorators. Just export a const called `decorators` which React component(s) you want as a wrapper around all your stories. + +```js +import { AuthProvider } from '../state/auth/AuthContext'; + +... + +export const decorators = [ + (Story) => ( + + + + ), +]; +``` + +Thats it! Now run `yarn dev` and load [http://localhost:3000](http://localhost:3000) + +When you click on the "Sign In" button if all has gone correct it will toggle to a "Sign Out" which mimics the function of having logged into the site. Doing this is basic React behavior to toggle a button state. + +What is special about what we have done is when you enter a term into your search bar and hit search. It will navigate to a completely different page, the results page, but because of the React auth context wrapper your button should still show "Sign Out" if you had signed in on the home page. + +And that is persistent state between routes in Next.js + +## Next Steps -// [TODO] storybook aut context decorator +I hope you found this tutorial and learned something about setting up a solid and scaleable Next.js project for you and your team. + +This is the first part of what is intended to be a multi-part series on creating a production quality Next.js app. + +Some of my ideas for future installments are below, I'd encourage you to leave some feedback about which ones you'd find most useful (or other ones if you don't see them below). + +- ~~- How to Build Scalable Architecture for your Next.js Project~~ +- ~~- How to build a fullstack Next.js app using API routes and Tailwind CSS~~ +- How to implement unit and end-to-end testing in a Next.s app with jest and playwright +- How to add a global state manager to your Next.js app with Recoil +- How to create a CI/CD pipeline with Github actions and Vercel +- How to implement SSO authentication and internationalization in a Next.js app using NextAuth and i18next +- How to connect a database to your Next.js app with Prisma and Supabase +- How to manage multiple applications in a monorepo with Next.js and Nx + +Stay tuned and please don't hesitate to ask any questions, I'm happy to answer if I can! ## Wrapping Up -TODO +Remember that all code from this tutorial as a complete package is available in [this repository](https://github.com/alexeagleson/nextjs-fullstack-app-template). + +Please check some of my other learning tutorials. Feel free to leave a comment or question and share with others if you find any of them helpful: + +- [How to Build Scalable Architecture for your Next.js Project](https://dev.to/alexeagleson/how-to-build-scalable-architecture-for-your-nextjs-project-2pb7) + +- [How to Connect a React App to a Notion Database](https://dev.to/alexeagleson/how-to-connect-a-react-app-to-a-notion-database-51mc) + +- [How to use Node.js to backup your personal files](https://dev.to/alexeagleson/how-to-use-nodejs-to-backup-your-personal-files-and-learn-some-webdev-skills-along-the-way-541a) + +- [Introduction to Docker for Javascript Developers](https://dev.to/alexeagleson/docker-for-javascript-developers-41me) + +- [Learnings from React Conf 2021](https://dev.to/alexeagleson/learnings-from-react-conf-2021-17lg) + +- [How to Create a Dark Mode Component in React](https://dev.to/alexeagleson/how-to-create-a-dark-mode-component-in-react-3ibg) + +- [How to Analyze and Improve your 'Create React App' Production Build ](https://dev.to/alexeagleson/how-to-analyze-and-improve-your-create-react-app-production-build-4f34) + +- [How to Create and Publish a React Component Library](https://dev.to/alexeagleson/how-to-create-and-publish-a-react-component-library-2oe) + +- [How to use IndexedDB to Store Local Data for your Web App ](https://dev.to/alexeagleson/how-to-use-indexeddb-to-store-data-for-your-web-application-in-the-browser-1o90) + +- [Running a Local Web Server](https://dev.to/alexeagleson/understanding-the-modern-web-stack-running-a-local-web-server-4d8g) + +- [ESLint](https://dev.to/alexeagleson/understanding-the-modern-web-stack-linters-eslint-59pm) + +- [Prettier](https://dev.to/alexeagleson/understanding-the-modern-web-stack-prettier-214j) + +- [Babel](https://dev.to/alexeagleson/building-a-modern-web-stack-babel-3hfp) + +- [React & JSX](https://dev.to/alexeagleson/understanding-the-modern-web-stack-react-with-and-without-jsx-31c7) + +- [Webpack: The Basics](https://dev.to/alexeagleson/understanding-the-modern-web-stack-webpack-part-1-2mn1) diff --git a/README.auth.md b/README.auth.md new file mode 100644 index 0000000..00d5f7e --- /dev/null +++ b/README.auth.md @@ -0,0 +1,243 @@ +## Authentication + +_**Note: This section will likely be updated and reorganized in a future blog post, however it is complete and should help you get an auth solution in place for your app.**_ + +Let's look at how to add user authentication with [next-auth](https://next-auth.js.org/). NextAuth is a fantastic tool that removes a lot of the manual effort and management of the authentication process and leaves you with only the application specific behavior to configure. + +It has long been said that you should [never roll your own auth](https://withblue.ink/2020/04/08/stop-writing-your-own-user-authentication-code.html) and while of course, like everything, the reality is more complex and every project must be evaluated with the requirements it has, it's good general advice overall. + +User auth is a very complex topic, and if you are not experienced in it, making errors has a much higher risk than making errors in other areas of your application of exposing sensitive user data, which can obviously cause major issues. + +NextAuth does support using your own managed credentials (storing your own users in your database) which is a great feature if your app requires it, but since we haven't even configured a database yet we're going to set up the auth in our app to allow users to login with common existing credentials like Google and Github. + +Begin by installing `next-auth`. + +``` +yarn add next-auth +``` + +Unlike most of our recent tooling, this must exist during the runtime of our application, so it cannot be a `devDependency`. + +First step is to add the auth handler to an api route. Create the directory structure `/pages/api/auth` and create a file with the odd sounding filename `[...nextauth].ts` inside of it with the following content: + +`pages/api/auth/[...nextauth].ts` + +```ts +import NextAuth, { Profile } from 'next-auth'; +import GoogleProvider from 'next-auth/providers/google'; + +interface IGoogleProfile { + email: string; + email_verified: boolean; + name: string; + picture: `https://${string}`; + given_name: string; + family_name: string; + locale: string; // Two character lowercase + iat: number; + exp: number; +} + +if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) { + throw Error( + 'GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must be defined in environment' + ); +} + +export default NextAuth({ + // Configure one or more authentication providers + providers: [ + GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + }), + ], + callbacks: { + async signIn({ account, profile }) { + const googleProfile = profile as Profile & IGoogleProfile; + if (account.provider === 'google') { + return googleProfile.email_verified === true; + } + return true; // Do different verification for other providers that don't have `email_verified` + }, + }, +}); +``` + +_(Note that I have created my own interface above to show the shape of the current Google profile payload response data. This is not guaranteed to stay current if Google chooses to change their API in the future, so be aware, it is solely meant as a convenience.)_ + +First we will need to add some secret environment variables the auth can use to get private information from our environment. + +Create a file called `.env.local`. It's very important you use this exact naming pattern because this file is included in your `.gitignore` by default with the `create-next-app` configuration. + +**You must ensure this file is not included in your code repo when you commit.** + +If using VS Code you can do a sanity check and see that the filename should be greyed out compared to other files around it. It should also not appear in your source control tab showing files ready for staging. + +Before we create the environment file we will need a randomized secret value for NextAuth to use. If on a unix system you should be able to simply run + +``` +openssl rand -hex 32 +``` + +to generate a random value. If not you can use an online tool or method of your preference. + +`.env.local` + +```.env +NEXTAUTH_URL="http://localhost:3000" +NEXTAUTH_SECRET="YOUR_RANDOM_SECRET_VALUE" +``` + +Replace `YOUR_RANDOM_SECRET_VALUE` in the file above with the random string you generated. Remember that you are going to need to add this to your production environment as well, but we can do that later in the process. + +We will begin with the Google provider. Relevant documentation is [here](https://next-auth.js.org/providers/google) and [here](https://developers.google.com/identity/protocols/oauth2) and credentials/secrets can be obtained [here](https://console.developers.google.com/). + +Click "Credentials" then "Create Credentials" from the Google API console. + +It may ask you to configure your "consent" screen in which case select "external". + +Your form will look something like this. Google will not be able to redirect back to your local machine, so we'll be testing auth on your remote environment. If you've been following the full tutorial you will already have that configured for Vercel, so all you need to do is go to your Vercel dashboard and get the URL of your app. + +If you are using another service for deployment the process should be nearly the same. + +![Google OAuth](https://res.cloudinary.com/dqse2txyi/image/upload/v1649171060/blogs/nextjs-fullstack-app-template/google-oauth_bqf6er.png) + +Next add whatever "scopes" you like. These are keys that refer to the info your app will be able to see about the user. In this case I have just selected `userinfo.profile` to get name, but there is other info you request as well. + +Depending on what you configure this is what controls what the user sees in terms of things like "This app wants access to your name, email, etc" when they login. + +Once that is finished you can click "Publish App". + +Now to get the actual credentials, click `Credentials -> Create Credentials -> OAuth client ID` + +Fill out this screen in a similar fashion with your URL in place: + +![Google OAuth Client ID](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184075/blogs/nextjs-fullstack-app-template/google-client-id_gqwuib.png) + +The default callback URL for NExtAUth and Google is your domain followed by `/api/auth/callback/google` + +Once you have your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` add them to your env: + +`.env.local` + +```.env +NEXTAUTH_URL="http://localhost:3000" +NEXTAUTH_SECRET="YOUR_RANDOM_SECRET_VALUE" + +GOOGLE_CLIENT_ID="YOUR_PERSONAL_GOOGLE_CLIENT_ID" +GOOGLE_CLIENT_SECRET="YOUR_PERSONAL_GOOGLE_CLIENT_SECRET" +``` + +Obviously replacing the personal placeholders above with the values you get from your Google account. + +Now we can get back to the client side. You need to wrap your application in NextAuth's `` component like so: + +`pages/_app.tsx` + +```tsx +import { SessionProvider } from 'next-auth/react'; +import type { AppProps } from 'next/app'; +import '../styles/globals.css'; + +function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) { + return ( + + + + ); +} + +export default MyApp; +``` + +Then we can add a simple "Sign In" and "Sign Out" button to the homepage. You can move them to separate components later at your discretion. + +`pages/index.tsx` + +```tsx +import type { NextPage } from 'next'; +import { signOut, useSession } from 'next-auth/react'; +import Head from 'next/head'; +import Link from 'next/link'; +import styles from '../styles/Home.module.css'; + +const Home: NextPage = () => { + const { status } = useSession(); + + return ( +
+ + Create Next App + + + + +
+

+ Welcome to Next.js! +

+ + {status === 'authenticated' ? ( + <> +

Signed in

+ + + ) : ( + + Sign in + + )} +
+
+ ); +}; + +export default Home; +``` + +Make a commit to your project and push to your live production URL. Presuming you have properly configured that domain on your Google console then your login process will be as follows: + +![Login Step 01](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-01_mjkoxn.png) + +![Login Step 02](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-02_itlok7.png) + +_You can customize the above sign-in page to look however you like by creating your own Next page and [adding the route to the NextAuth pages config](https://next-auth.js.org/configuration/pages) in your `[...nextauth].ts` file._ + +![Login Step 03](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-03_hpwrva.png) + +![Login Step 04](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-04_yxhwid.png) + +And that's it! You now have basic authentication configured for your app. Trying refreshing and testing your logout button as well. + +You can use the `useSession()` hook to check for auth, you can read and process your token on the server side. Spend some time with the NextAuth docs to cover [all the different options you have](https://next-auth.js.org/configuration/options) for further development. + +You can also add any number of additional auth [providers](https://next-auth.js.org/providers/), each one will have to be configured and secrets generated on those platforms, but they all follow the same basic process as Google. In fact most are even simpler. + +### Storybook + +For those using Storybook, you'll get a "not defined" error if you try to load up a component that uses the auth hooks. That's because we haven't wrapped our stories in the auth provider. + +For this tutorial we are not going to worry about how to mock the actual authentication in Storybook, that's beyond the scope, but here's all you need to add to get your auth components back up and running in storybook in a default "logged out" state: + +`.storybook.preview.js` + +```jsx +import { SessionProvider } from 'next-auth/react'; + +... + +export const decorators = [ + (Story) => ( + + + + ), +]; + +export const parameters = { + ... +}; +``` + +Just add a `decorators` export with a null session provider and you'll be in business. diff --git a/README.md b/README.md index d007d4b..cddc4cf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ If you find this tutorial helpful, please share it with your friends and colleag This tutorial is available as a video lesson if you prefer that format: -{% youtube https://youtu.be/Iu5aZDqZt8E %} +{% youtube Iu5aZDqZt8E %} ## Table of Contents @@ -1475,250 +1475,6 @@ The only thing you need to keep in mind are the differences between your two env We will be adding `env` values in future tutorials so you will need to make sure those values are configured in both your local and production environment, since they are secrets and should never be committed to a public (or even private if can be avoided) repository. -## Authentication - -_**Note: This section will likely be updated and reorganized in a future blog post, however it is complete and should help you get an auth solution in place for your app.**_ - -Let's look at how to add user authentication with [next-auth](https://next-auth.js.org/). NextAuth is a fantastic tool that removes a lot of the manual effort and management of the authentication process and leaves you with only the application specific behavior to configure. - -It has long been said that you should [never roll your own auth](https://withblue.ink/2020/04/08/stop-writing-your-own-user-authentication-code.html) and while of course, like everything, the reality is more complex and every project must be evaluated with the requirements it has, it's good general advice overall. - -User auth is a very complex topic, and if you are not experienced in it, making errors has a much higher risk than making errors in other areas of your application of exposing sensitive user data, which can obviously cause major issues. - -NextAuth does support using your own managed credentials (storing your own users in your database) which is a great feature if your app requires it, but since we haven't even configured a database yet we're going to set up the auth in our app to allow users to login with common existing credentials like Google and Github. - -Begin by installing `next-auth`. - -``` -yarn add next-auth -``` - -Unlike most of our recent tooling, this must exist during the runtime of our application, so it cannot be a `devDependency`. - -First step is to add the auth handler to an api route. Create the directory structure `/pages/api/auth` and create a file with the odd sounding filename `[...nextauth].ts` inside of it with the following content: - -`pages/api/auth/[...nextauth].ts` - -```ts -import NextAuth, { Profile } from 'next-auth'; -import GoogleProvider from 'next-auth/providers/google'; - -interface IGoogleProfile { - email: string; - email_verified: boolean; - name: string; - picture: `https://${string}`; - given_name: string; - family_name: string; - locale: string; // Two character lowercase - iat: number; - exp: number; -} - -if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) { - throw Error( - 'GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must be defined in environment' - ); -} - -export default NextAuth({ - // Configure one or more authentication providers - providers: [ - GoogleProvider({ - clientId: process.env.GOOGLE_CLIENT_ID, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - }), - ], - callbacks: { - async signIn({ account, profile }) { - const googleProfile = profile as Profile & IGoogleProfile; - if (account.provider === 'google') { - return googleProfile.email_verified === true; - } - return true; // Do different verification for other providers that don't have `email_verified` - }, - }, -}); -``` - -_(Note that I have created my own interface above to show the shape of the current Google profile payload response data. This is not guaranteed to stay current if Google chooses to change their API in the future, so be aware, it is solely meant as a convenience.)_ - -First we will need to add some secret environment variables the auth can use to get private information from our environment. - -Create a file called `.env.local`. It's very important you use this exact naming pattern because this file is included in your `.gitignore` by default with the `create-next-app` configuration. - -**You must ensure this file is not included in your code repo when you commit.** - -If using VS Code you can do a sanity check and see that the filename should be greyed out compared to other files around it. It should also not appear in your source control tab showing files ready for staging. - -Before we create the environment file we will need a randomized secret value for NextAuth to use. If on a unix system you should be able to simply run - -``` -openssl rand -hex 32 -``` - -to generate a random value. If not you can use an online tool or method of your preference. - -`.env.local` - -```.env -NEXTAUTH_URL="http://localhost:3000" -NEXTAUTH_SECRET="YOUR_RANDOM_SECRET_VALUE" -``` - -Replace `YOUR_RANDOM_SECRET_VALUE` in the file above with the random string you generated. Remember that you are going to need to add this to your production environment as well, but we can do that later in the process. - -We will begin with the Google provider. Relevant documentation is [here](https://next-auth.js.org/providers/google) and [here](https://developers.google.com/identity/protocols/oauth2) and credentials/secrets can be obtained [here](https://console.developers.google.com/). - -Click "Credentials" then "Create Credentials" from the Google API console. - -It may ask you to configure your "consent" screen in which case select "external". - -Your form will look something like this. Google will not be able to redirect back to your local machine, so we'll be testing auth on your remote environment. If you've been following the full tutorial you will already have that configured for Vercel, so all you need to do is go to your Vercel dashboard and get the URL of your app. - -If you are using another service for deployment the process should be nearly the same. - -![Google OAuth](https://res.cloudinary.com/dqse2txyi/image/upload/v1649171060/blogs/nextjs-fullstack-app-template/google-oauth_bqf6er.png) - -Next add whatever "scopes" you like. These are keys that refer to the info your app will be able to see about the user. In this case I have just selected `userinfo.profile` to get name, but there is other info you request as well. - -Depending on what you configure this is what controls what the user sees in terms of things like "This app wants access to your name, email, etc" when they login. - -Once that is finished you can click "Publish App". - -Now to get the actual credentials, click `Credentials -> Create Credentials -> OAuth client ID` - -Fill out this screen in a similar fashion with your URL in place: - -![Google OAuth Client ID](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184075/blogs/nextjs-fullstack-app-template/google-client-id_gqwuib.png) - -The default callback URL for NExtAUth and Google is your domain followed by `/api/auth/callback/google` - -Once you have your `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` add them to your env: - -`.env.local` - -```.env -NEXTAUTH_URL="http://localhost:3000" -NEXTAUTH_SECRET="YOUR_RANDOM_SECRET_VALUE" - -GOOGLE_CLIENT_ID="YOUR_PERSONAL_GOOGLE_CLIENT_ID" -GOOGLE_CLIENT_SECRET="YOUR_PERSONAL_GOOGLE_CLIENT_SECRET" -``` - -Obviously replacing the personal placeholders above with the values you get from your Google account. - -Now we can get back to the client side. You need to wrap your application in NextAuth's `` component like so: - -`pages/_app.tsx` - -```tsx -import { SessionProvider } from 'next-auth/react'; -import type { AppProps } from 'next/app'; -import '../styles/globals.css'; - -function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) { - return ( - - - - ); -} - -export default MyApp; -``` - -Then we can add a simple "Sign In" and "Sign Out" button to the homepage. You can move them to separate components later at your discretion. - -`pages/index.tsx` - -```tsx -import type { NextPage } from 'next'; -import { signOut, useSession } from 'next-auth/react'; -import Head from 'next/head'; -import Link from 'next/link'; -import styles from '../styles/Home.module.css'; - -const Home: NextPage = () => { - const { status } = useSession(); - - return ( -
- - Create Next App - - - - -
-

- Welcome to Next.js! -

- - {status === 'authenticated' ? ( - <> -

Signed in

- - - ) : ( - - Sign in - - )} -
-
- ); -}; - -export default Home; -``` - -Make a commit to your project and push to your live production URL. Presuming you have properly configured that domain on your Google console then your login process will be as follows: - -![Login Step 01](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-01_mjkoxn.png) - -![Login Step 02](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-02_itlok7.png) - -_You can customize the above sign-in page to look however you like by creating your own Next page and [adding the route to the NextAuth pages config](https://next-auth.js.org/configuration/pages) in your `[...nextauth].ts` file._ - -![Login Step 03](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-03_hpwrva.png) - -![Login Step 04](https://res.cloudinary.com/dqse2txyi/image/upload/v1649184647/blogs/nextjs-fullstack-app-template/sign-in-step-04_yxhwid.png) - -And that's it! You now have basic authentication configured for your app. Trying refreshing and testing your logout button as well. - -You can use the `useSession()` hook to check for auth, you can read and process your token on the server side. Spend some time with the NextAuth docs to cover [all the different options you have](https://next-auth.js.org/configuration/options) for further development. - -You can also add any number of additional auth [providers](https://next-auth.js.org/providers/), each one will have to be configured and secrets generated on those platforms, but they all follow the same basic process as Google. In fact most are even simpler. - -### Storybook - -For those using Storybook, you'll get a "not defined" error if you try to load up a component that uses the auth hooks. That's because we haven't wrapped our stories in the auth provider. - -For this tutorial we are not going to worry about how to mock the actual authentication in Storybook, that's beyond the scope, but here's all you need to add to get your auth components back up and running in storybook in a default "logged out" state: - -`.storybook.preview.js` - -```jsx -import { SessionProvider } from 'next-auth/react'; - -... - -export const decorators = [ - (Story) => ( - - - - ), -]; - -export const parameters = { - ... -}; -``` - -Just add a `decorators` export with a null session provider and you'll be in business. - ## Next Steps I hope you found this tutorial and learned something about setting up a solid and scaleable Next.js project for you and your team. @@ -1727,9 +1483,10 @@ This is the first part of what is intended to be a multi-part series on creating Some of my ideas for future installments are below, I'd encourage you to leave some feedback about which ones you'd find most useful (or other ones if you don't see them below). -- How to build a fullstack Next.js app using API routes and Tailwind CSS -- How to add a global state manager to your Next.js app with Recoil +- ~~- How to Build Scalable Architecture for your Next.js Project~~ +- ~~- How to build a fullstack Next.js app using API routes and Tailwind CSS~~ - How to implement unit and end-to-end testing in a Next.s app with jest and playwright +- How to add a global state manager to your Next.js app with Recoil - How to create a CI/CD pipeline with Github actions and Vercel - How to implement SSO authentication and internationalization in a Next.js app using NextAuth and i18next - How to connect a database to your Next.js app with Prisma and Supabase @@ -1743,6 +1500,8 @@ Remember that all code from this tutorial as a complete package is available in Please check some of my other learning tutorials. Feel free to leave a comment or question and share with others if you find any of them helpful: +- [How to Build Scalable Architecture for your Next.js Project](https://dev.to/alexeagleson/how-to-build-scalable-architecture-for-your-nextjs-project-2pb7) + - [How to Connect a React App to a Notion Database](https://dev.to/alexeagleson/how-to-connect-a-react-app-to-a-notion-database-51mc) - [How to use Node.js to backup your personal files](https://dev.to/alexeagleson/how-to-use-nodejs-to-backup-your-personal-files-and-learn-some-webdev-skills-along-the-way-541a)