Browse Source

JWT Auth + Token Persist

master
sipp11 7 years ago
parent
commit
cac8eb475f
  1. 3
      package.json
  2. 37
      src/actions/index.js
  3. 8
      src/constants/ActionTypes.js
  4. 3
      src/constants/Api.js
  5. 19
      src/container/Gone.js
  6. 28
      src/index.js
  7. 40
      src/reducers/auth.js
  8. 4
      src/reducers/index.js
  9. 27
      src/store.js
  10. 12
      src/utils/ApiClient.js
  11. 29
      src/utils/Auth.js

3
package.json

@ -3,6 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"axios": "^0.18.0",
"lodash": "^4.17.10",
"react": "^16.4.1", "react": "^16.4.1",
"react-dom": "^16.4.1", "react-dom": "^16.4.1",
"react-redux": "^5.0.7", "react-redux": "^5.0.7",
@ -10,6 +12,7 @@
"react-scripts": "1.1.4", "react-scripts": "1.1.4",
"redux": "^4.0.0", "redux": "^4.0.0",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0" "redux-thunk": "^2.3.0"
}, },
"scripts": { "scripts": {

37
src/actions/index.js

@ -1,4 +1,5 @@
import * as types from '../constants/ActionTypes' import * as types from '../constants/ActionTypes'
import { login } from '../utils/Auth'
export const incFirstCounter = _ => ({ export const incFirstCounter = _ => ({
type: types.FIRST_INCREMENT_COUNTER, type: types.FIRST_INCREMENT_COUNTER,
@ -8,3 +9,39 @@ export const assignFirstId = objId => ({
type: types.FIRST_ASSIGN_ID, type: types.FIRST_ASSIGN_ID,
newId: objId newId: objId
}) })
// auth
export const reqAuth = _ => {
return {
type: types.REQUEST_LOGIN,
}
}
export const successAuth = (body) => {
return {
type: types.SUCCESS_LOGIN,
body
}
}
export const failAuth = (body) => {
return {
type: types.FAILED_LOGIN,
body
}
}
export function fetchAuth(user, passwd) {
return (dispatch) => {
dispatch(reqAuth())
return login(user, passwd)
}
}
// token
export const setToken = (data) => {
return {
type: types.SET_TOKEN,
token: data,
}
}

8
src/constants/ActionTypes.js

@ -1,3 +1,9 @@
export const FIRST_INCREMENT_COUNTER = 'FIRST_INCREMENT_COUNTER' export const FIRST_INCREMENT_COUNTER = 'FIRST_INCREMENT_COUNTER'
export const FIRST_ASSIGN_ID = 'FIRST_ASSIGN_ID' export const FIRST_ASSIGN_ID = 'FIRST_ASSIGN_ID'
export const SET_TOKEN = 'SET_TOKEN'
export const REQUEST_LOGIN = 'REQUEST_LOGIN'
export const SUCCESS_LOGIN = 'SUCCESS_LOGIN'
export const FAILED_LOGIN = 'FAILED_LOGIN'

3
src/constants/Api.js

@ -0,0 +1,3 @@
export const URL = process.env.API_URL || '//localhost:8000'
export const LOGIN = '/api-token-auth/'

19
src/container/Gone.js

@ -1,14 +1,29 @@
import React from 'react' import React from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { getToken, loggedIn } from '../reducers/auth'
import { fetchAuth } from '../actions'
const Main = (props) => ( const Gone = (props) => (
<div> <div>
Gone { props.match.params.id } Gone { props.match.params.id }
<hr /> <hr />
<Link to={`/`}>Main</Link> <Link to={`/`}>Main</Link>
<hr />
{props.loggedIn ? 'Logged in': 'Nah anonymous'}
<hr />
<button onClick={_ => props.fetchAuth("user", "passwd")}>Login</button>
</div> </div>
) )
export default Main const mapStateToProps = state => ({
token: getToken(state.auth)
})
export default connect(
mapStateToProps,
{ loggedIn, fetchAuth }
)(Gone)

28
src/index.js

@ -4,30 +4,22 @@ import './index.css'
import App from './App' import App from './App'
import registerServiceWorker from './registerServiceWorker' import registerServiceWorker from './registerServiceWorker'
import { HashRouter } from 'react-router-dom' import { HashRouter } from 'react-router-dom'
import thunk from 'redux-thunk'
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { createLogger } from 'redux-logger' import { persistStore } from 'redux-persist'
import gruntApp from './reducers' import { PersistGate } from 'redux-persist/integration/react'
import store from './store'
const middleware = [ thunk ] const persistor = persistStore(store)
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger())
}
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(...middleware)
)
const store = createStore(gruntApp, enhancer)
/* eslint-enable */
ReactDOM.render(( ReactDOM.render((
<Provider store={store}> <Provider store={store}>
<HashRouter> <PersistGate loading={null} persistor={persistor}>
<App /> <HashRouter>
</HashRouter> <App />
</HashRouter>
</PersistGate>
</Provider> </Provider>
), document.getElementById('root')) ), document.getElementById('root'))

40
src/reducers/auth.js

@ -0,0 +1,40 @@
import {
SET_TOKEN, REQUEST_LOGIN, SUCCESS_LOGIN, FAILED_LOGIN,
} from '../constants/ActionTypes'
const tokenInitialState = {
token: null,
fetching: false,
}
const token = (state = tokenInitialState, action) => {
switch(action.type) {
case REQUEST_LOGIN:
return {
token: null,
fetching: true,
}
case SUCCESS_LOGIN:
return {
token: action.body.token,
fetching: false,
}
case FAILED_LOGIN:
return {
token: null,
fetching: false,
}
case SET_TOKEN:
return {
token: action.token,
fetching: false,
};
default:
return state;
}
}
export default token
export const getToken = state => state.token
export const loggedIn = state => state && state.token == null

4
src/reducers/index.js

@ -1,6 +1,8 @@
import { combineReducers } from 'redux' import { combineReducers } from 'redux'
import first from './first' import first from './first'
import auth from './auth'
export default combineReducers({ export default combineReducers({
first auth,
first,
}) })

27
src/store.js

@ -0,0 +1,27 @@
import thunk from 'redux-thunk'
import { createStore, applyMiddleware, compose } from 'redux'
import { createLogger } from 'redux-logger'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
import gruntApp from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger())
}
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(...middleware),
)
const persistedReducer = persistReducer(persistConfig, gruntApp)
const store = createStore(persistedReducer, enhancer)
export default store

12
src/utils/ApiClient.js

@ -0,0 +1,12 @@
import axios from 'axios'
import store from '../store'
import { URL } from '../config/Api'
export const apiClient = function() {
const token = store.getState().token
const params = {
baseURL: URL,
headers: {'Authorization': 'Token ' + token}
}
return axios.create(params)
}

29
src/utils/Auth.js

@ -0,0 +1,29 @@
import axios from 'axios'
import _ from 'lodash'
import store from '../store'
import { failAuth, successAuth } from '../actions'
import { URL, LOGIN } from '../constants/Api'
export function InvalidCredentialsException(message) {
this.message = message
this.name = 'InvalidCredentialsException'
}
export function login(username, password) {
return axios
.post(URL + LOGIN, {
username,
password
})
.then(function (response) {
store.dispatch(successAuth(response.data))
})
.catch(function (error) {
store.dispatch(failAuth(error))
// raise different exception if due to invalid credentials
if (_.get(error, 'response.status') === 400) {
throw new InvalidCredentialsException(error)
}
throw error
})
}
Loading…
Cancel
Save