├── .babelrc ├── .gitignore ├── LICENSE ├── actions └── index.js ├── components ├── Auth.js ├── Jedi.js └── JedisList.js ├── containers ├── App.js ├── DevTools.js ├── Root.dev.js ├── Root.js └── Root.prod.js ├── index.html ├── index.js ├── middleware └── api.js ├── package.json ├── readme.md ├── reducers └── index.js ├── routes.js ├── server.js ├── server ├── package.json ├── public │ └── images │ │ ├── anakin-skywalker.png │ │ ├── luke-skywalker.jpg │ │ ├── mace-windu.jpg │ │ ├── obi-wan-kenobi.jpg │ │ └── yoda.png └── server.js ├── store ├── configureStore.dev.js ├── configureStore.js └── configureStore.prod.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"], 3 | "env": { 4 | "development": { 5 | "presets": ["react-hmre"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Auth0, Inc. (http://auth0.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /actions/index.js: -------------------------------------------------------------------------------- 1 | import { CALL_API } from '../middleware/api' 2 | 3 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS' 4 | export const LOGIN_ERROR = 'LOGIN_ERROR' 5 | 6 | function loginSuccess(profile) { 7 | return { 8 | type: LOGIN_SUCCESS, 9 | profile 10 | } 11 | } 12 | 13 | function loginError(error) { 14 | return { 15 | type: LOGIN_ERROR, 16 | error 17 | } 18 | } 19 | 20 | export function login() { 21 | const lock = new Auth0Lock('AUTH0_CLIENT_ID', 'AUTH0_DOMAIN') 22 | return dispatch => { 23 | lock.show((error, profile, token) => { 24 | if(error) { 25 | return dispatch(loginError(error)) 26 | } 27 | localStorage.setItem('profile', JSON.stringify(profile)) 28 | localStorage.setItem('id_token', token) 29 | return dispatch(loginSuccess(profile)) 30 | }) 31 | } 32 | } 33 | 34 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS' 35 | 36 | function logoutSuccess(profile) { 37 | return { 38 | type: LOGOUT_SUCCESS 39 | } 40 | } 41 | 42 | export function logout() { 43 | return dispatch => { 44 | localStorage.removeItem('id_token'); 45 | localStorage.removeItem('profile'); 46 | return dispatch(logoutSuccess()); 47 | } 48 | } 49 | 50 | export const JEDIS_REQUEST = 'JEDIS_REQUEST' 51 | export const JEDIS_SUCCESS = 'JEDIS_SUCCESS' 52 | export const JEDIS_FAILURE = 'JEDIS_FAILURE' 53 | 54 | function fetchJedis() { 55 | return { 56 | [CALL_API]: { 57 | types: [ JEDIS_REQUEST, JEDIS_SUCCESS, JEDIS_FAILURE ], 58 | endpoint: 'jedis', 59 | authenticatedRequest: false 60 | } 61 | } 62 | } 63 | 64 | export function loadJedis() { 65 | return dispatch => { 66 | return dispatch(fetchJedis()) 67 | } 68 | } 69 | 70 | export const JEDI_REQUEST = 'JEDI_REQUEST' 71 | export const JEDI_SUCCESS = 'JEDI_SUCCESS' 72 | export const JEDI_FAILURE = 'JEDI_FAILURE' 73 | 74 | function fetchJedi(id) { 75 | return { 76 | [CALL_API]: { 77 | types: [ JEDI_REQUEST, JEDI_SUCCESS, JEDI_FAILURE ], 78 | endpoint: `jedis/${id}`, 79 | authenticatedRequest: true 80 | } 81 | } 82 | } 83 | 84 | export function loadJedi(id) { 85 | return dispatch => { 86 | return dispatch(fetchJedi(id)) 87 | } 88 | } -------------------------------------------------------------------------------- /components/Auth.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | 3 | export default class Auth extends Component { 4 | constructor(props) { 5 | super(props) 6 | } 7 | 8 | render() { 9 | const { onLoginClick, onLogoutClick, isAuthenticated, profile } = this.props 10 | return ( 11 |
12 | { !isAuthenticated ? ( 13 | 16 | ) : ( 17 | 22 | )} 23 |
24 | ) 25 | } 26 | } -------------------------------------------------------------------------------- /components/Jedi.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | 3 | export default class Jedi extends Component { 4 | 5 | render() { 6 | const { jedi } = this.props 7 | return ( 8 |
9 | { jedi && 10 |
11 |

{jedi.name}

12 | 13 |
14 | } 15 |
16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /components/JedisList.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | 3 | function getJediListItem(jedi, isAuthenticated, onClick) { 4 | return( 5 |
  • 6 | { isAuthenticated ? ( 7 | onClick(jedi.id)}>

    {jedi.name}

    8 | ) : ( 9 |

    {jedi.name}

    10 | )} 11 |
  • 12 | ) 13 | } 14 | export default class JedisList extends Component { 15 | constructor(props) { 16 | super(props) 17 | } 18 | 19 | render() { 20 | const { jedis, error, onClick, onGetJediClick, isAuthenticated } = this.props 21 | const jedisList = jedis.map(jedi => getJediListItem(jedi, isAuthenticated, onGetJediClick)) 22 | return ( 23 |
    24 | 25 | { jedis && 26 | 29 | } 30 | { error && 31 | {error} 32 | } 33 |
    34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { connect } from 'react-redux' 3 | import { loadJedis, loadJedi, login, logout } from '../actions' 4 | import JedisList from '../components/JedisList' 5 | import Jedi from '../components/Jedi' 6 | import Auth from '../components/Auth' 7 | 8 | class App extends Component { 9 | constructor(props) { 10 | super(props) 11 | this.handleGetJedisClick = this.handleGetJedisClick.bind(this) 12 | this.handleGetJediClick = this.handleGetJediClick.bind(this) 13 | this.handleLoginClick = this.handleLoginClick.bind(this) 14 | this.handleLogoutClick = this.handleLogoutClick.bind(this) 15 | } 16 | 17 | handleGetJedisClick() { 18 | this.props.loadJedis() 19 | } 20 | 21 | handleGetJediClick(id) { 22 | this.props.loadJedi(id) 23 | } 24 | 25 | handleLoginClick() { 26 | this.props.login() 27 | } 28 | 29 | handleLogoutClick() { 30 | this.props.logout() 31 | } 32 | 33 | render() { 34 | const { allJedis, singleJedi, error, isAuthenticated, profile } = this.props 35 | return ( 36 |
    37 |
    38 |
    39 | Redux Jedi 40 | 46 |
    47 |
    48 | 49 |
    50 | 57 | 58 |
    59 |
    60 | ) 61 | } 62 | } 63 | 64 | function mapStateToProps(state) { 65 | const { jedis, jedi, auth } = state 66 | const { allJedis, error } = jedis 67 | const { singleJedi } = jedi 68 | const { isAuthenticated, profile } = auth 69 | return { 70 | allJedis, 71 | singleJedi, 72 | error, 73 | isAuthenticated, 74 | profile 75 | } 76 | } 77 | 78 | export default connect(mapStateToProps, { 79 | loadJedis, 80 | loadJedi, 81 | login, 82 | logout 83 | })(App) 84 | -------------------------------------------------------------------------------- /containers/DevTools.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createDevTools } from 'redux-devtools' 3 | import LogMonitor from 'redux-devtools-log-monitor' 4 | import DockMonitor from 'redux-devtools-dock-monitor' 5 | 6 | export default createDevTools( 7 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /containers/Root.dev.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Provider } from 'react-redux' 3 | import routes from '../routes' 4 | import DevTools from './DevTools' 5 | import { Router } from 'react-router' 6 | 7 | export default class Root extends Component { 8 | render() { 9 | const { store, history } = this.props 10 | return ( 11 | 12 |
    13 | 14 | 15 |
    16 |
    17 | ) 18 | } 19 | } 20 | 21 | Root.propTypes = { 22 | store: PropTypes.object.isRequired, 23 | history: PropTypes.object.isRequired 24 | } 25 | -------------------------------------------------------------------------------- /containers/Root.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./Root.prod') 3 | } else { 4 | module.exports = require('./Root.dev') 5 | } 6 | -------------------------------------------------------------------------------- /containers/Root.prod.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Provider } from 'react-redux' 3 | import routes from '../routes' 4 | import { Router } from 'react-router' 5 | 6 | export default class Root extends Component { 7 | render() { 8 | const { store, history } = this.props 9 | return ( 10 | 11 | 12 | 13 | ) 14 | } 15 | } 16 | 17 | Root.propTypes = { 18 | store: PropTypes.object.isRequired, 19 | history: PropTypes.object.isRequired 20 | } 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Redux Jedi 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 |
    19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill' 2 | import React from 'react' 3 | import { render } from 'react-dom' 4 | import { browserHistory } from 'react-router' 5 | import { syncHistoryWithStore } from 'react-router-redux' 6 | import Root from './containers/Root' 7 | import configureStore from './store/configureStore' 8 | 9 | const store = configureStore() 10 | const history = syncHistoryWithStore(browserHistory, store) 11 | 12 | render( 13 | , 14 | document.getElementById('root') 15 | ) 16 | -------------------------------------------------------------------------------- /middleware/api.js: -------------------------------------------------------------------------------- 1 | export const API_ROOT = 'http://localhost:7000/api/' 2 | 3 | function callApi(endpoint, authenticatedRequest) { 4 | 5 | let token = localStorage.getItem('id_token') || null 6 | let config = {} 7 | 8 | if(authenticatedRequest) { 9 | if(token) { 10 | config = { 11 | headers: { 'Authorization': `Bearer ${token}` } 12 | } 13 | } else { 14 | throw new Error("No token saved!") 15 | } 16 | } 17 | 18 | return fetch(API_ROOT + endpoint, config) 19 | .then(response => 20 | response.json() 21 | .then(resource => ({ resource, response })) 22 | ).then(({ resource, response }) => { 23 | if (!response.ok) { 24 | return Promise.reject(resource) 25 | } 26 | return resource 27 | }) 28 | } 29 | 30 | export const CALL_API = Symbol('Call API') 31 | 32 | export default store => next => action => { 33 | 34 | const callAPI = action[CALL_API] 35 | 36 | if (typeof callAPI === 'undefined') { 37 | return next(action) 38 | } 39 | 40 | let { endpoint, types, authenticatedRequest } = callAPI 41 | 42 | if (typeof endpoint !== 'string') { 43 | throw new Error('Specify a string endpoint URL.') 44 | } 45 | 46 | if (!Array.isArray(types) || types.length !== 3) { 47 | throw new Error('Expected an array of three action types.') 48 | } 49 | 50 | if (!types.every(type => typeof type === 'string')) { 51 | throw new Error('Expected action types to be strings.') 52 | } 53 | 54 | function actionWith(data) { 55 | const finalAction = Object.assign({}, action, data) 56 | delete finalAction[CALL_API] 57 | return finalAction 58 | } 59 | 60 | const [ requestType, successType, failureType ] = types 61 | next(actionWith({ type: requestType })) 62 | 63 | return callApi(endpoint, authenticatedRequest).then( 64 | response => next(actionWith({ 65 | response, 66 | authenticatedRequest, 67 | type: successType 68 | })), 69 | error => next(actionWith({ 70 | type: failureType, 71 | error: error.message || 'Error!' 72 | })) 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-auth0", 3 | "version": "0.0.0", 4 | "description": "Redux authentication example", 5 | "scripts": { 6 | "start": "node server.js" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/auth0-blog/redux-auth0.git" 11 | }, 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/auth0-blog/redux-auth0/issues" 15 | }, 16 | "homepage": "https://auth0.com", 17 | "dependencies": { 18 | "babel-polyfill": "^6.3.14", 19 | "humps": "^0.6.0", 20 | "isomorphic-fetch": "^2.1.1", 21 | "jwt-decode": "^2.0.1", 22 | "lodash": "^4.0.0", 23 | "normalizr": "^2.0.0", 24 | "react": "^0.14.7", 25 | "react-dom": "^0.14.7", 26 | "react-redux": "^4.2.1", 27 | "react-router": "2.0.0", 28 | "react-router-redux": "^4.0.0-rc.1", 29 | "redux": "^3.2.1", 30 | "redux-logger": "^2.4.0", 31 | "redux-thunk": "^1.0.3" 32 | }, 33 | "devDependencies": { 34 | "babel-core": "^6.3.15", 35 | "babel-loader": "^6.2.0", 36 | "babel-preset-es2015": "^6.3.13", 37 | "babel-preset-react": "^6.3.13", 38 | "babel-preset-react-hmre": "^1.1.1", 39 | "concurrently": "^0.1.1", 40 | "express": "^4.13.3", 41 | "redux-devtools": "^3.1.0", 42 | "redux-devtools-dock-monitor": "^1.0.1", 43 | "redux-devtools-log-monitor": "^1.0.3", 44 | "webpack": "^1.9.11", 45 | "webpack-dev-middleware": "^1.2.0", 46 | "webpack-hot-middleware": "^2.9.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # React + Redux + Auth0 2 | 3 | This repo goes along with the [tutorial at SitePoint]() and shows how to create a React + Redux app that calls an API and authenticates users with Auth0. 4 | 5 | ![single jedi](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1464890085redux-auth-7.png) 6 | 7 | ## Installation and Running the App 8 | 9 | The app has a server directory that has an Express API for serving up Jedi data. 10 | 11 | If you haven't already done so, [sign up for Auth0](https://auth0.com/signup) and retrieve your credentials from your [management area](https://manage.auth0.com). 12 | 13 | Once you have your credentials, replace the values in `server/server.js` with your secret and client ID, an the values in the Lock instance in `actions/index.js` with your client ID and domain. 14 | 15 | After that, install the dependencies and run the app. 16 | 17 | ```bash 18 | cd server 19 | npm install 20 | node server.js 21 | 22 | cd .. 23 | npm install 24 | npm start 25 | ``` 26 | 27 | ## What is Auth0? 28 | 29 | Auth0 helps you to: 30 | 31 | * Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders), either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce, amont others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory, ADFS or any SAML Identity Provider**. 32 | * Add authentication through more traditional **[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**. 33 | * Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with the same user. 34 | * Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and **flow the user identity** securely. 35 | * Analytics of how, when and where users are logging in. 36 | * Pull data from other sources and add it to the user profile, through [JavaScript rules](https://docs.auth0.com/rules). 37 | 38 | ## Create a free Auth0 account 39 | 40 | 1. Go to [Auth0](https://auth0.com/signup) and click Sign Up. 41 | 2. Use Google, GitHub or Microsoft Account to login. 42 | 43 | ## Issue Reporting 44 | 45 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 46 | 47 | ## Author 48 | 49 | [Auth0](auth0.com) 50 | 51 | ## License 52 | 53 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. -------------------------------------------------------------------------------- /reducers/index.js: -------------------------------------------------------------------------------- 1 | import * as ActionTypes from '../actions' 2 | import { routerReducer as routing } from 'react-router-redux' 3 | import { combineReducers } from 'redux' 4 | const jwtDecode = require('jwt-decode') 5 | 6 | function checkTokenExpiry() { 7 | let jwt = localStorage.getItem('id_token') 8 | if(jwt) { 9 | let jwtExp = jwtDecode(jwt).exp; 10 | let expiryDate = new Date(0); 11 | expiryDate.setUTCSeconds(jwtExp); 12 | 13 | if(new Date() < expiryDate) { 14 | return true; 15 | } 16 | } 17 | return false; 18 | } 19 | 20 | function getProfile() { 21 | return JSON.parse(localStorage.getItem('profile')); 22 | } 23 | 24 | function auth(state = { 25 | isAuthenticated: checkTokenExpiry(), 26 | profile: getProfile(), 27 | error: '' 28 | }, action) { 29 | switch (action.type) { 30 | case ActionTypes.LOGIN_SUCCESS: 31 | return Object.assign({}, state, { 32 | isAuthenticated: true, 33 | profile: action.profile, 34 | error: '' 35 | }) 36 | case ActionTypes.LOGIN_ERROR: 37 | return Object.assign({}, state, { 38 | isAuthenticated: false, 39 | profile: null, 40 | error: action.error 41 | }) 42 | case ActionTypes.LOGOUT_SUCCESS: 43 | return Object.assign({}, state, { 44 | isAuthenticated: false, 45 | profile: null 46 | }) 47 | default: 48 | return state 49 | } 50 | } 51 | 52 | function jedis(state = { 53 | isFetching: false, 54 | allJedis: [], 55 | error: '' 56 | }, action) { 57 | switch (action.type) { 58 | case ActionTypes.JEDIS_REQUEST: 59 | return Object.assign({}, state, { 60 | isFetching: true 61 | }) 62 | case ActionTypes.JEDIS_SUCCESS: 63 | return Object.assign({}, state, { 64 | isFetching: false, 65 | allJedis: action.response, 66 | error: '' 67 | }) 68 | case ActionTypes.JEDIS_FAILURE: 69 | return Object.assign({}, state, { 70 | isFetching: false, 71 | allJedis: [], 72 | error: action.error 73 | }) 74 | default: 75 | return state 76 | } 77 | } 78 | 79 | function jedi(state = { 80 | isFetching: false, 81 | singleJedi: {}, 82 | error: '' 83 | }, action) { 84 | switch (action.type) { 85 | case ActionTypes.JEDI_REQUEST: 86 | return Object.assign({}, state, { 87 | isFetching: true 88 | }) 89 | case ActionTypes.JEDI_SUCCESS: 90 | return Object.assign({}, state, { 91 | isFetching: false, 92 | singleJedi: action.response, 93 | error: '' 94 | }) 95 | case ActionTypes.JEDI_FAILURE: 96 | return Object.assign({}, state, { 97 | isFetching: false, 98 | singleJedi: {}, 99 | error: action.error 100 | }) 101 | default: 102 | return state 103 | } 104 | } 105 | 106 | const rootReducer = combineReducers({ 107 | routing, 108 | auth, 109 | jedis, 110 | jedi 111 | }) 112 | 113 | export default rootReducer 114 | -------------------------------------------------------------------------------- /routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route } from 'react-router' 3 | import App from './containers/App' 4 | 5 | export default ( 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var webpackDevMiddleware = require('webpack-dev-middleware') 3 | var webpackHotMiddleware = require('webpack-hot-middleware') 4 | var config = require('./webpack.config') 5 | 6 | var app = new (require('express'))() 7 | var port = 3000 8 | 9 | var compiler = webpack(config) 10 | app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath })) 11 | app.use(webpackHotMiddleware(compiler)) 12 | 13 | app.use(function(req, res) { 14 | res.sendFile(__dirname + '/index.html') 15 | }) 16 | 17 | app.listen(port, function(error) { 18 | if (error) { 19 | console.error(error) 20 | } else { 21 | console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port) 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-auth-server", 3 | "version": "0.0.0", 4 | "description": "Server for Auth0-Redux example", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Auth0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "cors": "^2.7.1", 13 | "express": "^4.13.4", 14 | "express-jwt": "^3.4.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/public/images/anakin-skywalker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/redux-auth0/639e349bf03325f78e78e3c134c3ee5564c29c98/server/public/images/anakin-skywalker.png -------------------------------------------------------------------------------- /server/public/images/luke-skywalker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/redux-auth0/639e349bf03325f78e78e3c134c3ee5564c29c98/server/public/images/luke-skywalker.jpg -------------------------------------------------------------------------------- /server/public/images/mace-windu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/redux-auth0/639e349bf03325f78e78e3c134c3ee5564c29c98/server/public/images/mace-windu.jpg -------------------------------------------------------------------------------- /server/public/images/obi-wan-kenobi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/redux-auth0/639e349bf03325f78e78e3c134c3ee5564c29c98/server/public/images/obi-wan-kenobi.jpg -------------------------------------------------------------------------------- /server/public/images/yoda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/redux-auth0/639e349bf03325f78e78e3c134c3ee5564c29c98/server/public/images/yoda.png -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const jwt = require('express-jwt'); 4 | const cors = require('cors'); 5 | 6 | app.use(cors()); 7 | app.use(express.static('public')); 8 | 9 | // Authentication middleware provided by express-jwt. 10 | // This middleware will check incoming requests for a valid 11 | // JWT on any routes that it is applied to. 12 | const authCheck = jwt({ 13 | secret: 'AUTH0_SECRET', 14 | // If your Auth0 client was created before Dec 6, 2016, 15 | // uncomment the line below and remove the line above 16 | // secret: new Buffer('AUTH0_SECRET', 'base64'), 17 | audience: 'AUTH0_CLIENT_ID' 18 | }); 19 | 20 | var jedis = [ 21 | { 22 | id: 1, 23 | name: 'Luke Skywalker', 24 | image: 'http://localhost:7000/images/luke-skywalker.jpg' 25 | }, 26 | { 27 | id: 2, 28 | name: 'Anakin Skywalker', 29 | image: 'http://localhost:7000/images/anakin-skywalker.png' 30 | }, 31 | { 32 | id: 3, 33 | name: 'Yoda', 34 | image: 'http://localhost:7000/images/yoda.png' 35 | }, 36 | { 37 | id: 4, 38 | name: 'Obi-Wan Kenobi', 39 | image: 'http://localhost:7000/images/obi-wan-kenobi.jpg' 40 | }, 41 | { 42 | id: 5, 43 | name: 'Mace Windu', 44 | image: 'http://localhost:7000/images/mace-windu.jpg' 45 | } 46 | ]; 47 | 48 | app.get('/api/jedis', (req, res) => { 49 | const allJedis = jedis.map(jedi => { 50 | return { id: jedi.id, name: jedi.name } 51 | }); 52 | res.json(allJedis); 53 | }); 54 | 55 | app.get('/api/jedis/:id', authCheck, (req, res) => { 56 | res.json(jedis.filter(jedi => jedi.id === parseInt(req.params.id))[0]); 57 | }); 58 | 59 | app.listen(7000); 60 | console.log('Listening on http://localhost:7000'); -------------------------------------------------------------------------------- /store/configureStore.dev.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import createLogger from 'redux-logger' 4 | import api from '../middleware/api' 5 | import rootReducer from '../reducers' 6 | import DevTools from '../containers/DevTools' 7 | 8 | export default function configureStore(preloadedState) { 9 | const store = createStore( 10 | rootReducer, 11 | preloadedState, 12 | compose( 13 | applyMiddleware(thunk, api, createLogger()), 14 | DevTools.instrument() 15 | ) 16 | ) 17 | 18 | if (module.hot) { 19 | // Enable Webpack hot module replacement for reducers 20 | module.hot.accept('../reducers', () => { 21 | const nextRootReducer = require('../reducers').default 22 | store.replaceReducer(nextRootReducer) 23 | }) 24 | } 25 | 26 | return store 27 | } 28 | -------------------------------------------------------------------------------- /store/configureStore.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === 'production') { 2 | module.exports = require('./configureStore.prod') 3 | } else { 4 | module.exports = require('./configureStore.dev') 5 | } 6 | -------------------------------------------------------------------------------- /store/configureStore.prod.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import api from '../middleware/api' 4 | import rootReducer from '../reducers' 5 | 6 | export default function configureStore(preloadedState) { 7 | return createStore( 8 | rootReducer, 9 | preloadedState, 10 | applyMiddleware(thunk, api) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | devtool: 'cheap-module-eval-source-map', 6 | entry: [ 7 | 'webpack-hot-middleware/client', 8 | './index' 9 | ], 10 | output: { 11 | path: path.join(__dirname, 'dist'), 12 | filename: 'bundle.js', 13 | publicPath: '/static/' 14 | }, 15 | plugins: [ 16 | new webpack.optimize.OccurenceOrderPlugin(), 17 | new webpack.HotModuleReplacementPlugin() 18 | ], 19 | module: { 20 | loaders: [ 21 | { 22 | test: /\.js$/, 23 | loaders: [ 'babel' ], 24 | exclude: /node_modules/, 25 | include: __dirname 26 | } 27 | ] 28 | } 29 | } 30 | --------------------------------------------------------------------------------