Skip to content

Commit 0e997d8

Browse files
committed
first commit
0 parents  commit 0e997d8

33 files changed

+1110
-0
lines changed

.babelrc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"presets": ["es2015", "stage-0", "react"],
3+
"env": {
4+
"development": {
5+
"presets": ["react-hmre"]
6+
},
7+
"production": {
8+
"presets": []
9+
},
10+
"test": {
11+
"presets": []
12+
}
13+
}
14+
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
.env
3+
npm-debug.log
4+
dist
5+
.DS_Store

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# React Authentication for Front End Masters
2+
3+
## Running the App
4+
5+
Install the dependencies:
6+
7+
```bash
8+
npm install
9+
```
10+
11+
Start the app:
12+
13+
```bash
14+
npm start
15+
```
16+
17+
The app will be served at `localhost:3000`.

karma.conf.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
var argv = require('yargs').argv;
2+
var path = require('path');
3+
4+
var webpackConfig = require('./webpack.config');
5+
6+
module.exports = function(config) {
7+
config.set({
8+
basePath: '',
9+
frameworks: ['mocha', 'chai'],
10+
files: [
11+
'tests.webpack.js'
12+
],
13+
14+
preprocessors: {
15+
// add webpack as preprocessor
16+
'tests.webpack.js': ['webpack', 'sourcemap'],
17+
},
18+
19+
webpack: webpackConfig,
20+
webpackServer: {
21+
noInfo: true
22+
},
23+
24+
plugins: [
25+
'karma-mocha',
26+
'karma-chai',
27+
'karma-webpack',
28+
'karma-phantomjs-launcher',
29+
'karma-spec-reporter',
30+
'karma-sourcemap-loader'
31+
],
32+
33+
reporters: ['spec'],
34+
port: 9876,
35+
colors: true,
36+
logLevel: config.LOG_INFO,
37+
browsers: ['PhantomJS'],
38+
singleRun: !argv.watch
39+
})
40+
};

package.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{
2+
"name": "auth0-react-sample",
3+
"version": "1.0.0",
4+
"description": "A minimal reactJS sample application showing auth0 integration",
5+
"repository": "https://github.com/auth0-samples/auth0-react-sample",
6+
"license": "MIT",
7+
"scripts": {
8+
"start": "npm-run-all --parallel dev-server",
9+
"dev-server": "NODE_ENV=development cross-env hjs-dev-server",
10+
"clean": "rimraf dist",
11+
"build": "npm run clean && cross-env NODE_ENV=production webpack",
12+
"publish_pages": "gh-pages -d dist",
13+
"ghpages": "npm run build && npm run publish_pages",
14+
"test": "cross-env NODE_ENV=test karma start karma.conf.js",
15+
"test:watch": "npm run test -- --watch"
16+
},
17+
"devDependencies": {
18+
"autoprefixer": "^6.3.6",
19+
"babel-core": "^6.7.7",
20+
"babel-loader": "^6.2.4",
21+
"babel-plugin-transform-es2015-modules-umd": "^6.8.0",
22+
"babel-polyfill": "^6.7.4",
23+
"babel-preset-es2015": "^6.6.0",
24+
"babel-preset-react": "^6.5.0",
25+
"babel-preset-react-hmre": "^1.1.1",
26+
"babel-preset-stage-0": "^6.5.0",
27+
"babel-register": "^6.7.2",
28+
"chai": "^3.5.0",
29+
"chai-enzyme": "^0.4.2",
30+
"cheerio": "^0.20.0",
31+
"cross-env": "^1.0.8",
32+
"css-loader": "^0.23.1",
33+
"cssnano": "^3.5.2",
34+
"dotenv": "^2.0.0",
35+
"enzyme": "^2.2.0",
36+
"expect": "^1.18.0",
37+
"file-loader": "^0.8.5",
38+
"gh-pages": "^0.11.0",
39+
"hjs-webpack": "^8.1.0",
40+
"jasmine-core": "^2.4.1",
41+
"json-loader": "^0.5.4",
42+
"karma": "^0.13.22",
43+
"karma-chai": "^0.1.0",
44+
"karma-jasmine": "^0.3.8",
45+
"karma-mocha": "^1.0.1",
46+
"karma-phantomjs-launcher": "^1.0.0",
47+
"karma-sourcemap-loader": "^0.3.7",
48+
"karma-spec-reporter": "0.0.26",
49+
"karma-webpack": "^1.7.0",
50+
"mocha": "^2.4.5",
51+
"npm-run-all": "^2.3.0",
52+
"phantomjs-polyfill": "0.0.2",
53+
"phantomjs-prebuilt": "^2.1.7",
54+
"postcss-loader": "^0.9.1",
55+
"precss": "^1.4.0",
56+
"prettyjson": "^1.1.3",
57+
"react-addons-test-utils": "^15.0.2",
58+
"sinon": "^1.17.4",
59+
"style-loader": "^0.13.1",
60+
"transform-loader": "^0.2.3",
61+
"url-loader": "^0.5.7",
62+
"webpack": "^1.13.0",
63+
"yargs": "^4.7.1"
64+
},
65+
"dependencies": {
66+
"auth0-lock": "^10.2.2",
67+
"bootstrap": "^3.3.7",
68+
"classnames": "^2.2.5",
69+
"express-jwt": "^5.0.0",
70+
"jwt-decode": "^2.1.0",
71+
"md5": "^2.2.1",
72+
"react": "^15.3.1",
73+
"react-bootstrap": "^0.30.0-rc.1",
74+
"react-dom": "^15.3.1",
75+
"react-router": "^2.8.0",
76+
"react-router-bootstrap": "^0.23.1"
77+
}
78+
}

scripts/prepublish.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
3+
echo "=> Compiling..."
4+
echo ""
5+
rm -rf ./dist
6+
NODE_ENV=production ./node_modules/.bin/webpack
7+
echo ""
8+
echo "=> Complete"

src/app.css

Whitespace-only changes.

src/app.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
4+
import 'bootstrap/dist/css/bootstrap.css'
5+
import './app.css'
6+
7+
import App from 'containers/App/App'
8+
9+
import {browserHistory} from 'react-router'
10+
import makeRoutes from './routes'
11+
12+
const routes = makeRoutes()
13+
14+
const mountNode = document.querySelector('#root');
15+
ReactDOM.render(
16+
<App history={browserHistory}
17+
routes={routes} />,
18+
mountNode)

src/containers/App/App.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { PropTypes } from 'react'
2+
import { Router } from 'react-router'
3+
4+
class App extends React.Component {
5+
static contextTypes = {
6+
router: PropTypes.object
7+
}
8+
9+
static propTypes = {
10+
history: PropTypes.object.isRequired,
11+
routes: PropTypes.element.isRequired
12+
}
13+
14+
get content() {
15+
return (
16+
<Router
17+
routes={this.props.routes}
18+
history={this.props.history} />
19+
)
20+
}
21+
22+
render () {
23+
return (
24+
<div style={{ height: '100%' }}>
25+
{this.content}
26+
</div>
27+
)
28+
}
29+
}
30+
31+
export default App

src/containers/App/App.spec.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react'
2+
import { expect } from 'chai'
3+
import { shallow } from 'enzyme'
4+
5+
import App from './App'
6+
import styles from './styles.module.css'
7+
8+
describe('<App />', () => {
9+
let wrapper
10+
let history = {}
11+
beforeEach(() => {
12+
wrapper =
13+
shallow(<App history={history}/>)
14+
})
15+
16+
it('has a Router component', () => {
17+
expect(wrapper.find('Router'))
18+
.to.have.length(1)
19+
})
20+
21+
it('passes a history prop', () => {
22+
const props = wrapper.find('Router').props();
23+
24+
expect(props.history)
25+
.to.be.defined
26+
})
27+
28+
})

src/containers/App/styles.module.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.wrapper {
2+
display: flex;
3+
}

src/routes.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react'
2+
import {browserHistory, Router, Route, Redirect} from 'react-router'
3+
4+
import makeMainRoutes from './views/Main/routes'
5+
6+
export const makeRoutes = () => {
7+
const main = makeMainRoutes()
8+
9+
return (
10+
<Route path=''>
11+
{main}
12+
</Route>
13+
)
14+
}
15+
16+
export default makeRoutes

src/styles/base.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:root {
2+
--topbar-height: 80px;
3+
--padding: 25px;
4+
}

src/styles/colors.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:root {
2+
--dark: #404040;
3+
--light-gray: #a2a2a2;
4+
--white: #ffffff;
5+
--highlight: #48b5e9;
6+
--heading-color: var(--highlight);
7+
}

src/styles/queries.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@custom-media --screen-phone (width <= 35.5em);
2+
@custom-media --screen-phone-lg (width > 35.5em);
3+
4+
@custom-media --screen-sm var(--screen-phone) and (width < 48em);
5+
@custom-media --screen-md (width >= 48em) and (width < 64em);
6+
@custom-media --screen-lg (width >= 64em) and (width < 80em);
7+
@custom-media --screen-xl (width >= 80em);

src/utils/AuthService.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { EventEmitter } from 'events'
2+
import { isTokenExpired } from './jwtHelper'
3+
import { browserHistory } from 'react-router'
4+
import jwtDecode from 'jwt-decode'
5+
6+
import { API_URL } from './constants'
7+
8+
export default class AuthService extends EventEmitter {
9+
constructor() {
10+
super()
11+
12+
// binds login functions to keep this context
13+
this.login = this.login.bind(this)
14+
}
15+
16+
_doAuthentication(endpoint, values) {
17+
return this.fetch(`${API_URL}/${endpoint}`, {
18+
method: 'POST',
19+
body: JSON.stringify(values),
20+
headers: { 'Content-Type': 'application/json' }
21+
})
22+
}
23+
24+
login(user, password) {
25+
return this._doAuthentication('users/authenticate', { user, password })
26+
}
27+
28+
signup(username, email, password) {
29+
return this._doAuthentication('users', { username, email, password })
30+
}
31+
32+
isAuthenticated() {
33+
// Checks if there is a saved token and it's still valid
34+
const token = localStorage.getItem('token')
35+
if (token) {
36+
return !isTokenExpired(token)
37+
} else {
38+
return false
39+
}
40+
}
41+
42+
isAdmin() {
43+
return jwtDecode(this.getToken()).scope === 'admin'
44+
}
45+
46+
finishAuthentication(token) {
47+
localStorage.setItem('token', token)
48+
}
49+
50+
getToken() {
51+
// Retrieves the user token from localStorage
52+
return localStorage.getItem('token')
53+
}
54+
55+
logout() {
56+
// Clear user token and profile data from localStorage
57+
localStorage.removeItem('token')
58+
}
59+
60+
_checkStatus(response) {
61+
// raises an error in case response status is not a success
62+
if (response.status >= 200 && response.status < 300) {
63+
return response
64+
} else {
65+
var error = new Error(response.statusText)
66+
error.response = response
67+
return error
68+
}
69+
}
70+
71+
fetch(url, options) {
72+
// performs api calls sending the required authentication headers
73+
const headers = {
74+
'Accept': 'application/json',
75+
'Content-Type': 'application/json'
76+
}
77+
78+
if (this.isAuthenticated()) {
79+
headers['Authorization'] = 'Bearer ' + this.getToken()
80+
}
81+
82+
return fetch(url, {
83+
headers,
84+
...options
85+
})
86+
.then(response => response.json())
87+
}
88+
}

src/utils/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const API_URL = 'https://user-authentication-api-ocokqryugz.now.sh/api'

0 commit comments

Comments
 (0)