-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a demo of client-only routes that require a user to be authenticated before viewing. It does NOT show how to build secure authentication. (This is called out in the README.) Signed-off-by: Jason Lengstorf <[email protected]>
- Loading branch information
1 parent
d169097
commit 1442b70
Showing
24 changed files
with
562 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Gatsby Authentication Demo | ||
|
||
This is a simplified demo to show how an authentication workflow is implemented in Gatsby. | ||
|
||
The short version is: | ||
|
||
* Gatsby statically renders all unauthenticated routes as usual | ||
* Authenticated routes are whitelisted as client-only | ||
* Logged out users are redirected to the login page if they attempt to visit private routes | ||
* Logged in users will see their private content | ||
|
||
## A Note About Security | ||
|
||
This example is less about creating an example of secure, production-ready authentication, and more about showing Gatsby's ability to support dynamic content in client-only routes. | ||
|
||
For production-ready authentication solutions, take a look at [Auth0](https://auth0.com) or [Passport.js](http://www.passportjs.org/). Rolling a custom auth system is hard and likely to have security holes. Auth0 and Passport.js are both battle tested and widely used. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
siteMetadata: { | ||
title: 'Gatsby Demo Simple Authentication', | ||
}, | ||
plugins: ['gatsby-plugin-react-helmet'], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* Implement Gatsby's Node APIs in this file. | ||
* | ||
* See: https://www.gatsbyjs.org/docs/node-apis/ | ||
*/ | ||
|
||
exports.onCreatePage = async ({ page, boundActionCreators }) => { | ||
const { createPage } = boundActionCreators; | ||
|
||
// page.matchPath is a special key that's used for matching pages | ||
// only on the client. | ||
if (page.path.match(/^\/app/)) { | ||
page.matchPath = '/app/:path'; | ||
|
||
// Update the page. | ||
createPage(page); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "gatsby-demo-simple-auth", | ||
"description": "Gatsby demo of simplified authentication flow.", | ||
"version": "1.0.0", | ||
"author": "Jason Lengstorf <[email protected]>", | ||
"dependencies": { | ||
"gatsby": "latest", | ||
"gatsby-link": "latest", | ||
"gatsby-plugin-react-helmet": "latest", | ||
"prop-types": "^15.6.1", | ||
"react-helmet": "^5.2.0", | ||
"react-router-dom": "^4.2.2" | ||
}, | ||
"keywords": ["gatsby"], | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "gatsby build", | ||
"develop": "gatsby develop", | ||
"format": "prettier --write 'src/**/*.js'", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"devDependencies": { | ||
"prettier": "^1.11.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from "react" | ||
import View from "./View" | ||
import { getCurrentUser } from "../utils/auth" | ||
|
||
const Details = () => { | ||
const { name, legalName, email } = getCurrentUser() | ||
|
||
return ( | ||
<View title="Your Details"> | ||
<ul> | ||
<li>Preferred name: {name}</li> | ||
<li>Legal name: {legalName}</li> | ||
<li>Email address: {email}</li> | ||
</ul> | ||
</View> | ||
) | ||
} | ||
|
||
export default Details |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
.form { | ||
margin: 1rem 0; | ||
} | ||
|
||
.form__label { | ||
display: block; | ||
font-size: 67.5%; | ||
letter-spacing: 0.125em; | ||
text-transform: uppercase; | ||
} | ||
|
||
.form__label + .form__label { | ||
margin-top: 0.5rem; | ||
} | ||
|
||
.form__input { | ||
display: block; | ||
font-size: 1rem; | ||
padding: 0.25rem; | ||
} | ||
|
||
.form__button { | ||
background-color: rebeccapurple; | ||
border: 0; | ||
color: white; | ||
font-size: 1.25rem; | ||
font-weight: bold; | ||
margin-top: 0.5rem; | ||
padding: 0.25rem 1rem; | ||
transition: background-color 150ms linear; | ||
} | ||
|
||
.form__button:hover { | ||
cursor: pointer; | ||
} | ||
|
||
.form__button:hover, | ||
.form__button:active, | ||
.form__button:focus { | ||
background-color: color(rebeccapurple lightness(-20%)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from "react" | ||
import { withRouter } from "react-router-dom" | ||
import styles from "./form.module.css" | ||
|
||
export default withRouter(({ handleSubmit, handleUpdate, history }) => ( | ||
<form | ||
className={styles.form} | ||
method="post" | ||
onSubmit={event => { | ||
handleSubmit(event) | ||
history.push(`/app/profile`) | ||
}} | ||
> | ||
<p className={styles[`form__instructions`]}> | ||
For this demo, please log in with the username <code>gatsby</code> and the | ||
password <code>demo</code>. | ||
</p> | ||
<label className={styles[`form__label`]}> | ||
Username | ||
<input | ||
className={styles[`form__input`]} | ||
type="text" | ||
name="username" | ||
onChange={handleUpdate} | ||
/> | ||
</label> | ||
<label className={styles[`form__label`]}> | ||
Password | ||
<input | ||
className={styles[`form__input`]} | ||
type="password" | ||
name="password" | ||
onChange={handleUpdate} | ||
/> | ||
</label> | ||
<input className={styles[`form__button`]} type="submit" value="Log In" /> | ||
</form> | ||
)) |
44 changes: 44 additions & 0 deletions
44
examples/simple-auth/src/components/Header/header.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
.header { | ||
background-color: rebeccapurple; | ||
} | ||
|
||
.header__wrap { | ||
align-items: baseline; | ||
display: grid; | ||
grid-template-columns: repeat(2, 1fr); | ||
margin: 0 auto; | ||
max-width: 640px; | ||
padding: 1rem 0; | ||
} | ||
|
||
.header__heading { | ||
margin: 0; | ||
font-size: 2rem; | ||
} | ||
|
||
.header__nav { | ||
font-size: 1.25rem; | ||
margin-top: 0; | ||
text-align: right; | ||
} | ||
|
||
.header__link { | ||
color: white; | ||
font-weight: bold; | ||
margin-left: 0.75rem; | ||
margin-top: 0; | ||
padding: 0.25rem; | ||
text-decoration: none; | ||
} | ||
|
||
.header__link--home { | ||
font-size: 2rem; | ||
margin-left: -0.25rem; | ||
} | ||
|
||
.header__link:hover, | ||
.header__link:active, | ||
.header__link:focus { | ||
background: white; | ||
color: rebeccapurple; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from "react" | ||
import Link from "gatsby-link" | ||
import styles from "./header.module.css" | ||
|
||
const Header = () => ( | ||
<header className={styles.header}> | ||
<div className={styles[`header__wrap`]}> | ||
<h1 className={styles[`header__heading`]}> | ||
<Link | ||
to="/" | ||
className={`${styles[`header__link`]} ${ | ||
styles[`header__link--home`] | ||
}`} | ||
> | ||
Gatsby Profiles | ||
</Link> | ||
</h1> | ||
<nav role="main" className={styles[`header__nav`]}> | ||
<Link to="/" className={styles[`header__link`]}> | ||
Home | ||
</Link> | ||
<Link to="/app/profile" className={styles[`header__link`]}> | ||
Profile | ||
</Link> | ||
<Link to="/app/details" className={styles[`header__link`]}> | ||
Details | ||
</Link> | ||
</nav> | ||
</div> | ||
</header> | ||
) | ||
|
||
export default Header |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from "react" | ||
import View from "./View" | ||
import { getCurrentUser } from "../utils/auth" | ||
|
||
const Home = () => { | ||
const { name } = getCurrentUser() | ||
|
||
return ( | ||
<View title="Your Profile"> | ||
<p>Welcome back, {name}!</p> | ||
</View> | ||
) | ||
} | ||
|
||
export default Home |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from "react" | ||
import { Redirect } from "react-router-dom" | ||
import Form from "./Form" | ||
import View from "./View" | ||
import { handleLogin, isLoggedIn } from "../utils/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()) { | ||
return <Redirect to={{ pathname: `/app/profile` }} /> | ||
} | ||
|
||
return ( | ||
<View title="Log In"> | ||
<Form | ||
handleUpdate={e => this.handleUpdate(e)} | ||
handleSubmit={e => this.handleSubmit(e)} | ||
/> | ||
</View> | ||
) | ||
} | ||
} | ||
|
||
export default Login |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from "react" | ||
import PropTypes from "prop-types" | ||
import { Redirect, Route } from "react-router-dom" | ||
import { isLoggedIn } from "../utils/auth" | ||
|
||
// More info at https://reacttraining.com/react-router/web/example/auth-workflow | ||
const PrivateRoute = ({ component: Component, ...rest }) => ( | ||
<Route | ||
{...rest} | ||
render={props => | ||
!isLoggedIn() ? ( | ||
// If we’re not logged in, redirect to the home page. | ||
<Redirect to={{ pathname: `/app/login` }} /> | ||
) : ( | ||
<Component {...props} /> | ||
) | ||
} | ||
/> | ||
) | ||
|
||
PrivateRoute.propTypes = { | ||
component: PropTypes.any.isRequired, | ||
} | ||
|
||
export default PrivateRoute |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import React from "react" | ||
import { Link, withRouter } from "react-router-dom" | ||
import { getCurrentUser, isLoggedIn, logout } from "../../utils/auth" | ||
import styles from "./status.module.css" | ||
|
||
export default withRouter(({ history }) => { | ||
let details | ||
if (!isLoggedIn()) { | ||
details = ( | ||
<p className={styles[`status__text`]}> | ||
To get the full app experience, you’ll need to{` `} | ||
<Link to="/app/login">log in</Link>. | ||
</p> | ||
) | ||
} else { | ||
const { name, email } = getCurrentUser() | ||
|
||
details = ( | ||
<p className={styles[`status__text`]}> | ||
Logged in as {name} ({email})!{` `} | ||
<a | ||
href="/" | ||
onClick={event => { | ||
event.preventDefault() | ||
logout(() => history.push(`/app/login`)) | ||
}} | ||
> | ||
log out | ||
</a> | ||
</p> | ||
) | ||
} | ||
|
||
return <div className={styles.status}>{details}</div> | ||
}) |
11 changes: 11 additions & 0 deletions
11
examples/simple-auth/src/components/Status/status.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
.status { | ||
background: lightgrey; | ||
font-size: 87.5%; | ||
padding: 0.25rem; | ||
} | ||
|
||
.status__text { | ||
margin: 0 auto; | ||
max-width: 640px; | ||
text-align: right; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React from "react" | ||
import PropTypes from "prop-types" | ||
import styles from "./view.module.css" | ||
|
||
const View = ({ title, children }) => ( | ||
<section className={styles.view}> | ||
<h1 className={styles[`view__heading`]}>{title}</h1> | ||
{children} | ||
</section> | ||
) | ||
|
||
View.propTypes = { | ||
title: PropTypes.string.isRequired, | ||
} | ||
|
||
export default View |
Oops, something went wrong.