Auth service receives login data
This commit is contained in:
@@ -15,11 +15,14 @@
|
||||
"autoprefixer-loader": "^2.0.0",
|
||||
"babel-core": "6.1.4",
|
||||
"babel-loader": "6.1.0",
|
||||
"babel-plugin-add-module-exports": "^0.1.2",
|
||||
"babel-plugin-transform-runtime": "6.1.4",
|
||||
"babel-polyfill": "^6.1.4",
|
||||
"babel-preset-es2015": "6.1.4",
|
||||
"babel-preset-react": "6.1.4",
|
||||
"babel-preset-stage-0": "6.1.2",
|
||||
"babel-register": "6.1.4",
|
||||
"babel-runtime": "^6.0.14",
|
||||
"css-loader": "^0.14.4",
|
||||
"del": "^1.2.0",
|
||||
"extract-text-webpack-plugin": "^0.8.1",
|
||||
@@ -45,13 +48,14 @@
|
||||
"webpack-dev-server": "^1.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "6.1.4",
|
||||
"babel-runtime": "6.0.14",
|
||||
"classnames": "^2.2.3",
|
||||
"history": "1.17.0",
|
||||
"immutable": "^3.7.6",
|
||||
"invariant": "^2.1.1",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"js-cookie": "^2.1.0",
|
||||
"object-pick": "^0.1.1",
|
||||
"querystring": "^0.2.0",
|
||||
"react": "^0.14.7",
|
||||
"react-bootstrap": "^0.28.3",
|
||||
"react-dom": "^0.14.0",
|
||||
@@ -63,10 +67,9 @@
|
||||
"react-router-redux": "^3.0.0",
|
||||
"react-select": "^0.9.1",
|
||||
"redux": "^3.0.2",
|
||||
"redux-auth": "0.0.2",
|
||||
"redux-batched-subscribe": "^0.1.4",
|
||||
"redux-logger": "^2.5.2",
|
||||
"redux-multi": "^0.1.9",
|
||||
"redux-logger": "^2.6.0",
|
||||
"redux-multi": "^0.1.91",
|
||||
"redux-router": "^1.0.0-beta7",
|
||||
"redux-thunk": "^1.0.3",
|
||||
"uniloc": "^0.2.0"
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
import React from "react";
|
||||
import { createStore, compose, applyMiddleware, combineReducers} from "redux";
|
||||
import { Provider} from "react-redux";
|
||||
import { Provider, connect} from "react-redux";
|
||||
|
||||
import thunk from "redux-thunk";
|
||||
import createLogger from 'redux-logger';
|
||||
|
||||
@@ -14,17 +15,16 @@ import { ReduxRouter} from "redux-router";
|
||||
//import { Router, IndexRoute, Route, browserHistory } from 'react-router';
|
||||
//import { syncHistory, routeReducer } from 'react-router-redux';
|
||||
|
||||
import { configure as reduxAuthConfigure, authStateReducer } from "redux-auth";
|
||||
import { AuthGlobals } from "redux-auth/bootstrap-theme";
|
||||
//import { configure as reduxAuthConfigure, authStateReducer } from "redux-auth";
|
||||
//import { authStateReducer } from "redux-auth";
|
||||
import authStateReducer from "./reducers/auth";
|
||||
import { configure as reduxAuthConfigure } from './actions/configure';
|
||||
//import { AuthGlobals } from "redux-auth/bootstrap-theme";
|
||||
|
||||
import { createHistory, createHashHistory, createMemoryHistory } from "history";
|
||||
import { routerStateReducer, reduxReactRouter as clientRouter} from "redux-router";
|
||||
import { pushState, routerStateReducer, reduxReactRouter as clientRouter} from "redux-router";
|
||||
import { reduxReactRouter as serverRouter } from "redux-router/server";
|
||||
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { pushState } from 'redux-router';
|
||||
|
||||
import { requireAuthentication } from './components/AuthComponent';
|
||||
|
||||
//import demoButtons from "./reducers/request-test-buttons";
|
||||
@@ -35,10 +35,7 @@ import Account from "./views/Account";
|
||||
import SignIn from "./views/SignIn";
|
||||
import SignUp from "./views/SignUp";
|
||||
//import GlobalComponents from "./views/partials/GlobalComponents";
|
||||
|
||||
|
||||
// TODO: !!!!
|
||||
// <GlobalComponents />
|
||||
const AuthGlobals = () => (<div></div>);
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
@@ -60,26 +57,6 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
|
||||
//demoUi
|
||||
});
|
||||
|
||||
//let store;
|
||||
|
||||
//// access control method, used above in the "account" route
|
||||
//const requireAuth = (nextState, transition, cb) => {
|
||||
// // the setTimeout is necessary because of this bug:
|
||||
// // https://github.com/rackt/redux-router/pull/62
|
||||
// // this will result in a bunch of warnings, but it doesn't seem to be a serious problem
|
||||
// setTimeout(() => {
|
||||
// if (!store.getState().auth.getIn(["user", "isSignedIn"])) {
|
||||
// transition(null, "/login");
|
||||
// }
|
||||
// cb();
|
||||
// }, 0);
|
||||
//};
|
||||
|
||||
|
||||
// define app routes
|
||||
// <Route path="account" component={Account} onEnter={requireAuth} />
|
||||
//<Route path="account" component={requireAuthentication(Account)} />
|
||||
|
||||
const routes = (
|
||||
<Route path="/" component={App}>
|
||||
<IndexRoute component={requireAuthentication(MyAccounts)} />
|
||||
@@ -95,11 +72,10 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
|
||||
|
||||
// create the redux store
|
||||
const store = compose(
|
||||
applyMiddleware(thunk),
|
||||
applyMiddleware(createLogger()),
|
||||
applyMiddleware(thunk, createLogger()),
|
||||
reduxReactRouter({
|
||||
createHistory: createHistoryMethod,
|
||||
routes
|
||||
routes,
|
||||
createHistory: createHistoryMethod
|
||||
})
|
||||
)(createStore)(reducer);
|
||||
|
||||
@@ -107,44 +83,36 @@ export function initialize({cookies, isServer, currentLocation, userAgent} = {})
|
||||
/**
|
||||
* The React Router 1.0 routes for both the server and the client.
|
||||
*/
|
||||
return store.dispatch(((() => { debugger; })() , reduxAuthConfigure)([
|
||||
return store.dispatch(reduxAuthConfigure([
|
||||
{
|
||||
default: {
|
||||
//apiUrl: __API_URL__
|
||||
apiUrl: '/',
|
||||
emailSignInPath: 'login',
|
||||
emailRegistrationPath: 'customers'
|
||||
}
|
||||
}
|
||||
//, {
|
||||
// evilUser: {
|
||||
// //apiUrl: __API_URL__,
|
||||
// apiUrl: '/api',
|
||||
// signOutPath: "/mangs/sign_out",
|
||||
// emailSignInPath: "/mangs/sign_in",
|
||||
// emailRegistrationPath: "/mangs",
|
||||
// accountUpdatePath: "/mangs",
|
||||
// accountDeletePath: "/mangs",
|
||||
// passwordResetPath: "/mangs/password",
|
||||
// passwordUpdatePath: "/mangs/password",
|
||||
// tokenValidationPath: "/mangs/validate_token",
|
||||
// authProviderPaths: {
|
||||
// github: "/mangs/github",
|
||||
// facebook: "/mangs/facebook",
|
||||
// google: "/mangs/google_oauth2"
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
], {
|
||||
cookies,
|
||||
isServer,
|
||||
currentLocation,
|
||||
tokenFormat: {
|
||||
"access-token": "{{ access-token }}",
|
||||
"uid": "true"
|
||||
"access-token": "{{ access-token }}"
|
||||
},
|
||||
initialCredentials: {
|
||||
'uid': 123
|
||||
handleLoginResponse: function(resp) {
|
||||
debugger;
|
||||
|
||||
return resp.data;
|
||||
},
|
||||
|
||||
handleAccountUpdateResponse: function(resp) {
|
||||
debugger;
|
||||
|
||||
return resp.data;
|
||||
},
|
||||
|
||||
handleTokenValidationResponse: function(resp) {
|
||||
debugger;
|
||||
return resp.data;
|
||||
}
|
||||
})).then(({ redirectPath, blank } = {}) => {
|
||||
// hack for material-ui server-side rendering.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../constants/ACTION_TYPES';
|
||||
|
||||
export function configureStart({...props} = {}) {
|
||||
return {
|
||||
@@ -24,12 +23,5 @@ export function configureError({errors, ...props} = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
export function authenticateStart() {
|
||||
return { type: T.AUTH.AUTHENTICATE_START };
|
||||
}
|
||||
export function authenticateComplete(user) {
|
||||
return { type: T.AUTH.AUTHENTICATE_COMPLETE, user };
|
||||
}
|
||||
export function authenticateError(errors) {
|
||||
return { type: T.AUTH.AUTHENTICATE_ERROR, errors };
|
||||
}
|
||||
|
||||
|
||||
|
||||
14
js-frontend/src/actions/authenticate.js
Normal file
14
js-frontend/src/actions/authenticate.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import T from '../constants/ACTION_TYPES';
|
||||
|
||||
export function authenticateStart() {
|
||||
return { type: T.AUTH.AUTHENTICATE_START };
|
||||
}
|
||||
export function authenticateComplete(user) {
|
||||
return { type: T.AUTH.AUTHENTICATE_COMPLETE, user };
|
||||
}
|
||||
export function authenticateError(errors) {
|
||||
return { type: T.AUTH.AUTHENTICATE_ERROR, errors };
|
||||
}
|
||||
95
js-frontend/src/actions/configure.js
Normal file
95
js-frontend/src/actions/configure.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import * as C from "../utils/constants";
|
||||
import {
|
||||
authenticateStart,
|
||||
authenticateComplete,
|
||||
authenticateError
|
||||
} from "./authenticate";
|
||||
import {applyConfig} from "../utils/clientSettings";
|
||||
|
||||
//import {
|
||||
// showFirstTimeLoginSuccessModal,
|
||||
// showFirstTimeLoginErrorModal,
|
||||
// showPasswordResetSuccessModal,
|
||||
// showPasswordResetErrorModal
|
||||
//} from "./ui";
|
||||
|
||||
import {destroySession} from "../utils/sessionStorage";
|
||||
import getRedirectInfo from "../utils/parseUrl";
|
||||
import {pushState} from "redux-router";
|
||||
|
||||
export const SET_ENDPOINT_KEYS = "SET_ENDPOINT_KEYS";
|
||||
export const STORE_CURRENT_ENDPOINT_KEY = "STORE_CURRENT_ENDPOINT_KEY";
|
||||
|
||||
export function setEndpointKeys(endpoints, currentEndpointKey, defaultEndpointKey) {
|
||||
return { type: SET_ENDPOINT_KEYS, endpoints, currentEndpointKey, defaultEndpointKey };
|
||||
};
|
||||
|
||||
export function storeCurrentEndpointKey(currentEndpointKey) {
|
||||
return { type: STORE_CURRENT_ENDPOINT_KEY, currentEndpointKey };
|
||||
};
|
||||
|
||||
export function configure(endpoint={}, settings={}) {
|
||||
|
||||
return dispatch => {
|
||||
// don't render anything for OAuth redirects
|
||||
if (settings.currentLocation && settings.currentLocation.match(/blank=true/)) {
|
||||
return Promise.resolve({blank: true});
|
||||
}
|
||||
|
||||
dispatch(authenticateStart());
|
||||
|
||||
let promise,
|
||||
firstTimeLogin,
|
||||
mustResetPassword,
|
||||
user,
|
||||
headers;
|
||||
|
||||
let {authRedirectPath, authRedirectHeaders} = getRedirectInfo(window.location);
|
||||
|
||||
if (authRedirectPath) {
|
||||
dispatch(pushState(null, authRedirectPath));
|
||||
}
|
||||
|
||||
if (authRedirectHeaders && authRedirectHeaders["access-token"]) {
|
||||
debugger;
|
||||
settings.initialCredentials = {
|
||||
...settings.initialCredentials,
|
||||
...authRedirectHeaders
|
||||
};
|
||||
}
|
||||
|
||||
// if tokens were invalidated by server, make sure to clear browser
|
||||
// credentials
|
||||
if (!settings.initialCredentials) {
|
||||
destroySession();
|
||||
}
|
||||
|
||||
promise = Promise.resolve(applyConfig({ dispatch, endpoint, settings }));
|
||||
|
||||
return promise
|
||||
.then(user => {
|
||||
|
||||
dispatch(authenticateComplete(user));
|
||||
|
||||
return user;
|
||||
})
|
||||
.catch(({reason} = {}) => {
|
||||
|
||||
dispatch(authenticateError([reason]));
|
||||
|
||||
//if (firstTimeLogin) {
|
||||
// dispatch(showFirstTimeLoginErrorModal());
|
||||
//}
|
||||
//
|
||||
//if (mustResetPassword) {
|
||||
// dispatch(showPasswordResetErrorModal());
|
||||
//}
|
||||
|
||||
return Promise.resolve({reason});
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
70
js-frontend/src/actions/signIn.js
Normal file
70
js-frontend/src/actions/signIn.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import {
|
||||
getEmailSignInUrl,
|
||||
setCurrentEndpointKey,
|
||||
getCurrentEndpointKey
|
||||
} from "../utils/sessionStorage";
|
||||
|
||||
import { storeCurrentEndpointKey } from "./configure";
|
||||
import { parseResponse } from "../utils/handleFetchResponse";
|
||||
import fetch from "../utils/fetch";
|
||||
|
||||
import T from '../constants/ACTION_TYPES';
|
||||
|
||||
import root from '../utils/root';
|
||||
|
||||
export function emailSignInFormUpdate(key, value) {
|
||||
return { type: T.AUTH.SIGN_IN_FORM_UPDATE, key, value };
|
||||
}
|
||||
|
||||
export function emailSignInStart() {
|
||||
return { type: T.AUTH.SIGN_IN_START };
|
||||
}
|
||||
|
||||
export function emailSignInComplete(user) {
|
||||
return { type: T.AUTH.SIGN_IN_COMPLETE, user };
|
||||
}
|
||||
|
||||
export function emailSignInError(errors) {
|
||||
return { type: T.AUTH.SIGN_IN_ERROR, errors };
|
||||
}
|
||||
|
||||
export function emailSignIn(body) {
|
||||
return dispatch => {
|
||||
// save previous endpoint key in case of failure
|
||||
var prevEndpointKey = getCurrentEndpointKey();
|
||||
|
||||
const endpointKey = 'default';
|
||||
|
||||
// necessary for fetch to recognize the response as an api request
|
||||
setCurrentEndpointKey(endpointKey);
|
||||
var currentEndpointKey = getCurrentEndpointKey();
|
||||
|
||||
dispatch(storeCurrentEndpointKey(currentEndpointKey));
|
||||
|
||||
dispatch(emailSignInStart());
|
||||
|
||||
return fetch(getEmailSignInUrl(currentEndpointKey), {
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
method: "post",
|
||||
body: root.JSON.stringify(body)
|
||||
})
|
||||
.then(parseResponse)
|
||||
.then(function(K) {
|
||||
debugger;
|
||||
return K;
|
||||
})
|
||||
.then((user) => dispatch(emailSignInComplete(user)))
|
||||
.catch((errors) => {
|
||||
// revert endpoint key to what it was before failed request
|
||||
setCurrentEndpointKey(prevEndpointKey);
|
||||
dispatch(storeCurrentEndpointKey(prevEndpointKey));
|
||||
return dispatch(emailSignInError(errors));
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -10,7 +10,6 @@ export function requireAuthentication(Component) {
|
||||
class AuthComponent extends React.Component {
|
||||
|
||||
componentWillMount() {
|
||||
debugger;
|
||||
if (!this.props.isAuthenticated) {
|
||||
|
||||
// redirect to login and add next param so we can redirect again after login
|
||||
@@ -20,6 +19,11 @@ export function requireAuthentication(Component) {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.isAuthenticated) {
|
||||
return (<div className="panel">
|
||||
<h2 className="text-danger">No anonymous access!</h2>
|
||||
</div>)
|
||||
}
|
||||
// render the component that requires auth (passed to this wrapper)
|
||||
return (
|
||||
<Component {...this.props} />
|
||||
@@ -29,12 +33,12 @@ export function requireAuthentication(Component) {
|
||||
|
||||
const mapStateToProps =
|
||||
(state) => {
|
||||
debugger;
|
||||
console.info('state', state);
|
||||
return ({
|
||||
token: state.auth.token,
|
||||
//token: state.auth.token,
|
||||
//userName: state.auth.userName,
|
||||
//isAuthenticated: state.auth.isAuthenticated
|
||||
isAuthenticated: state.auth.getIn(['user', 'isSignedIn'])
|
||||
isAuthenticated: state.auth.user.isSignedIn
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
import React, { PropTypes } from "react";
|
||||
import { Grid, Col, Navbar, NavItem, Nav, NavbarBrand, Footer } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import { SignOutButton } from "redux-auth/bootstrap-theme";
|
||||
//import { SignOutButton } from "redux-auth/bootstrap-theme";
|
||||
|
||||
const SignOutButton = () => (<div>SignOutButton!</div>);
|
||||
|
||||
|
||||
//if (!global.__SERVER__ && !global.__TEST__) {
|
||||
// require("../../styles/main.scss");
|
||||
@@ -30,7 +33,7 @@ class Container extends React.Component {
|
||||
<NavItem eventKey={2}>Account</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
<Nav right={true}>
|
||||
<Nav pullRight={true}>
|
||||
<SignOutButton></SignOutButton>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
* Created by andrew on 15/02/16.
|
||||
*/
|
||||
import React, {PropTypes} from "react";
|
||||
import auth from "redux-auth";
|
||||
//import auth from "redux-auth";
|
||||
import * as BS from "react-bootstrap";
|
||||
|
||||
import Input from "./Input";
|
||||
import ButtonLoader from "./ButtonLoader";
|
||||
import { emailSignInFormUpdate, emailSignIn } from "redux-auth";
|
||||
//import { emailSignInFormUpdate, emailSignIn } from "redux-auth";
|
||||
import { Glyphicon } from "react-bootstrap";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { emailSignInFormUpdate, emailSignIn } from "../../actions/signIn";
|
||||
|
||||
/*
|
||||
<Input type="password"
|
||||
label="Password"
|
||||
@@ -23,7 +25,21 @@ import { connect } from "react-redux";
|
||||
{...this.props.inputProps.password} />
|
||||
*/
|
||||
|
||||
function read(src, path = '', defaultVal = '') {
|
||||
const [pathItem = null, ...rest] = path.split('.');
|
||||
if (pathItem === null ) {
|
||||
return src;
|
||||
} else if (rest.length === 0) {
|
||||
if (!src) { return defaultVal; }
|
||||
return src[pathItem];
|
||||
} else {
|
||||
if (!src) { return defaultVal; }
|
||||
return read(src[pathItem], rest.join('.'));
|
||||
}
|
||||
}
|
||||
|
||||
class EmailSignInForm extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
endpoint: PropTypes.string,
|
||||
inputProps: PropTypes.shape({
|
||||
@@ -41,28 +57,22 @@ class EmailSignInForm extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
getEndpoint () {
|
||||
return (
|
||||
this.props.endpoint ||
|
||||
this.props.auth.getIn(["configure", "currentEndpointKey"]) ||
|
||||
this.props.auth.getIn(["configure", "defaultEndpointKey"])
|
||||
);
|
||||
}
|
||||
|
||||
handleInput (key, val) {
|
||||
this.props.dispatch(emailSignInFormUpdate(this.getEndpoint(), key, val));
|
||||
this.props.dispatch(emailSignInFormUpdate(key, val));
|
||||
}
|
||||
|
||||
handleSubmit (event) {
|
||||
event.preventDefault();
|
||||
let formData = this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form"]).toJS();
|
||||
this.props.dispatch(emailSignIn(formData, this.getEndpoint()));
|
||||
let formData = { ...this.props.auth.signIn.form };
|
||||
this.props.dispatch(emailSignIn(formData, null));
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
try {
|
||||
let disabled = (
|
||||
this.props.auth.getIn(["user", "isSignedIn"]) ||
|
||||
this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "loading"])
|
||||
this.props.auth.user.isSignedIn ||
|
||||
this.props.auth.signIn.loading
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -74,12 +84,12 @@ class EmailSignInForm extends React.Component {
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
disabled={disabled}
|
||||
value={this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "form", "email"])}
|
||||
errors={this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "errors", "email"])}
|
||||
value={read(this.props.auth, 'signIn.form.email', '')}
|
||||
errors={read(this.props.auth, 'signIn.errors.email', null)}
|
||||
onChange={this.handleInput.bind(this, "email")}
|
||||
{...this.props.inputProps.email} />
|
||||
|
||||
<ButtonLoader loading={this.props.auth.getIn(["emailSignIn", this.getEndpoint(), "loading"])}
|
||||
<ButtonLoader loading={read(this.props.auth, 'signIn.loading', false)}
|
||||
type="submit"
|
||||
icon={<Glyphicon glyph="log-in" />}
|
||||
className='email-sign-in-submit pull-right'
|
||||
@@ -90,6 +100,10 @@ class EmailSignInForm extends React.Component {
|
||||
</ButtonLoader>
|
||||
</form>
|
||||
);
|
||||
} catch (ex){
|
||||
console.error('Render exception: ', ex);
|
||||
return [' ERROR '];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
* Created by andrew on 15/02/16.
|
||||
*/
|
||||
import React, {PropTypes} from "react";
|
||||
import auth from "redux-auth";
|
||||
//import auth from "redux-auth";
|
||||
import Input from "./Input";
|
||||
import ButtonLoader from "./ButtonLoader";
|
||||
import { emailSignUpFormUpdate, emailSignUp } from "redux-auth";
|
||||
//import { emailSignUpFormUpdate, emailSignUp } from "redux-auth";
|
||||
import IndexPanel from "./../../components/partials/IndexPanel";
|
||||
import { customerInfoMap } from '../../entities/formToPayloadMappers';
|
||||
|
||||
import { Glyphicon } from "react-bootstrap";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
const emailSignUpFormUpdate = () => {debugger; },
|
||||
emailSignUp = () => {debugger; };
|
||||
|
||||
|
||||
class EmailSignUpForm extends React.Component {
|
||||
static propTypes = {
|
||||
endpoint: PropTypes.string,
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
|
||||
const authInitialState = {
|
||||
loading: false,
|
||||
valid: false,
|
||||
errors: null
|
||||
};
|
||||
export const authReducer = (state = {...authInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.AUTHENTICATE_START: return {...state, loading: true };
|
||||
case T.AUTH.AUTHENTICATE_COMPLETE: return {...state, loading: false,
|
||||
errors: null,
|
||||
valid: true};
|
||||
case T.AUTH.AUTHENTICATE_ERROR: return {...state, loading: false,
|
||||
errors: "Invalid token",
|
||||
valid: false};
|
||||
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,37 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
|
||||
const configInitialState = {
|
||||
loading: true,
|
||||
errors: null,
|
||||
config: null
|
||||
};
|
||||
|
||||
export const configReducer = (state = {...configInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.CONFIGURE_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.CONFIGURE_COMPLETE:
|
||||
const { config } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors: null,
|
||||
config
|
||||
};
|
||||
case T.AUTH.CONFIGURE_ERROR:
|
||||
const { errors } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -1,143 +1,20 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
import { combineReducers } from 'redux';
|
||||
|
||||
const configInitialState = {
|
||||
loading: true,
|
||||
errors: null,
|
||||
config: null
|
||||
};
|
||||
import { configReducer } from './configure';
|
||||
import { authReducer } from './authenticate';
|
||||
import { signInReducer } from './signin';
|
||||
import { signOutReducer } from './signout';
|
||||
import { userReducer } from './user';
|
||||
|
||||
export const configReducer = (state = {...configInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.CONFIGURE_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.CONFIGURE_COMPLETE:
|
||||
const { config } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors: null,
|
||||
config
|
||||
};
|
||||
case T.AUTH.CONFIGURE_ERROR:
|
||||
const { errors } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
const authStateReducer = combineReducers({
|
||||
configure: configReducer,
|
||||
signIn: signInReducer,
|
||||
signOut: signOutReducer,
|
||||
authentication: authReducer,
|
||||
user: userReducer
|
||||
});
|
||||
|
||||
const authInitialState = {
|
||||
loading: false,
|
||||
valid: false,
|
||||
errors: null
|
||||
};
|
||||
export const authReducer = (state = {...authInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.AUTHENTICATE_START: return {...state, loading: true };
|
||||
case T.AUTH.AUTHENTICATE_COMPLETE: return {...state, loading: false,
|
||||
errors: null,
|
||||
valid: true};
|
||||
case T.AUTH.AUTHENTICATE_ERROR: return {...state, loading: false,
|
||||
errors: "Invalid token",
|
||||
valid: false};
|
||||
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
const signInInitialState = {
|
||||
loading: false,
|
||||
errors: null,
|
||||
form: {}
|
||||
};
|
||||
export const signInReducer = (state = { ...signInInitialState, form: {...signInInitialState.form }}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.SIGN_IN_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.SIGN_IN_COMPLETE:
|
||||
return {
|
||||
...signInInitialState,
|
||||
form: { ...signInInitialState.form }
|
||||
};
|
||||
case T.AUTH.SIGN_IN_ERROR:
|
||||
const { errors } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors
|
||||
};
|
||||
case T.AUTH.SIGN_IN_FORM_UPDATE:
|
||||
const { key, value } = action;
|
||||
return {
|
||||
...state,
|
||||
form: {
|
||||
...state.form,
|
||||
[key]: value
|
||||
}
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
const signOutInitialState = {
|
||||
loading: false,
|
||||
errors: null
|
||||
};
|
||||
|
||||
export const signOutReducer = (state = {...signOutInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.SIGN_OUT_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.SIGN_OUT_COMPLETE:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors: null
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
const userInitalState = {
|
||||
attributes: null,
|
||||
isSignedIn: false
|
||||
};
|
||||
|
||||
export const userReducer = (state = {...userInitalState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.AUTHENTICATE_COMPLETE:
|
||||
const { user } = action;
|
||||
return {...state,
|
||||
attributes: user,
|
||||
isSignedIn: true
|
||||
};
|
||||
case T.AUTH.SIGN_IN_COMPLETE:
|
||||
const { user } = action;
|
||||
return {...state,
|
||||
attributes: user.data,
|
||||
isSignedIn: true
|
||||
};
|
||||
case T.AUTH.SIGN_OUT_COMPLETE:
|
||||
case T.AUTH.AUTHENTICATE_ERROR:
|
||||
return {
|
||||
...userInitalState
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
export default authStateReducer;
|
||||
@@ -1,3 +1,42 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
|
||||
|
||||
const signInInitialState = {
|
||||
loading: false,
|
||||
errors: null,
|
||||
form: {}
|
||||
};
|
||||
export const signInReducer = (state = { ...signInInitialState, form: {...signInInitialState.form }}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.SIGN_IN_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.SIGN_IN_COMPLETE:
|
||||
return {
|
||||
...signInInitialState,
|
||||
form: { ...signInInitialState.form }
|
||||
};
|
||||
case T.AUTH.SIGN_IN_ERROR:
|
||||
const { errors } = action;
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors
|
||||
};
|
||||
case T.AUTH.SIGN_IN_FORM_UPDATE:
|
||||
const { key, value } = action;
|
||||
return {
|
||||
...state,
|
||||
form: {
|
||||
...state.form,
|
||||
[key]: value
|
||||
}
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,26 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
|
||||
const signOutInitialState = {
|
||||
loading: false,
|
||||
errors: null
|
||||
};
|
||||
|
||||
export const signOutReducer = (state = {...signOutInitialState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.SIGN_OUT_START:
|
||||
return {
|
||||
...state,
|
||||
loading: true
|
||||
};
|
||||
case T.AUTH.SIGN_OUT_COMPLETE:
|
||||
return {
|
||||
...state,
|
||||
loading: false,
|
||||
errors: null
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
/**
|
||||
* Created by andrew on 25/02/16.
|
||||
*/
|
||||
import T from '../../constants/ACTION_TYPES';
|
||||
|
||||
const userInitalState = {
|
||||
attributes: null,
|
||||
isSignedIn: false
|
||||
};
|
||||
|
||||
export const userReducer = (state = {...userInitalState}, action) => {
|
||||
switch(action.type) {
|
||||
case T.AUTH.AUTHENTICATE_COMPLETE: {
|
||||
const { user } = action;
|
||||
return {...state,
|
||||
attributes: user,
|
||||
isSignedIn: true
|
||||
};
|
||||
}
|
||||
|
||||
case T.AUTH.SIGN_IN_COMPLETE: {
|
||||
|
||||
const { user } = action;
|
||||
return {...state,
|
||||
attributes: user.data,
|
||||
isSignedIn: true
|
||||
};
|
||||
}
|
||||
case T.AUTH.SIGN_OUT_COMPLETE:
|
||||
case T.AUTH.AUTHENTICATE_ERROR:
|
||||
return {
|
||||
...userInitalState
|
||||
};
|
||||
default: return state;
|
||||
}
|
||||
};
|
||||
117
js-frontend/src/utils/clientSettings.js
Normal file
117
js-frontend/src/utils/clientSettings.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
|
||||
import * as C from "./constants";
|
||||
import fetch from "./fetch";
|
||||
|
||||
import parseEndpointConfig from "./parseEndpointConfig";
|
||||
import { setEndpointKeys } from "../actions/configure";
|
||||
|
||||
import {
|
||||
getCurrentSettings,
|
||||
setCurrentSettings,
|
||||
getInitialEndpointKey,
|
||||
setDefaultEndpointKey,
|
||||
setCurrentEndpoint,
|
||||
setCurrentEndpointKey,
|
||||
retrieveData,
|
||||
persistData
|
||||
} from "./sessionStorage";
|
||||
|
||||
// can't use "window" with node app
|
||||
var root = Function("return this")() || (42, eval)("this");
|
||||
|
||||
const defaultSettings = {
|
||||
//proxyIf: function() { return false; },
|
||||
//proxyUrl: "/proxy",
|
||||
forceHardRedirect: false,
|
||||
storage: "cookies",
|
||||
cookieExpiry: 14,
|
||||
cookiePath: "/",
|
||||
initialCredentials: null,
|
||||
|
||||
passwordResetSuccessUrl: function() {
|
||||
return root.location.href;
|
||||
},
|
||||
|
||||
confirmationSuccessUrl: function() {
|
||||
return root.location.href;
|
||||
},
|
||||
|
||||
tokenFormat: {
|
||||
"access-token": "{{ access-token }}"
|
||||
//"token-type": "Bearer",
|
||||
//client: "{{ client }}",
|
||||
//expiry: "{{ expiry }}",
|
||||
//uid: "{{ uid }}"
|
||||
},
|
||||
|
||||
parseExpiry: function(headers){
|
||||
// convert from ruby time (seconds) to js time (millis)
|
||||
return (parseInt(headers["expiry"], 10) * 1000) || null;
|
||||
},
|
||||
|
||||
handleLoginResponse: function(resp) {
|
||||
return resp.data;
|
||||
},
|
||||
|
||||
handleAccountUpdateResponse: function(resp) {
|
||||
return resp.data;
|
||||
},
|
||||
|
||||
handleTokenValidationResponse: function(resp) {
|
||||
return resp.data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// save session configuration
|
||||
export function applyConfig({ dispatch, endpoint={}, settings={}, reset=false } = {}) {
|
||||
let currentEndpointKey;
|
||||
|
||||
if (reset) {
|
||||
resetConfig();
|
||||
}
|
||||
|
||||
if (settings.initialCredentials) {
|
||||
currentEndpointKey = settings.initialCredentials.currentEndpointKey;
|
||||
}
|
||||
|
||||
setCurrentSettings({ ...defaultSettings, ...settings });
|
||||
|
||||
let { defaultEndpointKey, currentEndpoint } = parseEndpointConfig(
|
||||
endpoint, getInitialEndpointKey()
|
||||
);
|
||||
|
||||
if (!currentEndpointKey) {
|
||||
currentEndpointKey = defaultEndpointKey;
|
||||
}
|
||||
|
||||
// persist default config key with session storage
|
||||
setDefaultEndpointKey(defaultEndpointKey);
|
||||
setCurrentEndpoint(currentEndpoint);
|
||||
|
||||
dispatch(setEndpointKeys(Object.keys(currentEndpoint), currentEndpointKey, defaultEndpointKey));
|
||||
setCurrentEndpointKey(currentEndpointKey);
|
||||
|
||||
|
||||
if (getCurrentSettings().initialCredentials) {
|
||||
|
||||
// skip initial headers check (i.e. check was already done server-side)
|
||||
let { user, headers, config } = getCurrentSettings().initialCredentials;
|
||||
persistData(C.SAVED_CREDS_KEY, headers);
|
||||
return Promise.resolve(user);
|
||||
}
|
||||
|
||||
const savedCreds = retrieveData(C.SAVED_CREDS_KEY);
|
||||
|
||||
if (savedCreds) {
|
||||
// verify session credentials with API
|
||||
debugger;
|
||||
return fetch(savedCreds)
|
||||
}
|
||||
|
||||
return Promise.reject({ reason: "No credentials." })
|
||||
|
||||
}
|
||||
7
js-frontend/src/utils/constants.js
Normal file
7
js-frontend/src/utils/constants.js
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
export const INITIAL_CONFIG_KEY = "default";
|
||||
export const DEFAULT_CONFIG_KEY = "defaultConfigKey";
|
||||
export const SAVED_CONFIG_KEY = "currentConfigName";
|
||||
export const SAVED_CREDS_KEY = "authHeaders";
|
||||
79
js-frontend/src/utils/fetch.js
Normal file
79
js-frontend/src/utils/fetch.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import originalFetch from "isomorphic-fetch";
|
||||
import * as C from "./constants";
|
||||
//import extend from "extend";
|
||||
import {
|
||||
getApiUrl,
|
||||
retrieveData,
|
||||
persistData,
|
||||
getTokenFormat,
|
||||
getSessionEndpointKey,
|
||||
isApiRequest
|
||||
} from "./sessionStorage";
|
||||
|
||||
|
||||
function getAuthHeaders(url) {
|
||||
if (isApiRequest(url)) {
|
||||
// fetch current auth headers from storage
|
||||
const currentHeaders = retrieveData(C.SAVED_CREDS_KEY) || {},
|
||||
nextHeaders = {};
|
||||
|
||||
// bust IE cache
|
||||
nextHeaders["If-Modified-Since"] = "Mon, 26 Jul 1997 05:00:00 GMT";
|
||||
|
||||
// set header for each key in `tokenFormat` config
|
||||
for (var key in getTokenFormat()) {
|
||||
nextHeaders[key] = currentHeaders[key];
|
||||
}
|
||||
|
||||
return nextHeaders;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function updateAuthCredentials(resp) {
|
||||
// check config apiUrl matches the current response url
|
||||
if (isApiRequest(resp.url)) {
|
||||
// set header for each key in `tokenFormat` config
|
||||
var newHeaders = {};
|
||||
|
||||
// set flag to ensure that we don't accidentally nuke the headers
|
||||
// if the response tokens aren't sent back from the API
|
||||
var blankHeaders = true;
|
||||
|
||||
// set header key + val for each key in `tokenFormat` config
|
||||
for (var key in getTokenFormat()) {
|
||||
newHeaders[key] = resp.headers.get(key);
|
||||
|
||||
if (newHeaders[key]) {
|
||||
blankHeaders = false;
|
||||
}
|
||||
}
|
||||
|
||||
// persist headers for next request
|
||||
if (!blankHeaders) {
|
||||
persistData(C.SAVED_CREDS_KEY, newHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
export default function (url, options = {}) {
|
||||
|
||||
if (!options.headers) {
|
||||
options.headers = {}
|
||||
}
|
||||
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
...getAuthHeaders(url)
|
||||
};
|
||||
|
||||
//extend(options.headers, getAuthHeaders(url));
|
||||
return originalFetch(url, options)
|
||||
.then(resp => updateAuthCredentials(resp));
|
||||
}
|
||||
11
js-frontend/src/utils/handleFetchResponse.js
Normal file
11
js-frontend/src/utils/handleFetchResponse.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
export function parseResponse (response) {
|
||||
let json = response.json();
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return json;
|
||||
} else {
|
||||
return json.then(err => Promise.reject(err));
|
||||
}
|
||||
}
|
||||
65
js-frontend/src/utils/parseEndpointConfig.js
Normal file
65
js-frontend/src/utils/parseEndpointConfig.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import * as C from "./constants";
|
||||
|
||||
// base endpoint that other endpoints extend from
|
||||
const defaultEndpoint = {
|
||||
apiUrl: "/api",
|
||||
signOutPath: "/auth/sign_out",
|
||||
emailSignInPath: "/auth/sign_in",
|
||||
emailRegistrationPath: "/auth",
|
||||
accountUpdatePath: "/auth",
|
||||
accountDeletePath: "/auth",
|
||||
passwordResetPath: "/auth/password",
|
||||
passwordUpdatePath: "/auth/password",
|
||||
tokenValidationPath: "/auth/validate_token",
|
||||
|
||||
authProviderPaths: {
|
||||
github: "/auth/github",
|
||||
facebook: "/auth/facebook",
|
||||
google: "/auth/google_oauth2"
|
||||
}
|
||||
};
|
||||
|
||||
function getFirstObjectKey (obj) {
|
||||
for (var key in obj) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
export default function parseEndpointConfig(endpoint, defaultEndpointKey = null) {
|
||||
// normalize so opts is always an array of objects
|
||||
if (endpoint.constructor !== Array) {
|
||||
// single config will always be called 'default' unless set
|
||||
// by previous session
|
||||
defaultEndpointKey = C.INITIAL_CONFIG_KEY;
|
||||
|
||||
// config should look like {default: {...}}
|
||||
var defaultConfig = {};
|
||||
defaultConfig[defaultEndpointKey] = endpoint;
|
||||
|
||||
// endpoint should look like [{default: {...}}]
|
||||
endpoint = [defaultConfig];
|
||||
}
|
||||
|
||||
let currentEndpoint = {};
|
||||
|
||||
// iterate over config items, extend each from defaults
|
||||
for (var i = 0; i < endpoint.length; i++) {
|
||||
var configName = getFirstObjectKey(endpoint[i]);
|
||||
|
||||
// set first as default config
|
||||
if (!defaultEndpointKey) {
|
||||
defaultEndpointKey = configName;
|
||||
}
|
||||
|
||||
// save config to `configs` hash
|
||||
currentEndpoint[configName] = {
|
||||
...defaultEndpoint,
|
||||
...endpoint[i][configName]
|
||||
};
|
||||
}
|
||||
|
||||
return { defaultEndpointKey, currentEndpoint };
|
||||
}
|
||||
134
js-frontend/src/utils/parseUrl.js
Normal file
134
js-frontend/src/utils/parseUrl.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import querystring from "querystring";
|
||||
|
||||
export function normalizeTokenKeys (params) {
|
||||
// normalize keys
|
||||
if (params.token) {
|
||||
params["access-token"] = params.token;
|
||||
delete params.token;
|
||||
}
|
||||
if (params.auth_token) {
|
||||
params["access-token"] = params.auth_token;
|
||||
delete params.auth_token;
|
||||
}
|
||||
if (params.client_id) {
|
||||
params.client = params.client_id;
|
||||
delete params.client_id;
|
||||
}
|
||||
if (params.config) {
|
||||
params.endpointKey = params.config;
|
||||
delete params.config;
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
const getAnchorSearch = function(location) {
|
||||
const rawAnchor = location.anchor || "",
|
||||
arr = rawAnchor.split("?");
|
||||
return (arr.length > 1) ? arr[1] : null;
|
||||
};
|
||||
|
||||
const getSearchQs = function(location) {
|
||||
const rawQs = location.search || "",
|
||||
qs = rawQs.replace("?", ""),
|
||||
qsObj = (qs) ? querystring.parse(qs) : {};
|
||||
|
||||
return qsObj;
|
||||
};
|
||||
|
||||
const getAnchorQs = function(location) {
|
||||
const anchorQs = getAnchorSearch(location),
|
||||
anchorQsObj = (anchorQs) ? querystring.parse(anchorQs) : {};
|
||||
|
||||
return anchorQsObj;
|
||||
};
|
||||
|
||||
const stripKeys = function(obj, keys) {
|
||||
for (var q in keys) {
|
||||
delete obj[keys[q]];
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
export function getAllParams (location) {
|
||||
return {
|
||||
...getAnchorQs(location),
|
||||
...getSearchQs(location)
|
||||
};
|
||||
};
|
||||
|
||||
const buildCredentials = function(location, keys) {
|
||||
const params = getAllParams(location);
|
||||
let authHeaders = {};
|
||||
|
||||
for (var key of keys) {
|
||||
authHeaders[key] = params[key];
|
||||
}
|
||||
|
||||
return normalizeTokenKeys(authHeaders);
|
||||
};
|
||||
|
||||
|
||||
// this method is tricky. we want to reconstruct the current URL with the
|
||||
// following conditions:
|
||||
// 1. search contains none of the supplied keys
|
||||
// 2. anchor search (i.e. `#/?key=val`) contains none of the supplied keys
|
||||
// 3. all of the keys NOT supplied are presevered in their original form
|
||||
// 4. url protocol, host, and path are preserved
|
||||
const getLocationWithoutParams = function(currentLocation, keys) {
|
||||
// strip all values from both actual and anchor search params
|
||||
let newSearch = querystring.stringify(stripKeys(getSearchQs(currentLocation), keys)),
|
||||
newAnchorQs = querystring.stringify(stripKeys(getAnchorQs(currentLocation), keys)),
|
||||
newAnchor = (currentLocation.hash || "").split("?")[0];
|
||||
|
||||
if (newSearch) {
|
||||
newSearch = "?" + newSearch;
|
||||
}
|
||||
|
||||
if (newAnchorQs) {
|
||||
newAnchor += "?" + newAnchorQs;
|
||||
}
|
||||
|
||||
if (newAnchor && !newAnchor.match(/^#/)) {
|
||||
newAnchor = "#/" + newAnchor;
|
||||
}
|
||||
|
||||
// reconstruct location with stripped auth keys
|
||||
const newLocation = currentLocation.pathname + newSearch + newAnchor;
|
||||
|
||||
return newLocation;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default function getRedirectInfo(currentLocation) {
|
||||
if (!currentLocation) {
|
||||
return {};
|
||||
} else {
|
||||
let authKeys = [
|
||||
"access-token",
|
||||
"token",
|
||||
"auth_token",
|
||||
"config",
|
||||
"client",
|
||||
"client_id",
|
||||
"expiry",
|
||||
"uid",
|
||||
"reset_password",
|
||||
"account_confirmation_success"
|
||||
];
|
||||
|
||||
var authRedirectHeaders = buildCredentials(currentLocation, authKeys);
|
||||
var authRedirectPath = getLocationWithoutParams(currentLocation, authKeys);
|
||||
|
||||
if (authRedirectPath !== currentLocation) {
|
||||
return {authRedirectHeaders, authRedirectPath};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
4
js-frontend/src/utils/root.js
Normal file
4
js-frontend/src/utils/root.js
Normal file
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Created by andrew on 27/02/16.
|
||||
*/
|
||||
export default Function("return this")() || (42, eval)("this");
|
||||
176
js-frontend/src/utils/sessionStorage.js
Normal file
176
js-frontend/src/utils/sessionStorage.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Created by andrew on 26/02/16.
|
||||
*/
|
||||
import Cookies from "js-cookie";
|
||||
import * as C from "./constants";
|
||||
//import "babel-polyfill";
|
||||
|
||||
|
||||
// even though this code shouldn't be used server-side, node will throw
|
||||
// errors if "window" is used
|
||||
var root = Function("return this")() || (42, eval)("this");
|
||||
|
||||
// stateful variables that persist throughout session
|
||||
root.authState = {
|
||||
currentSettings: {},
|
||||
currentEndpoint: {},
|
||||
defaultEndpointKey: null
|
||||
};
|
||||
|
||||
export function setCurrentSettings (s) {
|
||||
root.authState.currentSettings = s;
|
||||
}
|
||||
|
||||
export function getCurrentSettings () {
|
||||
return root.authState.currentSettings;
|
||||
}
|
||||
|
||||
export function setCurrentEndpoint (e) {
|
||||
root.authState.currentEndpoint = e;
|
||||
}
|
||||
|
||||
export function getCurrentEndpoint () {
|
||||
return root.authState.currentEndpoint;
|
||||
}
|
||||
|
||||
export function setCurrentEndpointKey (k) {
|
||||
persistData(C.SAVED_CONFIG_KEY, k || getDefaultEndpointKey());
|
||||
}
|
||||
|
||||
export function getCurrentEndpointKey () {
|
||||
return retrieveData(C.SAVED_CONFIG_KEY) || getDefaultEndpointKey();
|
||||
}
|
||||
|
||||
export function setDefaultEndpointKey (k) {
|
||||
persistData(C.DEFAULT_CONFIG_KEY, k);
|
||||
}
|
||||
|
||||
export function getDefaultEndpointKey () {
|
||||
return retrieveData(C.DEFAULT_CONFIG_KEY);
|
||||
}
|
||||
|
||||
// reset stateful variables
|
||||
export function resetConfig () {
|
||||
root.authState = root.authState || {};
|
||||
root.authState.currentSettings = {};
|
||||
root.authState.currentEndpoint = {};
|
||||
destroySession();
|
||||
}
|
||||
|
||||
|
||||
export function destroySession () {
|
||||
var sessionKeys = [
|
||||
C.SAVED_CREDS_KEY,
|
||||
C.SAVED_CONFIG_KEY
|
||||
];
|
||||
|
||||
for (var key in sessionKeys) {
|
||||
key = sessionKeys[key];
|
||||
|
||||
// kill all local storage keys
|
||||
if (root.localStorage) {
|
||||
root.localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
// remove from base path in case config is not specified
|
||||
Cookies.remove(key, {
|
||||
path: root.authState.currentSettings.cookiePath || "/"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function unescapeQuotes (val) {
|
||||
return val && val.replace(/("|')/g, "");
|
||||
};
|
||||
|
||||
export function getInitialEndpointKey () {
|
||||
return unescapeQuotes(
|
||||
Cookies.get(C.SAVED_CONFIG_KEY) ||
|
||||
(root.localStorage && root.localStorage.getItem(C.SAVED_CONFIG_KEY))
|
||||
);
|
||||
}
|
||||
|
||||
export function isApiRequest(url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: make this really work
|
||||
export function getSessionEndpointKey (k) {
|
||||
let key = k || getCurrentEndpointKey();
|
||||
if (!key) {
|
||||
throw "You must configure redux-auth before use.";
|
||||
} else {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
export function getSessionEndpoint (k) {
|
||||
return getCurrentEndpoint()[getSessionEndpointKey(k)];
|
||||
}
|
||||
|
||||
|
||||
// only should work for current session
|
||||
export function getSignOutUrl (endpointKey) {
|
||||
return `${getApiUrl(endpointKey)}${getSessionEndpoint(endpointKey).signOutPath}`
|
||||
}
|
||||
|
||||
export function getEmailSignInUrl (endpointKey) {
|
||||
return `${getApiUrl(endpointKey)}${getSessionEndpoint(endpointKey).emailSignInPath}`
|
||||
}
|
||||
|
||||
export function getEmailSignUpUrl (endpointKey) {
|
||||
return `${getApiUrl(endpointKey)}${getSessionEndpoint(endpointKey).emailRegistrationPath}?config_name=${endpointKey}`
|
||||
}
|
||||
|
||||
|
||||
export function getApiUrl(key) {
|
||||
let configKey = getSessionEndpointKey(key);
|
||||
return root.authState.currentEndpoint[configKey].apiUrl;
|
||||
}
|
||||
|
||||
export function getTokenFormat() {
|
||||
return root.authState.currentSettings.tokenFormat;
|
||||
}
|
||||
|
||||
|
||||
export function persistData (key, val) {
|
||||
val = root.JSON.stringify(val);
|
||||
|
||||
switch (root.authState.currentSettings.storage) {
|
||||
case "localStorage":
|
||||
root.localStorage.setItem(key, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
Cookies.set(key, val, {
|
||||
expires: root.authState.currentSettings.cookieExpiry,
|
||||
path: root.authState.currentSettings.cookiePath
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function retrieveData (key) {
|
||||
var val = null;
|
||||
|
||||
switch (root.authState.currentSettings.storage) {
|
||||
|
||||
case "localStorage":
|
||||
val = root.localStorage && root.localStorage.getItem(key);
|
||||
break;
|
||||
|
||||
default:
|
||||
val = Cookies.get(key);
|
||||
break;
|
||||
}
|
||||
|
||||
// if value is a simple string, the parser will fail. in that case, simply
|
||||
// unescape the quotes and return the string.
|
||||
try {
|
||||
// return parsed json response
|
||||
return JSON.parse(val);
|
||||
} catch (err) {
|
||||
// unescape quotes
|
||||
return unescapeQuotes(val);
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,6 @@ import React from "react";
|
||||
import { PageHeader, OverlayTrigger, Tooltip, Grid, Col, Row, Nav, NavItem, ButtonGroup, Button, Table } from "react-bootstrap";
|
||||
import { Link, IndexLink} from "react-router";
|
||||
import { connect } from "react-redux";
|
||||
import * as BSTheme from "redux-auth/bootstrap-theme";
|
||||
//import * as DefaultTheme from "redux-auth";
|
||||
import Select from "react-select";
|
||||
import * as Modals from './modals';
|
||||
@@ -218,10 +217,10 @@ class MyAccounts extends React.Component {
|
||||
|
||||
export default connect(({auth, demoUi = new Map()}) => {
|
||||
return ({
|
||||
currentUserUid: auth.getIn(["user", "attributes", "provider"]) || "none",
|
||||
currentUserProvider: auth.getIn(["user", "attributes", "uid"]) || "none",
|
||||
currentUserEndpoint: auth.getIn(["user", "endpointKey"]) || "none",
|
||||
currentUserUid: auth.user && auth.user.attributes && auth.user.attributes.provider || "none",
|
||||
currentUserProvider: auth.user && auth.user.attributes && auth.user.attributes.uid || "none",
|
||||
currentUserEndpoint: "none",
|
||||
//theme: demoUi.get("theme"),
|
||||
pageEndpoint: demoUi.get("endpoint")
|
||||
pageEndpoint: null
|
||||
})
|
||||
})(MyAccounts);
|
||||
@@ -21,10 +21,8 @@ import EmailSignInForm from "../controls/bootstrap/EmailSignInForm";
|
||||
export class SignIn extends React.Component {
|
||||
|
||||
componentWillMount() {
|
||||
debugger;
|
||||
if (this.props.isAuthenticated) {
|
||||
|
||||
debugger;
|
||||
|
||||
//// redirect to login and add next param so we can redirect again after login
|
||||
//const redirectAfterLogin = this.props.location.pathname;
|
||||
|
||||
@@ -30,13 +30,14 @@ export default (DEBUG, PATH, PORT=3000) => ({
|
||||
// Load ES6/JSX
|
||||
{ test: /\.jsx?$/,
|
||||
include: [
|
||||
path.resolve(__dirname, "src"),
|
||||
path.resolve(__dirname, "node_modules/redux-auth/src/views/bootstrap")
|
||||
path.resolve(__dirname, "src")
|
||||
//,
|
||||
//path.resolve(__dirname, "node_modules/redux-auth/src/views/bootstrap")
|
||||
],
|
||||
loader: "babel-loader",
|
||||
query: {
|
||||
plugins: ['transform-runtime'],
|
||||
presets: ['es2015', 'stage-0', 'react'],
|
||||
presets: ['es2015', 'react', 'stage-0']
|
||||
}
|
||||
},
|
||||
|
||||
@@ -78,11 +79,11 @@ export default (DEBUG, PATH, PORT=3000) => ({
|
||||
mangle: {screw_ie8: true, keep_fnames: true}
|
||||
}),
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
new webpack.optimize.AggressiveMergingPlugin(),
|
||||
new webpack.optimize.AggressiveMergingPlugin()
|
||||
],
|
||||
|
||||
resolveLoader: {
|
||||
root: path.join(__dirname, "node_modules"),
|
||||
root: path.join(__dirname, "node_modules")
|
||||
},
|
||||
|
||||
resolve: {
|
||||
@@ -97,6 +98,6 @@ export default (DEBUG, PATH, PORT=3000) => ({
|
||||
},
|
||||
|
||||
// Allow to omit extensions when requiring these files
|
||||
extensions: ["", ".js", ".jsx"],
|
||||
extensions: ["", ".js", ".jsx"]
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user