diff --git a/docs/docs/authentication-tutorial.md b/docs/docs/authentication-tutorial.md
index 757600808ef4c..34b21efe38c1e 100644
--- a/docs/docs/authentication-tutorial.md
+++ b/docs/docs/authentication-tutorial.md
@@ -2,7 +2,463 @@
title: Making a site with user authentication
---
-This is a stub. Help our community expand it.
+Sometimes, you need to create a site with gated content, available only to authenticated users. Using Gatsby, you may achieve this using the concept of [client-only routes](https://www.gatsbyjs.org/docs/building-apps-with-gatsby/#client-only-routes), to define which pages a user can view only after logging in.
-Please use the [Gatsby Style Guide](/docs/gatsby-style-guide/) to ensure your
-pull request gets accepted.
+## Prerequisites
+
+You should have already configured your environment to be able to use the `gatsby-cli`. A good starting point is the [main tutorial](https://www.gatsbyjs.org/tutorial/).
+
+## Security notice
+
+In production, you should use a tested and robust solution to handle the authentication. [Auth0](https://www.auth0.com), [Firebase](https://firebase.google.com), and [Passport.js](passportjs.org) are good examples. This tutorial will only cover the authentication workflow, but you should take the security of your app as seriously as possible.
+
+## Building your Gatsby app
+
+Start by creating a new Gatsby project:
+
+```shell
+gatsby new gatsby-auth
+cd gatsby-auth
+```
+
+Then, add a more apt title to your newly created site, changing the content of `gatsby-config.js`:
+
+```javascript:title=gatsby-config.js
+module.exports = {
+ siteMetadata: {
+ title: "Gatsby Authentication Tutorial",
+ },
+ plugins: ["gatsby-plugin-react-helmet", "gatsby-plugin-offline"],
+}
+```
+
+Create a new component to hold the links. For now, it will act as a placeholder:
+
+```jsx:title=src/components/navBar.js
+import React from "react"
+import { Link } from "gatsby"
+
+export default () => (
+
+ You are not logged in
+
+
+
+)
+```
+
+And edit the layout component to include it:
+
+```jsx{7,41}:title=src/components/layout.js
+import React from "react"
+import PropTypes from "prop-types"
+import Helmet from "react-helmet"
+import { StaticQuery, graphql } from "gatsby"
+
+import Header from "./header"
+import NavBar from "./navBar"
+import "./layout.css"
+
+const Layout = ({ children }) => (
+ (
+ <>
+
+
+
+
+
+
+ {children}
+
+ >
+ )}
+ />
+)
+
+Layout.propTypes = {
+ children: PropTypes.node.isRequired,
+}
+
+export default Layout
+```
+
+Lastly, change the index page to include this new content:
+
+```jsx{9-11}:title=src/pages/index.js
+import React from "react"
+import { Link } from "gatsby"
+
+import Layout from "../components/layout"
+
+const IndexPage = () => (
+
+
Hi people
+
+ You should log in to see restricted content
+
+
+)
+
+export default IndexPage
+```
+
+## Authentication service
+
+For this tutorial you will use a hardcoded user/password. Create the folder `src/services` and add the following content to the file `auth.js`:
+
+```javascript:title=src/services/auth.js
+export const isBrowser = () => typeof window !== "undefined"
+
+export const getUser = () =>
+ isBrowser() && window.localStorage.getItem("gatsbyUser")
+ ? JSON.parse(window.localStorage.getItem("gatsbyUser"))
+ : {}
+
+const setUser = user =>
+ window.localStorage.setItem("gatsbyUser", JSON.stringify(user))
+
+export const handleLogin = ({ username, password }) => {
+ if (username === `john` && password === `pass`) {
+ return setUser({
+ username: `john`,
+ name: `Johnny`,
+ email: `johnny@example.org`,
+ })
+ }
+
+ return false
+}
+
+export const isLoggedIn = () => {
+ const user = getUser()
+
+ return !!user.username
+}
+
+export const logout = callback => {
+ setUser({})
+ callback()
+}
+```
+
+## Creating client-only routes
+
+At the beginning of this tutorial, you created a "default" Gatsby site, which includes the `@reach/router` library. Now, using the [@reach/router](https://reach.tech/router/) library, you can create routes available only to logged-in users. This library is used by Gatsby under the hood, so you don't even have to install it.
+
+First, edit `gatsby-node.js`. You will define that any route that starts with `/app/` is part of your restricted content and the page will be created on demand:
+
+```javascript:title=gatsby-config.js
+// Implement the Gatsby API “onCreatePage”. This is
+// called after every page is created.
+exports.onCreatePage = async ({ page, actions }) => {
+ const { createPage } = actions
+
+ // page.matchPath is a special key that's used for matching pages
+ // only on the client.
+ if (page.path.match(/^\/app/)) {
+ page.matchPath = "/app/*"
+
+ // Update the page.
+ createPage(page)
+ }
+}
+```
+
+> Note: There is a convenient plugin that already does this work for you: [gatsby-plugin-create-client-paths](https://www.gatsbyjs.org/packages/gatsby-plugin-create-client-paths/)
+
+Now, you must create a generic page that will have the task to generate the restricted content:
+
+```jsx:title=src/pages/app.js
+import React from "react"
+import { Router } from "@reach/router"
+import Layout from "../components/layout"
+import Profile from "../components/profile"
+import Login from "../components/login"
+
+const App = () => (
+
+
+
+
+
+
+)
+
+export default App
+```
+
+Next, add the components regarding those new routes. The profile component to show the user data:
+
+```jsx:title=src/components/profile.js
+import React from "react"
+
+const Profile = () => (
+ <>
+
Your profile
+
+
Name: Your name will appear here
+
E-mail: And here goes the mail
+
+ >
+)
+
+export default Profile
+```
+
+The login component will handle - as you may have guessed - the login process:
+
+```jsx:title=src/components/login.js
+import React from "react"
+import { navigate } from "gatsby"
+import { handleLogin, isLoggedIn } from "../services/auth"
+
+class Login extends React.Component {
+ state = {
+ username: ``,
+ password: ``,
+ }
+
+ handleUpdate = event => {
+ this.setState({
+ [event.target.name]: event.target.value,
+ })
+ }
+
+ handleSubmit = event => {
+ event.preventDefault()
+ handleLogin(this.state)
+ }
+
+ render() {
+ if (isLoggedIn()) {
+ navigate(`/app/profile`)
+ }
+
+ return (
+ <>
+
Log in
+
+ >
+ )
+ }
+}
+
+export default Login
+```
+
+Though the routing is working now, you still can access all routes without restriction.
+
+## Controlling private routes
+
+To check if a user can access the content, you can wrap the restricted content inside a PrivateRoute component:
+
+```jsx:title=scr/components/privateRoute.js
+import React from "react"
+import { navigate } from "gatsby"
+import { isLoggedIn } from "../services/auth"
+
+const PrivateRoute = ({ component: Component, location, ...rest }) => {
+ if (!isLoggedIn() && location.pathname !== `/app/login`) {
+ // If the user is not logged in, redirect to the login page.
+ navigate(`/app/login`)
+ return null
+ }
+
+ return
+}
+
+export default PrivateRoute
+```
+
+And now you can edit your Router to use the PrivateRoute component:
+
+```jsx{4,11}:title=src/pages/app.js
+import React from "react"
+import { Router } from "@reach/router"
+import Layout from "../components/layout"
+import PrivateRoute from "../components/privateRoute"
+import Profile from "../components/profile"
+import Login from "../components/login"
+
+const App = () => (
+
+
+
+
+
+
+)
+
+export default App
+```
+
+## Refactoring to use new routes and user data
+
+With the client-only routes in place, you must now refactor some files to account for the user data available.
+
+The navigation bar will show the user name and logout option to registered users:
+
+```jsx{2-3,5-12,21,26,28-38,42}:title=src/components/navBar.js
+import React from "react"
+import { Link, navigate } from "gatsby"
+import { getUser, isLoggedIn, logout } from "../services/auth"
+
+export default () => {
+ const content = { message: "", login: true }
+ if (isLoggedIn()) {
+ content.message = `Hello, ${getUser().name}`
+ } else {
+ content.message = "You are not logged in"
+ }
+ return (
+
+ {content.message}
+
+
+
+ )
+}
+```
+
+The index page will suggest to login or check the profile accordingly:
+
+```jsx{3,7-8,10-23,26}:title=src/pages/index.js
+import React from "react"
+import { Link } from "gatsby"
+import { getUser, isLoggedIn } from "../services/auth"
+
+import Layout from "../components/layout"
+
+const IndexPage = () => {
+ return (
+
+
Hi {isLoggedIn() ? getUser().name : "people"}
+
+ {isLoggedIn() ? (
+ <>
+ You are logged in, so check your{" "}
+ profile
+ >
+ ) : (
+ <>
+ You should log in to see restricted
+ content
+ >
+ )}
+
+
+ )
+}
+
+export default IndexPage
+```
+
+And the profile will show the user data:
+
+```jsx{2,8,9}:title=src/components/profile.js
+import React from "react"
+import { getUser } from "../services/auth"
+
+const Profile = () => (
+ <>
+
Your profile
+
+
Name: {getUser().name}
+
E-mail: {getUser().email}
+
+ >
+)
+
+export default Profile
+```
+
+You should now have a complete authentication workflow, functioning with both login and a user-restricted area!
+
+## Further reading
+
+If you want to learn more about using production-ready auth solutions, these links may help:
+
+- [Gatsby repo simple auth example](https://github.com/gatsbyjs/gatsby/tree/master/examples/simple-auth)
+- [A Gatsby email _application_](https://github.com/DSchau/gatsby-mail), using React Context API to handle authentication
+- [The Gatsby store for swag and other Gatsby goodies](https://github.com/gatsbyjs/store.gatsbyjs.org)
+- [Building a blog with Gatsby, React and Webtask.io!](https://auth0.com/blog/building-a-blog-with-gatsby-react-and-webtask/)
+- [JAMstack PWA — Let’s Build a Polling App. with Gatsby.js, Firebase, and Styled-components Pt. 2](https://medium.com/@UnicornAgency/jamstack-pwa-lets-build-a-polling-app-with-gatsby-js-firebase-and-styled-components-pt-2-9044534ea6bc)