diff --git a/README.md b/README.md index fc1ed0e..6221aec 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,27 @@ See Guild https://github.com/lorenwest/node-config/wiki/Configuration-Files |`PORT`| The port to listen| |`GOOGLE_API_KEY`| The google api key see (https://developers.google.com/maps/documentation/javascript/get-api-key#key)| |`API_BASE_URL`| The base URL for Drone API | - +|`REACT_APP_AUTH0_CLIENT_ID`| The auth0 client id | +|`REACT_APP_AUTH0_CLIENT_DOMAIN`| The auth0 client domain | + +### Auth0 setup +- Create an account on auth0. +- Click on clients in left side menu, it will redirect you to client page. Click on CREATE CLIENT button + to create a new client. +- Copy the client id and client domain and export them as environment variables. +- Add `http://localhost:3000` as Allowed callback url's in client settings. + +### Add social connections + +### Facebook social connection +- To add facebook social connection to auth0, you have to create a facebook app. + Go to facebook [developers](https://developers.facebook.com/apps) and create a new app. +- Copy the app secret and app id to auth0 social connections facebook tab. +- You have to setup the oauth2 callback in app oauth settings. +- For more information visit auth0 [docs](https://auth0.com/docs/connections/social/facebook) + +### Google social connection +- For more information on how to connect google oauth2 client, visit official [docs](https://auth0.com/docs/connections/social/google) ## Install dependencies `npm i` diff --git a/config/default.js b/config/default.js index 6bc8230..d35b608 100644 --- a/config/default.js +++ b/config/default.js @@ -1,6 +1,6 @@ /* eslint-disable import/no-commonjs */ /** - * Main config file + * Main config file for the server which is hosting the reat app */ module.exports = { // below env variables are NOT visible in frontend @@ -8,5 +8,4 @@ module.exports = { // below env variables are visible in frontend GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || 'AIzaSyCrL-O319wNJK8kk8J_JAYsWgu6yo5YsDI', - API_BASE_PATH: process.env.API_BASE_PATH || 'https://kb-dsp-server-dev.herokuapp.com', }; diff --git a/envSample b/envSample index 4a42b68..003a7ca 100644 --- a/envSample +++ b/envSample @@ -1,5 +1,5 @@ -REACT_APP_API_BASE_PATH=https://kb-dsp-server.herokuapp.com -REACT_APP_SOCKET_URL=https://kb-dsp-server.herokuapp.com -REACT_APP_AUTH0_CLIEND_ID=3CGKzjS2nVSqHxHHE64RhvvKY6e0TYpK -REACT_APP_AUTH0_DOMAIN=dronetest.auth0.com -REACT_APP_GOOGLE_API_KEY=AIzaSyCR3jfBdv9prCBYBOf-fPUDhjPP4K05YjE +GOOGLE_API_KEY=AIzaSyCrL-O319wNJK8kk8J_JAYsWgu6yo5YsDI +API_BASE_PATH=http://localhost:3500 +REACT_APP_AUTH0_CLIENT_ID=h7p6V93Shau3SSvqGrl6V4xrATlkrVGm +REACT_APP_AUTH0_CLIENT_DOMAIN=spanhawk.auth0.com +REACT_APP_SOCKET_URL=http://localhost:3500 \ No newline at end of file diff --git a/package.json b/package.json index 2ee9179..56a0a6a 100644 --- a/package.json +++ b/package.json @@ -55,18 +55,18 @@ "rc-tooltip": "^3.4.2", "react": "^15.3.2", "react-breadcrumbs": "^1.5.1", - "react-count-down": "^1.0.3", "react-click-outside": "^2.2.0", + "react-count-down": "^1.0.3", "react-css-modules": "^3.7.10", "react-date-picker": "^5.3.28", "react-dom": "^15.3.2", + "react-dropdown": "^1.2.0", "react-flexbox-grid": "^0.10.2", "react-google-maps": "^6.0.1", - "react-modal": "^1.5.2", - "react-dropdown": "^1.2.0", + "react-highcharts": "^11.0.0", "react-icheck": "^0.3.6", "react-input-range": "^0.9.3", - "react-highcharts": "^11.0.0", + "react-modal": "^1.5.2", "react-redux": "^4.0.0", "react-redux-toastr": "^4.2.2", "react-router": "^2.8.1", @@ -75,8 +75,8 @@ "react-simple-dropdown": "^1.1.5", "react-slick": "^0.14.5", "react-star-rating-component": "^1.2.2", - "react-timeago": "^3.1.3", "react-tabs": "^0.8.2", + "react-timeago": "^3.1.3", "reactable": "^0.14.1", "redbox-react": "^1.2.10", "redux": "^3.0.0", @@ -86,8 +86,8 @@ "redux-logger": "^2.6.1", "redux-thunk": "^2.0.0", "sass-loader": "^4.0.0", - "socket.io-client": "^1.7.1", "slick-carousel": "^1.6.0", + "socket.io-client": "^1.7.1", "style-loader": "^0.13.0", "superagent": "^2.3.0", "superagent-promise": "^1.1.0", diff --git a/src/api/User.js b/src/api/User.js index 70c1728..a12bc57 100644 --- a/src/api/User.js +++ b/src/api/User.js @@ -56,7 +56,7 @@ class UserApi { })}); } - registerSocialUser(name, email) { + registerSocialUser(name, email, token) { const url = `${this.basePath}/api/v1/login/social`; return reqwest({ @@ -64,6 +64,9 @@ class UserApi { method: 'post', type: 'json', contentType: 'application/json', + headers: { + Authorization: `Bearer ${token}`, + }, data: JSON.stringify({ name, email, diff --git a/src/components/TextField/TextField.scss b/src/components/TextField/TextField.scss index 46a04ab..56f5f52 100644 --- a/src/components/TextField/TextField.scss +++ b/src/components/TextField/TextField.scss @@ -2,6 +2,7 @@ width: 100%; border: 1px solid #ebebeb; + input[type="password"], input[type="text"] { width: 100%; padding: 0 10px; diff --git a/src/config/index.js b/src/config/index.js index c6b575d..db5b4f9 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,23 +1,14 @@ +/* eslint-disable import/no-commonjs */ /** - * Copyright (c) 2016 Topcoder Inc, All rights reserved. + * Main config file for the react app */ - -/** - * Webapp configuration - * - * @author TCSCODER - * @version 1.0.0 - */ - -const config = { - api: { - basePath: process.env.REACT_APP_API_BASE_PATH || 'http://localhost:3500', - }, +module.exports = { + // below env variables are visible in frontend + API_BASE_PATH: process.env.API_BASE_PATH || 'http://localhost:3500', + REACT_APP_AUTH0_CLIENT_ID: process.env.REACT_APP_AUTH0_CLIENT_ID || 'h7p6V93Shau3SSvqGrl6V4xrATlkrVGm', + REACT_APP_AUTH0_CLIENT_DOMAIN: process.env.REACT_APP_AUTH0_CLIENT_DOMAIN || 'spanhawk.auth0.com', + AUTH0_CALLBACK: 'http://localhost:3000', socket: { url: process.env.REACT_APP_SOCKET_URL || 'http://localhost:3500', }, - AUTH0_CLIEND_ID: process.env.REACT_APP_AUTH0_CLIEND_ID || '3CGKzjS2nVSqHxHHE64RhvvKY6e0TYpK', - AUTH0_DOMAIN: process.env.REACT_APP_AUTH0_DOMAIN || 'dronetest.auth0.com', }; - -export default config; diff --git a/src/routes/DronesMap/modules/DronesMap.js b/src/routes/DronesMap/modules/DronesMap.js index 71ac81b..eb8da53 100644 --- a/src/routes/DronesMap/modules/DronesMap.js +++ b/src/routes/DronesMap/modules/DronesMap.js @@ -1,7 +1,7 @@ import {handleActions} from 'redux-actions'; import io from 'socket.io-client'; import APIService from 'services/APIService'; -import config from '../../../../config/default'; +import config from '../../../config'; // Drones will be updated and map will be redrawn every 3s // Otherwise if drones are updated with high frequency (e.g. 0.5s), the map will be freezing @@ -32,7 +32,7 @@ export const init = () => async(dispatch) => { const {body: {items: drones}} = await APIService.searchDrones({limit: DRONE_LIMIT}); lastUpdated = new Date().getTime(); dispatch({type: DRONES_LOADED, payload: {drones}}); - socket = io(config.API_BASE_PATH); + socket = io(config.socket.url); socket.on('dronepositionupdate', (drone) => { pendingUpdates[drone.id] = drone; if (updateTimeoutId) { diff --git a/src/routes/Home/components/LoginModal/LoginModal.jsx b/src/routes/Home/components/LoginModal/LoginModal.jsx index 5bd807e..2ed19ff 100644 --- a/src/routes/Home/components/LoginModal/LoginModal.jsx +++ b/src/routes/Home/components/LoginModal/LoginModal.jsx @@ -7,6 +7,12 @@ import Button from 'components/Button'; import Checkbox from 'components/Checkbox'; import TextField from 'components/TextField'; import styles from './LoginModal.scss'; +import APIService from '../../../../services/APIService'; +import {toastr} from 'react-redux-toastr'; +import {defaultAuth0Service} from '../../../../services/AuthService'; + +const EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; + /* * customStyles */ @@ -50,10 +56,11 @@ FormField.propTypes = { */ class LogInModal extends React.Component { - constructor() { - super(); + constructor(props) { + super(props); this.state = { modalLoginIsOpen: false, + showForgetPassword: false, }; } @@ -62,7 +69,7 @@ class LogInModal extends React.Component { } closeLoginModal() { - this.setState({modalLoginIsOpen: false}); + this.setState({modalLoginIsOpen: false, showForgetPassword: false}); } login() { @@ -70,7 +77,6 @@ class LogInModal extends React.Component { } handleLogin(handleLoggedIn, loggedUser) { - handleLoggedIn(); const _self = this; setTimeout(() => { handleLoggedIn(); @@ -81,9 +87,53 @@ class LogInModal extends React.Component { }, 100); } + forgetPassword() { + this.setState({showForgetPassword: true}); + } + + /** + * Login using google social network, + * this method internally uses auth0 service + */ + googleLogin() { + defaultAuth0Service.login({connection: 'google-oauth2'}, (error) => { + if (error) { + const message = error.message || 'something went wrong, please try again'; + toastr.error(message); + } + }); + } + + /** + * Login using facebook social network, + * this method internally uses auth0 service + */ + facebookLogin() { + defaultAuth0Service.login({connection: 'facebook'}, (error) => { + if (error) { + const message = error.message || 'something went wrong, please try again'; + toastr.error(message); + } + }); + } + + /** + * This method is invoked when reset password request is submitted + */ + handleForgetPassword(data) { + APIService.forgotPassword({email: data.emailUp}).then(() => { + toastr.success('', 'Reset password link emailed to your email address'); + this.closeLoginModal(); + }).catch((reason) => { + const message = reason.response.body.error || 'something went wrong, please try again'; + toastr.error(message); + this.closeLoginModal(); + }); + } + render() { + const _self = this; const {handleSubmit, fields, handleLoggedIn, loggedUser, hasError, errorText} = this.props; - return (
@@ -100,71 +150,88 @@ class LogInModal extends React.Component {
-
Login to Your Account
+ {this.state.showForgetPassword === false &&
Login to Your Account
} + {this.state.showForgetPassword === true &&
Reset forgotten password
}
+ {this.state.showForgetPassword === false && +
+
+ + + Log In with Facebook + +
- - - - - {/* login with end */} -
-
-
or
-
-
- {/* or end */} -
- {hasError && {errorText.error}} -
- - - + + {/* login with end */} +
+
+
or
+
+ {/* or end */}
- - - + {hasError && {errorText.error}} +
+ + + +
+
+ + + +
-
- {/* input end */} -
-
- this.props.fields.remember.onChange(!this.props.fields.remember.value)} - id="remember" + {/* input end */} +
+
+ this.props.fields.remember.onChange(!this.props.fields.remember.value)} + id="remember" + > + Remember me + +
+ +
+
+ +
+
+ Don’t have an account? Sign Up +
+ + } + { this.state.showForgetPassword === true && +
_self.handleForgetPassword(data))}> +
+ {hasError && {errorText.error}} +
+ + + +
+
+
+
- -
-
- -
-
- Don’t have an account? Sign Up -
- + + } - -
); } @@ -183,9 +250,21 @@ const fields = ['remember', 'email', 'password', 'emailUp', 'passwordUp']; const validate = (values) => { const errors = {}; + if (!values.emailUp && !values.email) { + errors.emailUp = 'Email is required'; + } else if (!EMAIL_REGEX.test(values.emailUp) && !values.email) { + errors.emailUp = 'Invalid email address'; + } + + if (errors.emailUp && (values.emailUp || values.email)) { + return errors; + } else if (values.emailUp) { + return errors; + } + if (!values.email) { errors.email = 'Email is required'; - } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) { + } else if (!EMAIL_REGEX.test(values.email)) { errors.email = 'Invalid email address'; } if (!values.password) { diff --git a/src/routes/Home/components/SignupModal/SignupModal.jsx b/src/routes/Home/components/SignupModal/SignupModal.jsx index 213d6e6..f6a97e3 100644 --- a/src/routes/Home/components/SignupModal/SignupModal.jsx +++ b/src/routes/Home/components/SignupModal/SignupModal.jsx @@ -6,6 +6,8 @@ import Modal from 'react-modal'; import Button from 'components/Button'; import TextField from 'components/TextField'; import styles from './SignupModal.scss'; +import {defaultAuth0Service} from '../../../../services/AuthService'; +import {toastr} from 'react-redux-toastr'; /* * customStyles @@ -79,6 +81,32 @@ class SignupModal extends React.Component { }, 100); } + /** + * Login using google social network, + * this method internally uses auth0 service + */ + googleLogin() { + defaultAuth0Service.login({connection: 'google-oauth2'}, (error) => { + if (error) { + const message = error.message || 'something went wrong, please try again'; + toastr.error(message); + } + }); + } + + /** + * Login using facebook social network, + * this method internally uses auth0 service + */ + facebookLogin() { + defaultAuth0Service.login({connection: 'facebook'}, (error) => { + if (error) { + const message = error.message || 'something went wrong, please try again'; + toastr.error(message); + } + }); + } + render() { const {handleSubmit, fields, handleSigned, signedUser, hasError, errorText} = this.props; @@ -103,14 +131,14 @@ class SignupModal extends React.Component {
- + Sign Up with Google Plus diff --git a/src/routes/ResetPassword/components/ResetPasswordView.jsx b/src/routes/ResetPassword/components/ResetPasswordView.jsx new file mode 100644 index 0000000..3fe1671 --- /dev/null +++ b/src/routes/ResetPassword/components/ResetPasswordView.jsx @@ -0,0 +1,80 @@ +import React, {Component} from 'react'; +import CSSModules from 'react-css-modules'; +import styles from './ResetPasswordView.scss'; +import TextField from '../../../components/TextField'; +import FormField from '../../../components/FormField'; +import Button from '../../../components/Button'; +import {reduxForm} from 'redux-form'; +import {sendRequest} from '../modules/ResetPassword'; +import {browserHistory} from 'react-router'; +import {toastr} from 'react-redux-toastr'; + +class ResetPasswordView extends Component { + + /** + * This function is called when the form is submitted + * This is triggered by handleSubmit + */ + onSubmit(data) { + sendRequest(data).then(() => { + toastr.success('', 'Password reset successfuly, kindly login again'); + browserHistory.push('/'); + }).catch((reason) => { + const message = reason.response.body.error || 'something went wrong, please try again'; + toastr.error(message); + }); + } + + render() { + const {fields, handleSubmit, location: {query: {token}}} = this.props; + const _self = this; + return ( +
+ _self.onSubmit({...data, code: token}))}> +
+ + + + +
+
+ + + + +
+ + {/* add-package end */} +
+ +
+ + {/* form end */} +
+ ); + } +} + +ResetPasswordView.propTypes = { + fields: React.PropTypes.object.isRequired, + location: React.PropTypes.object.isRequired, + handleSubmit: React.PropTypes.func.isRequired, +}; + +const form = reduxForm({ + form: 'resetPasswordForm', + fields: ['password', 'email'], + validate(values) { + const errors = {}; + if (!values.password) { + errors.password = 'required'; + } + if (!values.email) { + errors.email = 'required'; + } + + return errors; + }, +}); + +export default form(CSSModules(ResetPasswordView, styles)); diff --git a/src/routes/ResetPassword/components/ResetPasswordView.scss b/src/routes/ResetPassword/components/ResetPasswordView.scss new file mode 100644 index 0000000..b008991 --- /dev/null +++ b/src/routes/ResetPassword/components/ResetPasswordView.scss @@ -0,0 +1,48 @@ +.reset-password-form { + padding: 50px 0 14px; + margin: 0 300px; + height: calc(100vh - 60px - 42px - 50px); // header height - breadcrumb height - footer height + h4 { + font-weight: bold; + font-size: 20px; + color: #525051; + margin-top: 40px; + border-top: 1px solid #e7e8ea; + padding-top: 25px; + } + :global { + .form-field { + width: 100%; + &.error { + color: #ff3100; + > div:first-child { + border: 1px solid #ff3100; + } + } + } + } +} +.row { + display: flex; + margin-bottom: 22px; + label { + display: block; + flex: 0 0 20%; + align-self: center; + font-size: 14px; + color: #343434; + font-weight: bold; + } + + .input-with-label { + flex: 0 0 20%; + display: flex; + align-items: center; + .input { + flex: 0 0 66%; + } + } +} +.actions { + text-align: right; +} diff --git a/src/routes/ResetPassword/containers/ResetPasswordContainer.js b/src/routes/ResetPassword/containers/ResetPasswordContainer.js new file mode 100644 index 0000000..f6309c7 --- /dev/null +++ b/src/routes/ResetPassword/containers/ResetPasswordContainer.js @@ -0,0 +1,12 @@ +import {asyncConnect} from 'redux-connect'; +import {actions} from '../modules/ResetPassword'; + +import ResetPasswordView from '../components/ResetPasswordView'; + +const resolve = [{ + promise: () => Promise.resolve(), +}]; + +const mapState = (state) => state.resetPassword; + +export default asyncConnect(resolve, mapState, actions)(ResetPasswordView); diff --git a/src/routes/ResetPassword/index.js b/src/routes/ResetPassword/index.js new file mode 100644 index 0000000..eeb7dbb --- /dev/null +++ b/src/routes/ResetPassword/index.js @@ -0,0 +1,20 @@ +import {injectReducer} from '../../store/reducers'; + +export default (store) => ({ + path: 'reset-password', + name: 'Reset password', /* Breadcrumb name */ + staticName: true, + getComponent(nextState, cb) { + require.ensure([], (require) => { + const Dashboard = require('./containers/ResetPasswordContainer').default; + const reducer = require('./modules/ResetPassword').default; + + injectReducer(store, {key: 'resetPassword', reducer}); + if (!nextState.location.query.token) { + cb(new Error('Invalid route invocation')); + } else { + cb(null, Dashboard); + } + }, 'ResetPassword'); + }, +}); diff --git a/src/routes/ResetPassword/modules/ResetPassword.js b/src/routes/ResetPassword/modules/ResetPassword.js new file mode 100644 index 0000000..a5bcb9e --- /dev/null +++ b/src/routes/ResetPassword/modules/ResetPassword.js @@ -0,0 +1,23 @@ +import {handleActions} from 'redux-actions'; +import APIService from 'services/APIService'; +// ------------------------------------ +// Actions +// ------------------------------------ + +export const actions = { +}; + +export const sendRequest = (values) => new Promise((resolve, reject) => { + APIService.resetPassword(values).then((result) => { + resolve(result); + }).catch((reason) => { + reject(reason); + }); +}); + +// ------------------------------------ +// Reducer +// ------------------------------------ +export default handleActions({ +}, { +}); diff --git a/src/routes/index.js b/src/routes/index.js index ba238aa..b689d66 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -19,6 +19,8 @@ import BrowseProviderRoute from './BrowseProvider'; import DroneDetailsRoute from './DroneDetails'; import AvailablePackagesRoute from './AvailablePackages'; import ProviderDetailsRoute from './ProviderDetails'; +import ResetPasswordRoute from './ResetPassword'; +import {defaultAuth0Service} from '../services/AuthService'; export const createRoutes = (store) => ({ path: '/', @@ -27,8 +29,15 @@ export const createRoutes = (store) => ({ component: CoreLayout, indexRoute: { onEnter: (nextState, replace, cb) => { - replace('/dashboard'); - cb(); + // parse the hash if present + if (nextState.location.hash) { + defaultAuth0Service.parseHash(nextState.location.hash); + replace('/dashboard'); + cb(); + } else { + replace('/dashboard'); + cb(); + } }, }, childRoutes: [ @@ -53,7 +62,7 @@ export const createRoutes = (store) => ({ DroneDetailsRoute(store), AvailablePackagesRoute(store), ProviderDetailsRoute(store), - + ResetPasswordRoute(store), ], }); diff --git a/src/services/APIService.js b/src/services/APIService.js index a69c8d2..43c7f2d 100644 --- a/src/services/APIService.js +++ b/src/services/APIService.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import superagent from 'superagent'; import superagentPromise from 'superagent-promise'; -import config from '../../config/default'; +import config from '../config'; // DEMO: emulate API requests with dummy data for demo purposes @@ -578,4 +578,28 @@ export default class APIService { .query(params) .end(); } + + /** + * Reset the user password + * @param {Object} entity the client request payload + */ + static resetPassword(entity) { + return request + .post(`${config.API_BASE_PATH}/api/v1/reset-password`) + .set('Content-Type', 'application/json') + .send(entity) + .end(); + } + + /** + * Send the forgot password link to user's email account + * @param {Object} entity the client request payload + */ + static forgotPassword(entity) { + return request + .post(`${config.API_BASE_PATH}/api/v1/forgot-password`) + .set('Content-Type', 'application/json') + .send(entity) + .end(); + } } diff --git a/src/services/AuthService.js b/src/services/AuthService.js new file mode 100644 index 0000000..fbb2650 --- /dev/null +++ b/src/services/AuthService.js @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2016 Topcoder Inc, All rights reserved. + */ + +/** + * auth0 Authentication service for the app. + * + * @author TCSCODER + * @version 1.0.0 + */ + +import Auth0 from 'auth0-js'; +import config from '../config'; +import UserApi from '../api/User'; +import _ from 'lodash'; + + +const userApi = new UserApi(config.API_BASE_PATH); +const idTokenKey = 'id_token'; + +class AuthService { + + /** + * Default constructor + * @param {String} clientId the auth0 client id + * @param {String} domain the auth0 domain + */ + constructor(clientId, domain) { + this.auth0 = new Auth0({ + clientID: clientId, + domain, + responseType: 'token', + callbackURL: config.AUTH0_CALLBACK, + }); + this.login = this.login.bind(this); + this.parseHash = this.parseHash.bind(this); + this.loggedIn = this.loggedIn.bind(this); + this.logout = this.logout.bind(this); + this.getProfile = this.getProfile.bind(this); + this.getHeader = this.getHeader.bind(this); + } + + /** + * Redirects the user to appropriate social network for oauth2 authentication + * + * @param {Object} params any params to pass to auth0 client + * @param {Function} onError function to execute on error + */ + login(params, onError) { + // redirects the call to auth0 instance + this.auth0.login(params, onError); + } + + /** + * Parse the hash fragment of url + * This method will actually parse the token + * will create a user profile if not already present and save the id token in local storage + * if there is some error delete the access token + * @param {String} hash the hash fragment + */ + parseHash(hash) { + const _self = this; + const authResult = _self.auth0.parseHash(hash); + if (authResult && authResult.idToken) { + _self.setToken(authResult.idToken); + // get social profile + _self.getProfile((error, profile) => { + if (error) { + // remove the id token + _self.removeToken(); + throw error; + } else { + userApi.registerSocialUser(profile.name, profile.email, _self.getToken()).then(_.noop).catch((reason) => { + // remove the id token + _self.removeToken(); + throw reason; + }); + } + }); + } + } + + /** + * Check if the user is logged in + * @param {String} hash the hash fragment + */ + loggedIn() { + // Checks if there is a saved token and it's still valid + return !!this.getToken(); + } + + /** + * Set the id token to be stored in local storage + * @param {String} idToken the token to store + */ + setToken(idToken) { + // Saves user token to localStorage + localStorage.setItem(idTokenKey, idToken); + } + + /** + * Get the stored id token from local storage + */ + getToken() { + // Retrieves the user token from localStorage + return localStorage.getItem(idTokenKey); + } + + /** + * Remove the id token from local storage + */ + removeToken() { + // Clear user token and profile data from localStorage + localStorage.removeItem(idTokenKey); + } + + /** + * Logout the user from the application, delete the id token + */ + logout() { + this.removeToken(); + } + + /** + * Get the authorization header for API access + */ + getHeader() { + return { + Authorization: `Bearer ${this.getToken()}`, + }; + } + + /** + * Get the profile of currently logged in user + * + * @param {callback} the callback function to call after operation finishes + * @return {Object} the profile of logged in user + */ + getProfile(callback) { + this.auth0.getProfile(this.getToken(), callback); + } +} + +const defaultAuth0Service = new AuthService(config.REACT_APP_AUTH0_CLIENT_ID, config.REACT_APP_AUTH0_CLIENT_DOMAIN); + +export {AuthService as default, defaultAuth0Service}; diff --git a/src/store/modules/global.js b/src/store/modules/global.js index 0e0357c..4373f22 100644 --- a/src/store/modules/global.js +++ b/src/store/modules/global.js @@ -3,7 +3,7 @@ import {browserHistory} from 'react-router'; import UserApi from 'api/User.js'; import config from '../../config'; -const userApi = new UserApi(config.api.basePath); +const userApi = new UserApi(config.API_BASE_PATH); // ------------------------------------ // Actions pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy