├── .env.example
├── .gitignore
├── README.md
├── _config.yml
├── assets
└── img
│ └── logo.svg
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── components
│ ├── App.js
│ ├── Home.js
│ ├── Loading.js
│ ├── LoginTransition.js
│ └── shared
│ │ ├── NotFound.js
│ │ ├── Routes.js
│ │ ├── _TemplateComponent.js
│ │ └── _TemplateStaticHTML.js
├── css
│ ├── style.css
│ └── style.styl
├── index.js
└── utils
│ ├── API.js
│ ├── AuthService.js
│ ├── JSONDebugger.js
│ └── init.js
└── static.json
/.env.example:
--------------------------------------------------------------------------------
1 | REACT_APP_EXAMPLE=example
2 | REACT_APP_SOMETHING_ELSE=1
3 |
4 | REACT_APP_API_URL=example
5 | REACT_APP_AUTH0_CLIENT_ID={CLIENT_ID}
6 | REACT_APP_AUTH0_DOMAIN={DOMAIN}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # testing
7 | coverage
8 |
9 | # production
10 | build
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | npm-debug.log
16 |
17 | .vscode
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Demo
2 | ====
3 |
4 | Live site : [http://react-auth0-api.herokuapp.com/](http://react-auth0-api.herokuapp.com/)
5 |
6 | 
7 |
8 | This repository is the React front-end framework that interacts with a Rails API server for my step by step publication at [https://goo.gl/ffaWwr](https://goo.gl/ffaWwr)
9 |
10 | A companion Rails API server can be found at
11 | [https://github.com/iankhor/ror-auth0-api](https://github.com/iankhor/ror-auth0-api)
12 |
13 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/assets/img/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-auth0-api",
3 | "license": "MIT",
4 | "version": "0.1.0",
5 | "devDependencies": {
6 | "autoprefixer-stylus": "^0.13.0",
7 | "concurrently": "^3.1.0",
8 | "react-scripts": "0.8.5",
9 | "stylus": "^0.54.5"
10 | },
11 | "dependencies": {
12 | "auth0-lock": "^10.13.0",
13 | "axios": "^0.15.3",
14 | "bootstrap": "^4.0.0-alpha.6",
15 | "history": "^4.5.1",
16 | "react": "^15.4.2",
17 | "react-addons-css-transition-group": "^15.4.2",
18 | "react-addons-transition-group": "^15.4.2",
19 | "react-dom": "^15.4.2",
20 | "react-router": "4.0.0-beta.7",
21 | "react-router-dom": "v4.0.0-beta.7",
22 | "reactstrap": "^4.3.0"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/iankhor/react-auth0-api.git"
27 | },
28 | "scripts": {
29 | "start": "PORT=9000 react-scripts start",
30 | "watch": "concurrently --names \"webpack, stylus\" --prefix name \"npm run start\" \"npm run styles:watch\"",
31 | "styles": "stylus -u autoprefixer-stylus ./src/css/style.styl -o ./src/css/style.css",
32 | "styles:watch": "stylus -u autoprefixer-stylus -w ./src/css/style.styl -o ./src/css/style.css",
33 | "build": "react-scripts build",
34 | "test": "react-scripts test --env=jsdom",
35 | "eject": "react-scripts eject"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iankhor/react-auth0-api/0ad6cc0413c5d9391e2be5fb3a6ba2e7e3df420a/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | React + Auth0 + Rails API
15 |
16 |
17 |
18 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import logo from '../../assets/img/logo.svg'
3 | import '../css/style.css'
4 | import Home from './Home'
5 | import { auth } from './../utils/init'
6 |
7 | class App extends Component {
8 | constructor(props){
9 | super(props)
10 |
11 | this.state = {
12 | isLoggedIn: auth.loggedIn(),
13 | }
14 | }
15 |
16 | render() {
17 | return (
18 |
19 |
20 |

21 |
Welcome to React + Auth0 + Rails API
22 |
23 |
24 |
29 |
30 |
31 | )
32 | }
33 | }
34 |
35 | export default App
36 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import JSONDebugger from '../utils/JSONDebugger'
3 | import { pingApiServer,
4 | fetchProfilesWithAuth,
5 | fetchProfilesNoAuth } from './../utils/API'
6 | import { Container,
7 | Row,
8 | Col,
9 | Button,
10 | ButtonGroup } from 'reactstrap'
11 |
12 | import Loading from './Loading'
13 |
14 | class Home extends Component {
15 | constructor(props){
16 | super(props)
17 |
18 | this.state = { status: "Idle",
19 | pingData: "No data from server yet",
20 | profileData: "No data from server yet"
21 | }
22 | }
23 |
24 | resetStates = () => {
25 | this.setState({
26 | status: "Idle",
27 | pingData: "No data from server yet",
28 | profileData: "No data from server yet"
29 | })
30 | }
31 |
32 | renderLoading = () => {
33 | switch(this.state.status) {
34 | case "Fetching":
35 | return
36 | default:
37 | return null
38 | }
39 | }
40 |
41 | pingApi = () => {
42 | this.setState( { status: "Fetching" })
43 | pingApiServer()
44 | .then( data => {
45 | this.setState({ status: "Fetch completed", pingData: data} )
46 | } )
47 | }
48 |
49 | _fetchProfilesNoAuth = () => {
50 | this.setState( { status: "Fetching" })
51 | fetchProfilesNoAuth()
52 | .then( data => {
53 | this.setState({ status: "Fetch completed", profileData: data} )
54 | })
55 | }
56 |
57 | _fetchProfilesWithAuth = () => {
58 | this.setState( { status: "Fetching" })
59 | fetchProfilesWithAuth(this.props.token)
60 | .then( data => {
61 | this.setState({ status: "Fetch completed", profileData: data} )
62 | })
63 | }
64 |
65 | renderSignInUp = () => {
66 | return
67 | }
68 |
69 | renderLogOut = () => {
70 | return (
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | )
93 | }
94 |
95 | render(){
96 | return(
97 |
98 |
99 |
100 | { this.props.isLoggedIn ? this.renderLogOut() : this.renderSignInUp() }
101 |
102 |
103 | { this.renderLoading() }
104 | { }
105 |
106 | )
107 | }
108 | }
109 |
110 | export default Home
111 |
112 |
--------------------------------------------------------------------------------
/src/components/Loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Loading = (props) => {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
10 |
11 | export default Loading
--------------------------------------------------------------------------------
/src/components/LoginTransition.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Container, Row, Col } from 'reactstrap'
3 | import Loading from './Loading'
4 |
5 | class LoginTransition extends Component {
6 | render(){
7 | return(
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | )
17 | }
18 | }
19 |
20 | export default LoginTransition
21 |
22 |
--------------------------------------------------------------------------------
/src/components/shared/NotFound.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class NotFound extends Component {
4 | render(){
5 | return(
6 |
7 |
Not Found page
8 |
9 | )
10 | }
11 | }
12 |
13 | export default NotFound
14 |
15 |
--------------------------------------------------------------------------------
/src/components/shared/Routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | //Routes
4 | import NotFound from './NotFound'
5 | import App from './../App';
6 | import LoginTransition from './../LoginTransition';
7 |
8 | import { BrowserRouter, Route, Switch } from 'react-router-dom'
9 |
10 | const Routes = (props) => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Routes
--------------------------------------------------------------------------------
/src/components/shared/_TemplateComponent.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import StaticHTMLTemplate from './_TemplateStaticHTML'
3 |
4 | class TemplateComponent extends Component {
5 | render(){
6 | return(
7 |
8 |
This is a componentTemplate page
9 | ...
10 |
11 |
12 | )
13 | }
14 | }
15 |
16 | export default TemplateComponent
17 |
18 |
--------------------------------------------------------------------------------
/src/components/shared/_TemplateStaticHTML.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const StaticHTMLTemplate = (props) => {
4 | return (
5 |
6 |
This is from StaticHTMLTemplate
7 |
8 | )
9 | }
10 |
11 | export default StaticHTMLTemplate
--------------------------------------------------------------------------------
/src/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | font-size: 2em;
3 | }
4 | .color-silver {
5 | background-color: #ddd;
6 | }
7 | .color-font-aqua {
8 | color: #7fdbff;
9 | }
10 | .color-aqua {
11 | background-color: #7fdbff;
12 | }
13 | .generic-center {
14 | margin: auto;
15 | padding: 2rem;
16 | border: 2px solid #000;
17 | text-align: center;
18 | }
19 | .dummy-height {
20 | min-height: 500px;
21 | }
22 | .border {
23 | border: 2px solid #000;
24 | }
25 | .buttons {
26 | border: 2px solid #000;
27 | padding: 20px;
28 | }
29 | .borderless {
30 | border: none;
31 | }
32 | .App {
33 | text-align: center;
34 | }
35 | .App-logo {
36 | -webkit-animation: App-logo-spin infinite 20s linear;
37 | animation: App-logo-spin infinite 20s linear;
38 | height: 80px;
39 | }
40 | .App-header {
41 | height: 150px;
42 | padding: 20px;
43 | }
44 | .App-intro {
45 | font-size: large;
46 | }
47 | .login-transition {
48 | text-align: center;
49 | padding-top: 50vh;
50 | }
51 | .debugger {
52 | text-align: left;
53 | }
54 | @-webkit-keyframes App-logo-spin {
55 | from {
56 | -webkit-transform: rotate(0deg);
57 | transform: rotate(0deg);
58 | }
59 | to {
60 | -webkit-transform: rotate(360deg);
61 | transform: rotate(360deg);
62 | }
63 | }
64 | @keyframes App-logo-spin {
65 | from {
66 | -webkit-transform: rotate(0deg);
67 | transform: rotate(0deg);
68 | }
69 | to {
70 | -webkit-transform: rotate(360deg);
71 | transform: rotate(360deg);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/css/style.styl:
--------------------------------------------------------------------------------
1 | h1
2 | font-size 2em
3 |
4 | .color-silver
5 | background-color #DDDDDD
6 |
7 | .color-font-aqua
8 | color #7FDBFF
9 |
10 | .color-aqua
11 | background-color #7FDBFF
12 |
13 | .generic-center
14 | margin auto
15 | padding 2rem
16 | border 2px solid #000
17 | text-align center
18 |
19 | .dummy-height
20 | min-height 500px
21 |
22 | .border
23 | border 2px solid #000
24 |
25 | .buttons
26 | border 2px solid #000
27 | padding 20px
28 |
29 |
30 | .borderless
31 | border none
32 |
33 | // App.css
34 | .App
35 | text-align center
36 |
37 |
38 | .App-logo
39 | animation App-logo-spin infinite 20s linear
40 | height 80px
41 |
42 |
43 | .App-header
44 | height 150px
45 | padding 20px
46 |
47 | .App-intro
48 | font-size large
49 |
50 | .login-transition
51 | text-align center
52 | padding-top 50vh
53 |
54 | .debugger
55 | text-align left
56 |
57 | @keyframes App-logo-spin
58 | from { transform: rotate(0deg) }
59 | to { transform: rotate(360deg) }
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import 'bootstrap/dist/css/bootstrap.css'
4 |
5 | // Routes
6 | import Routes from './components/shared/Routes'
7 |
8 | ReactDOM.render(
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/src/utils/API.js:
--------------------------------------------------------------------------------
1 | import { api } from './init'
2 |
3 | export function pingApiServer(){
4 | return api.get('/')
5 | .then(function (response) {
6 | return response.data
7 | })
8 | .catch(function (error) {
9 | return error.response.data
10 | })
11 | }
12 |
13 | export function fetchProfilesNoAuth(){
14 | return api.get('/profiles')
15 | .then(function (response) {
16 | return response.data
17 | })
18 | .catch(function (error) {
19 | return error.response.data
20 | })
21 | }
22 |
23 | export function fetchProfilesWithAuth(token){
24 | return api.get('/profiles', { headers: { 'Authorization': 'Bearer ' + token } })
25 | .then(function (response) {
26 | return response.data
27 | })
28 | .catch(function (error) {
29 | return error.response.data
30 | })
31 | }
--------------------------------------------------------------------------------
/src/utils/AuthService.js:
--------------------------------------------------------------------------------
1 | import Auth0Lock from 'auth0-lock'
2 | import logo from './../../assets/img/logo.svg'
3 |
4 | export default class AuthService {
5 | constructor(clientId, domain) {
6 | // Configure Auth0
7 | this.lock = new Auth0Lock(clientId, domain, {
8 | auth: {
9 | redirectUrl: `${window.location.origin}/auth`,
10 | responseType: 'token'
11 | },
12 | theme: {
13 | logo: logo,
14 | primaryColor: '#7FDBFF'
15 | },
16 | languageDictionary: {
17 | title: "React + Auth0 + Rails API"
18 | }
19 | })
20 | // Add callback for lock `authenticated` event
21 | this.lock.on('authenticated', this._doAuthentication.bind(this))
22 | // binds login functions to keep this context
23 | this.login = this.login.bind(this)
24 | }
25 |
26 | _doAuthentication(authResult) {
27 | // Saves the user token
28 | this.setToken(authResult.idToken)
29 | // navigate to the home route
30 |
31 | // temp hack
32 | location.replace("/");
33 |
34 | }
35 |
36 | login() {
37 | // Call the show method to display the widget.
38 | this.lock.show()
39 | }
40 |
41 | loggedIn() {
42 | // Checks if there is a saved token and it's still valid
43 | return !!this.getToken()
44 | }
45 |
46 | setToken(idToken) {
47 | // Saves user token to local storage
48 | localStorage.setItem('id_token', idToken)
49 | }
50 |
51 | getToken() {
52 | // Retrieves the user token from local storage
53 | return localStorage.getItem('id_token')
54 | }
55 |
56 | logout() {
57 | // Clear user token and profile data from local storage
58 | localStorage.removeItem('id_token');
59 |
60 | // temp hack
61 | location.replace("/");
62 | }
63 | }
--------------------------------------------------------------------------------
/src/utils/JSONDebugger.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class JSONDebugger extends Component {
4 | render(){
5 | return(
6 |
7 | JSON Debugger
8 | {JSON.stringify(this.props.json, null, 2)}
9 |
10 | )
11 | }
12 | }
13 |
14 | export default JSONDebugger
15 |
16 |
--------------------------------------------------------------------------------
/src/utils/init.js:
--------------------------------------------------------------------------------
1 | import AuthService from './AuthService'
2 | import axios from 'axios'
3 |
4 | export const auth = new AuthService(
5 | process.env.REACT_APP_AUTH0_CLIENT_ID,
6 | process.env.REACT_APP_AUTH0_DOMAIN)
7 |
8 | export const api = axios.create({
9 | baseURL: process.env.REACT_APP_API_URL
10 | })
11 |
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "build/",
3 | "clean_urls": false,
4 | "routes": {
5 | "/**": "index.html"
6 | },
7 | "headers": {
8 | "/**": {
9 | "Strict-Transport-Security": "max-age=7776000"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------