working login

This commit is contained in:
Antti Pilto 2016-07-28 20:56:13 +03:00
parent cfa55f5cf1
commit df40e5d37a
10 changed files with 240 additions and 15 deletions

View file

@ -24,6 +24,7 @@
"babel-preset-es2015": "^6.9.0", "babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.11.1", "babel-preset-react": "^6.11.1",
"css-loader": "^0.23.1", "css-loader": "^0.23.1",
"dotenv": "^2.0.0",
"file-loader": "^0.9.0", "file-loader": "^0.9.0",
"html-webpack-plugin": "^2.22.0", "html-webpack-plugin": "^2.22.0",
"postcss": "^5.0.21", "postcss": "^5.0.21",
@ -36,6 +37,8 @@
"webpack-dev-server": "^1.14.1" "webpack-dev-server": "^1.14.1"
}, },
"dependencies": { "dependencies": {
"auth0-lock": "^10.0.0",
"jwt-decode": "^2.1.0",
"postcss": "^5.1.0", "postcss": "^5.1.0",
"react": "^15.2.1", "react": "^15.2.1",
"react-dom": "^15.2.1" "react-dom": "^15.2.1"

View file

@ -1,12 +1,54 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import AuthService from '../../utils/AuthService'
import './App.scss'; import './App.scss';
import MainMenu from '../MainMenu/MainMenu';
import Login from '../Login/Login';
const options = {
auth: {
params: {
redirectUrl: 'http://localhost:8080',
responseType: 'token'
}
}
};
const auth = new AuthService(__AUTH0_CLIENT_ID__, __AUTH0_DOMAIN__, options);
class App extends Component { class App extends Component {
constructor(props) {
super(props);
this.state = {
profile: auth.getProfile()
}
auth.on('profile_updated', (newProfile) => {
this.setState({profile: newProfile})
})
this.loginButton = this.loginButton.bind(this);
}
logout() {
auth.logout()
this.setState({
profile: auth.getProfile()
})
}
loginButton() {
if(this.state.profile.hasOwnProperty('user_id'))
return <button className="btn-primary" onClick={this.logout.bind(this)}>Log out</button>;
else
return <button className="btn-primary" onClick={auth.login.bind(this)}>Login</button>;
}
render() { render() {
return ( return (
<div>hello world</div> <div>
<MainMenu profile={this.state.profile} />
{this.loginButton()}
</div>
) )
} }
} }
// <Login profile={this.state.profile} />
export default App; export default App;

View file

@ -0,0 +1,36 @@
import React, { Component, PropTypes as T } from 'react';
import AuthService from '../../utils/AuthService'
import './Login.scss';
class Login extends Component {
constructor(props) {
super(props);
this.loginButton = this.loginButton.bind(this);
}
logout() {
/* this.props.auth.logout()
this.setState({
profile: this.props.auth.getProfile()
}) */
}
loginButton() {
if(this.props.profile.hasOwnProperty('user_id'))
return <button className="btn-primary" onClick={this.logout.bind(this)}>Log out</button>;
else
return <button className="btn-primary" onClick={this.props.auth.login.bind(this)}>Login</button>;
}
render() {
return (
<div>{this.loginButton()}</div>
)
}
}
Login.propTypes = {
auth: T.instanceOf(AuthService)
};
export default Login;

View file

View file

@ -0,0 +1,12 @@
import React, { Component } from 'react';
import './MainMenu.scss';
class MainMenu extends Component {
render() {
return (
<div>{this.props.profile.hasOwnProperty('user_id')?this.props.profile.name+' is logged in':'not logged in'}</div>
)
}
}
export default MainMenu;

View file

75
src/utils/AuthService.js Normal file
View file

@ -0,0 +1,75 @@
import { EventEmitter } from 'events'
import { isTokenExpired } from './jwtHelper'
import Auth0Lock from 'auth0-lock'
export default class AuthService extends EventEmitter {
constructor(clientId, domain, options) {
super()
// Configure Auth0
this.lock = new Auth0Lock(clientId, domain, options)
// Add callback for lock `authenticated` event
this.lock.on('authenticated', this._doAuthentication.bind(this))
// Add callback for lock `authorization_error` event
this.lock.on('authorization_error', this._authorizationError.bind(this))
// binds login functions to keep this context
this.login = this.login.bind(this)
}
_doAuthentication(authResult){
// Saves the user token
this.setToken(authResult.idToken)
// Async loads the user profile data
this.lock.getProfile(authResult.idToken, (error, profile) => {
if (error) {
console.log('Error loading the Profile', error)
} else {
this.setProfile(profile)
}
})
}
_authorizationError(error){
// Unexpected authentication error
console.log('Authentication Error', error)
}
login() {
// Call the show method to display the widget.
this.lock.show()
}
loggedIn(){
// Checks if there is a saved token and it's still valid
const token = this.getToken()
return !!token && !isTokenExpired(token)
}
setProfile(profile){
// Saves profile data to localStorage
localStorage.setItem('profile', JSON.stringify(profile))
// Triggers profile_updated event to update the UI
this.emit('profile_updated', profile)
}
getProfile(){
// Retrieves the profile data from localStorage
const profile = localStorage.getItem('profile')
return profile ? JSON.parse(localStorage.profile) : {}
}
setToken(idToken){
// Saves user token to localStorage
localStorage.setItem('id_token', idToken)
}
getToken(){
// Retrieves the user token from localStorage
return localStorage.getItem('id_token')
}
logout(){
// Clear user token and profile data from localStorage
localStorage.removeItem('id_token');
localStorage.removeItem('profile');
}
}

21
src/utils/jwtHelper.js Normal file
View file

@ -0,0 +1,21 @@
import decode from 'jwt-decode';
export function getTokenExpirationDate(token){
const decoded = decode(token)
if(!decoded.exp) {
return null
}
const date = new Date(0) // The 0 here is the key, which sets the date to the epoch
date.setUTCSeconds(decoded.exp)
return date
}
export function isTokenExpired(token){
const date = getTokenExpirationDate(token)
const offsetSeconds = 0
if (date === null) {
return false
}
return !(date.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)))
}

View file

@ -1,8 +1,25 @@
var path = require('path'); const NODE_ENV = 'development';
var webpack = require('webpack'); const dotenv = require('dotenv');
var autoprefixer = require('autoprefixer');
var precss = require('precss'); const webpack = require('webpack');
var HtmlWebPackPlugin = require('html-webpack-plugin'); const fs = require('fs');
const path = require('path');
const autoprefixer = require('autoprefixer');
const precss = require('precss');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const dotEnvVars = dotenv.config();
const envVariables =
Object.assign({}, dotEnvVars);
const defines =
Object.keys(envVariables)
.reduce((memo, key) => {
const val = JSON.stringify(envVariables[key]);
memo[`__${key.toUpperCase()}__`] = val;
return memo;
}, {
__NODE_ENV__: JSON.stringify(NODE_ENV)
});
module.exports = { module.exports = {
entry: [ entry: [
@ -24,7 +41,8 @@ module.exports = {
filename: '../index.html' filename: '../index.html'
}), }),
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin() new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin(defines)
], ],
module: { module: {
loaders: [ loaders: [

View file

@ -1,8 +1,25 @@
var path = require('path'); const NODE_ENV = 'production';
var webpack = require('webpack'); const dotenv = require('dotenv');
var autoprefixer = require('autoprefixer');
var precss = require('precss'); const webpack = require('webpack');
var HtmlWebPackPlugin = require('html-webpack-plugin'); const fs = require('fs');
const path = require('path');
const autoprefixer = require('autoprefixer');
const precss = require('precss');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const dotEnvVars = dotenv.config();
const envVariables =
Object.assign({}, dotEnvVars);
const defines =
Object.keys(envVariables)
.reduce((memo, key) => {
const val = JSON.stringify(envVariables[key]);
memo[`__${key.toUpperCase()}__`] = val;
return memo;
}, {
__NODE_ENV__: JSON.stringify(NODE_ENV)
});
module.exports = { module.exports = {
devtool: 'cheap-module-source-map', devtool: 'cheap-module-source-map',
@ -35,7 +52,8 @@ module.exports = {
warnings: false warnings: false
}, },
}), }),
new webpack.NoErrorsPlugin() new webpack.NoErrorsPlugin(),
new webpack.DefinePlugin(defines)
], ],
module: { module: {
loaders: [ loaders: [