javascript - React Native + Redux basic authentication -
i'm looking way create basic authentication react-native app. couldn't find example react-native app.
- to login, app sends email/password + clientsecret server
- if ok, server returns accesstoken + refreshtoken
- the user logged in, other requests include bearer accesstoken.
- if accesstoken expires, app requests new 1 refreshtoken automatically.
- the user stays logged in time, states should saved in phone.
what best approach this?
thanks.
when app communicates http api enforces form of authentication, app typically follows these steps:
- the app not authenticated, prompt user log in.
- the user enters credentials (username , password), , taps submit.
- we send these credentials api, , inspect response:
- on success (200 - ok): cache authentication token/ hash, because we're going use token/ hash in every subsequent request.
- if token/ hash not work during of subsequent api requests (401 - unauthorized), we'll need invalidate hash/ token , prompt user log in again.
- or, on failure (401 - unauthorized): display error message user, prompting them re-enter credentials.
- on success (200 - ok): cache authentication token/ hash, because we're going use token/ hash in every subsequent request.
logging in
based on work flow defined above our app starts displaying login form, step 2 kicks in when user taps login button dispatches login
action creator below:
/// actions/user.js export function login(username, password) { return (dispatch) => { // use update store state of `isloggingin` // can used display activity indicator on login // view. dispatch(loginrequest()) // note: base64 encode method works in nodejs, use // implementation works platform: // `base64-js` react native, // `btoa()` browsers, etc... const hash = new buffer(`${username}:${password}`).tostring('base64') return fetch('https://httpbin.org/basic-auth/admin/secret', { headers: { 'authorization': `basic ${hash}` } }) .then(response => response.json().then(json => ({ json, response }))) .then(({json, response}) => { if (response.ok === false) { return promise.reject(json) } return json }) .then( data => { // data = { authenticated: true, user: 'admin' } // pass `authentication hash` down reducer // can used in subsequent api requests. dispatch(loginsuccess(hash, data.user)) }, (data) => dispatch(loginfailure(data.error || 'log in failed')) ) } }
there's lot of code in function above, take comfort in fact majority of code sanitising response , can abstracted away.
the first thing dispatch action login_request
updates our store , lets know user isloggingin
.
dispatch(loginrequest())
we use display activity indicator (spinning wheel, "loading...", etc.), , disable log in button in our log in view.
next base64 encode user's username , password http basic auth, , pass request's headers.
const hash = new buffer(`${username}:${password}`).tostring('base64') return fetch('https://httpbin.org/basic-auth/admin/secret', { headers: { 'authorization': `basic ${hash}` } /* ... */
if went well, we'll dispatch login_success
action, results in having authentication hash
in our store, we'll use in subsequent requests.
dispatch(loginsuccess(hash, data.user))
on flip side, if went wrong want let user know:
dispatch(loginfailure(data.error || 'log in failed')
the loginsuccess
, loginfailure
, , loginrequest
action creators generic , don't warrant code samples. see: https://github.com/peterp/redux-http-basic-auth-example/blob/master/actions/user.js)
reducer
our reducer typical:
/// reducers/user.js function user(state = { isloggingin: false, isauthenticated: false }, action) { switch(action.type) { case login_request: return { isloggingin: true, // show loading indicator. isauthenticated: false } case login_failure: return { isloggingin: false, isauthenticated: false, error: action.error } case login_success: return { isloggingin: false, isauthenticated: true, // dismiss login view. hash: action.hash, // used in subsequent api requests. user: action.user } default: return state } }
subsequent api requests
now have authentication hash in our store can pass subsequent request's headers.
in our example below we're fetching list of friends our authenticated user:
/// actions/friends.js export function fetchfriends() { return (dispatch, getstate) => { dispatch(friendsrequest()) // notice how grab hash store: const hash = getstate().user.hash return fetch(`https://httpbin.org/get/friends/`, { headers: { 'authorization': `basic ${hash}` } }) .then(response => response.json().then(json => ({ json, response }))) .then(({json, response}) => { if (response.ok === false) { return promise.reject({response, json}) } return json }) .then( data => { // data = { friends: [ {}, {}, ... ] } dispatch(friendssuccess(data.friends)) }, ({response, data}) => { dispatch(friendsfailure(data.error)) // did our request fail because our auth credentials aren't working? if (response.status == 401) { dispatch(loginfailure(data.error)) } } ) } }
you may find api requests typically dispatch same 3 actions above: api_request
, api_success
, , api_failure
, , such majority of request/ response code can pushed redux middleware.
we fetch hash authentication token store , setup request.
const hash = getstate().user.hash return fetch(`https://httpbin.org/get/friends/`, { headers: { 'authorization': `basic ${hash}` } }) /* ... */
if api response 401 status code we've got remove our hash store, , present user log in view again.
if (response.status == 401) { dispatch(loginfailure(data.error)) }
i've answered question generically , dealing http-basic-auth.
i think concept may remain same, you'll push accesstoken
, refreshtoken
in store, , extract in subsequent requests.
if request fails you'll have dispatch action updates accesstoken, , recalls original request.
Comments
Post a Comment