From e78b36825ebbf09a85d77a985a1bde18b30e6023 Mon Sep 17 00:00:00 2001 From: Jonathan Ruddell Date: Sat, 18 Mar 2017 13:21:24 -0700 Subject: [PATCH] finalize auth process add flow for loading auth token add token to api auth header on app startup add AppStateRedux to track hydration state simplify ejs conditions --- boilerplate/App/Config/ReduxPersist.js | 2 +- boilerplate/App/Containers/RootContainer.js | 18 +++++++++++-- boilerplate/App/Redux/AppStateRedux.js | 29 +++++++++++++++++++++ boilerplate/App/Redux/LoginRedux.js.ejs | 21 +++++++++------ boilerplate/App/Redux/index.js | 1 + boilerplate/App/Sagas/LoginSagas.js.ejs | 21 ++++++++++----- boilerplate/App/Sagas/StartupSagas.js | 2 ++ boilerplate/App/Sagas/index.js | 3 ++- 8 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 boilerplate/App/Redux/AppStateRedux.js diff --git a/boilerplate/App/Config/ReduxPersist.js b/boilerplate/App/Config/ReduxPersist.js index 827f3314a..f3159d1e9 100644 --- a/boilerplate/App/Config/ReduxPersist.js +++ b/boilerplate/App/Config/ReduxPersist.js @@ -6,7 +6,7 @@ const REDUX_PERSIST = { reducerVersion: '4', storeConfig: { storage: AsyncStorage, - blacklist: ['search'], // reducer keys that you do NOT want stored to persistence here + blacklist: ['appState', 'search'], // reducer keys that you do NOT want stored to persistence here // whitelist: [], Optionally, just specify the keys you DO want stored to // persistence. An empty array means 'don't store any reducers' -> infinitered/ignite#409 transforms: [immutablePersistenceTransform] diff --git a/boilerplate/App/Containers/RootContainer.js b/boilerplate/App/Containers/RootContainer.js index 3e46e04ed..9e5aad804 100644 --- a/boilerplate/App/Containers/RootContainer.js +++ b/boilerplate/App/Containers/RootContainer.js @@ -3,6 +3,7 @@ import { View, StatusBar } from 'react-native' import NavigationRouter from '../Navigation/NavigationRouter' import { connect } from 'react-redux' import StartupActions from '../Redux/StartupRedux' +import LoginActions from '../Redux/LoginRedux' import ReduxPersist from '../Config/ReduxPersist' // Styles @@ -17,6 +18,12 @@ class RootContainer extends Component { } } + componentWillReceiveProps (newProps) { + if (newProps.rehydrationComplete) { + this.props.loadLogin() + } + } + render () { return ( @@ -27,9 +34,16 @@ class RootContainer extends Component { } } +const mapStateToProps = (state) => { + return { + rehydrationComplete: state.appState.rehydrationComplete + } +} + // wraps dispatch to create nicer functions to call within our component const mapDispatchToProps = (dispatch) => ({ - startup: () => dispatch(StartupActions.startup()) + startup: () => dispatch(StartupActions.startup()), + loadLogin: () => dispatch(LoginActions.loginLoad()) }) -export default connect(null, mapDispatchToProps)(RootContainer) +export default connect(mapStateToProps, mapDispatchToProps)(RootContainer) diff --git a/boilerplate/App/Redux/AppStateRedux.js b/boilerplate/App/Redux/AppStateRedux.js new file mode 100644 index 000000000..70afa39ac --- /dev/null +++ b/boilerplate/App/Redux/AppStateRedux.js @@ -0,0 +1,29 @@ +import { createReducer, createActions } from 'reduxsauce' +import Immutable from 'seamless-immutable' + +/* ------------- Types and Action Creators ------------- */ + +const { Types, Creators } = createActions({ + setRehydrationComplete: null +}) + +export const AppStateTypes = Types +export default Creators + +/* ------------- Initial State ------------- */ + +export const INITIAL_STATE = Immutable({ + rehydrationComplete: false +}) + +/* ------------- Reducers ------------- */ + +// rehydration is complete +export const setRehydrationComplete = (state: Object) => + state.merge({ rehydrationComplete: true }) + +/* ------------- Hookup Reducers To Types ------------- */ + +export const reducer = createReducer(INITIAL_STATE, { + [Types.SET_REHYDRATION_COMPLETE]: setRehydrationComplete +}) diff --git a/boilerplate/App/Redux/LoginRedux.js.ejs b/boilerplate/App/Redux/LoginRedux.js.ejs index 2206b37c6..4c09473ec 100644 --- a/boilerplate/App/Redux/LoginRedux.js.ejs +++ b/boilerplate/App/Redux/LoginRedux.js.ejs @@ -7,7 +7,9 @@ const { Types, Creators } = createActions({ loginSuccess: ['id_token'], loginFailure: ['error'], logoutRequest: null, - logoutSuccess: null + logoutSuccess: null, + loginLoad: [], + loginLoadSuccess: [] }) export const LoginTypes = Types @@ -18,7 +20,8 @@ export default Creators export const INITIAL_STATE = Immutable({ id_token: null, error: null, - fetching: false + fetching: false, + loading: false }) /* ------------- Reducers ------------- */ @@ -28,12 +31,7 @@ export const request = (state) => state.merge({ fetching: true }) // we've successfully logged in export const success = (state, data) => { - <%_ if (props.authType === 'oauth2' || props.authType === 'uaa') { _%> return state.merge({ fetching: false, error: null, id_token: data.id_token }) - <%_ } else if (props.authType === 'jwt') { _%> - const { id_token } = data.id_token - return state.merge({ fetching: false, error: null, id_token }) - <%_ } _%> } // we've had a problem logging in export const failure = (state, { error }) => state.merge({ fetching: false, error }) @@ -44,6 +42,11 @@ export const logoutRequest = state => state // we've logged out export const logoutSuccess = state => INITIAL_STATE +// we're attempting to load token from startup sagas +export const load = (state) => state.merge({ loading: true }) + +export const loadSuccess = (state) => state.merge({ loading: false }) + /* ------------- Hookup Reducers To Types ------------- */ export const reducer = createReducer(INITIAL_STATE, { @@ -51,7 +54,9 @@ export const reducer = createReducer(INITIAL_STATE, { [Types.LOGIN_SUCCESS]: success, [Types.LOGIN_FAILURE]: failure, [Types.LOGOUT_REQUEST]: logoutRequest, - [Types.LOGOUT_SUCCESS]: logoutSuccess + [Types.LOGOUT_SUCCESS]: logoutSuccess, + [Types.LOGIN_LOAD]: load, + [Types.LOGIN_LOAD_SUCCESS]: loadSuccess }) /* ------------- Selectors ------------- */ diff --git a/boilerplate/App/Redux/index.js b/boilerplate/App/Redux/index.js index 32d13b3c6..2b44a229e 100644 --- a/boilerplate/App/Redux/index.js +++ b/boilerplate/App/Redux/index.js @@ -5,6 +5,7 @@ import rootSaga from '../Sagas/' export default () => { /* ------------- Assemble The Reducers ------------- */ const rootReducer = combineReducers({ + appState: require('./AppStateRedux').reducer, github: require('./GithubRedux').reducer, account: require('./AccountRedux').reducer, login: require('./LoginRedux').reducer, diff --git a/boilerplate/App/Sagas/LoginSagas.js.ejs b/boilerplate/App/Sagas/LoginSagas.js.ejs index f15bc7439..d01a2bf42 100644 --- a/boilerplate/App/Sagas/LoginSagas.js.ejs +++ b/boilerplate/App/Sagas/LoginSagas.js.ejs @@ -1,7 +1,9 @@ -import { call, put } from 'redux-saga/effects' +import { call, put, select } from 'redux-saga/effects' import LoginActions from '../Redux/LoginRedux' import AccountActions from '../Redux/AccountRedux' +export const selectAuthToken = (state) => state.login.id_token + // attempts to login export function * login (api, { username, password }) { @@ -22,11 +24,7 @@ export function * login (api, { username, password }) { // success? if (response.ok) { console.tron.log("Login - OK") - <%_ if (props.authType === 'oauth2' || props.authType === 'uaa') { _%> - yield call(api.setAuthToken, response.data.id_token.access_token) - <%_ } else if (props.authType === 'jwt') { _%> - yield call(api.setAuthToken, response.data.id_token) - <%_ } _%> + yield call(api.setAuthToken, response.data.id_token<%_ if (props.authType === 'oauth2' || props.authType === 'uaa') { _%>.access_token<%_ } %>) yield put(LoginActions.loginSuccess(response.data)) yield put(AccountActions.accountRequest()) } else { @@ -46,3 +44,14 @@ export function * logout (api) { yield put(AccountActions.logout()) yield put(LoginActions.logoutSuccess()) } + +// loads the login +export function * loginLoad (api) { + const authToken = yield select(selectAuthToken) + // only set the token if we have it + if (authToken !== null) { + console.tron.log(authToken) + yield call(api.setAuthToken, authToken<%_ if (props.authType === 'oauth2' || props.authType === 'uaa') { _%>.access_token<%_ } %>) + } + yield put(LoginActions.loginLoadSuccess()) +} diff --git a/boilerplate/App/Sagas/StartupSagas.js b/boilerplate/App/Sagas/StartupSagas.js index 8cc37ae48..a279d4f7b 100644 --- a/boilerplate/App/Sagas/StartupSagas.js +++ b/boilerplate/App/Sagas/StartupSagas.js @@ -1,4 +1,5 @@ import { put, select } from 'redux-saga/effects' +import AppStateActions from '../Redux/AppStateRedux' import GithubActions from '../Redux/GithubRedux' import { is } from 'ramda' @@ -37,4 +38,5 @@ export function * startup (action) { if (!is(String, avatar)) { yield put(GithubActions.userRequest('GantMan')) } + yield put(AppStateActions.setRehydrationComplete()) } diff --git a/boilerplate/App/Sagas/index.js b/boilerplate/App/Sagas/index.js index 25e3104a4..1e0b91784 100644 --- a/boilerplate/App/Sagas/index.js +++ b/boilerplate/App/Sagas/index.js @@ -17,7 +17,7 @@ import { AccountTypes } from '../Redux/AccountRedux' /* ------------- Sagas ------------- */ import { startup } from './StartupSagas' -import { login, logout } from './LoginSagas' +import { login, logout, loginLoad } from './LoginSagas' import { register } from './RegisterSagas' import { forgotPasswordRequest } from './ForgotPasswordSagas' import { getUserAvatar } from './GithubSagas' @@ -41,6 +41,7 @@ export default function * root () { takeLatest(OpenScreenTypes.OPEN_SCREEN, openScreen), // JHipster accounts + takeLatest(LoginTypes.LOGIN_LOAD, loginLoad, jhipsterApi), takeLatest(LoginTypes.LOGIN_REQUEST, login, jhipsterApi), takeLatest(LoginTypes.LOGOUT_REQUEST, logout, jhipsterApi), takeLatest(RegisterTypes.REGISTER_REQUEST, register, jhipsterApi),