Skip to content
This repository was archived by the owner on Jul 12, 2020. It is now read-only.

Commit 448dc11

Browse files
committed
wrote the rest of the code which describes the subsequent requests.
1 parent 7543e56 commit 448dc11

File tree

1 file changed

+81
-19
lines changed

1 file changed

+81
-19
lines changed

README.md

+81-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
# redux-http-basic-auth-example
22

3-
43
When an app communicates with a HTTP API, which enforces some form of authentication, the app typically follows these steps:
54

6-
1. The app is not authenticated, so the user is prompted to log in.
7-
2. The user enters their username and password, and taps submit.
8-
3. The credentials are sent to the API, and the app inspects the response:
9-
4. On success (200 - OK): The authentication token or hash is cached, the app uses this token in every subsequent request.
10-
5. If at any stage, during an API request, the hash or token doesn't work anymore (401 - Unauthorized), then the user is prompted to re-enter their credentials.
11-
6. On failure (401 - Unauthorized): The client displays an error message to the user, prompting them re-enter their credentials.
5+
1. The app is not authenticated, so we prompt the user to log in.
6+
2. The user enters their credentials (username and password), and taps submit.
7+
3. We send these credentials to the API, and inspect the response:
8+
4. On success (200 - OK): We cache the authentication token/ hash, because we're going to use this token/ hash _in every subsequent_ request.
9+
5. If the token/ hash does not work during any of the subsequent API requests (401 - Unauthorized), we'll need to invalidate the hash/ token and prompt the user to log in again.
10+
6. Or, on failure (401 - Unauthorized): We display an error message to the user, prompting them re-enter their credentials.
1211

13-
Based on the work flow defined above we start our app by displaying a login form, _step 2_ kicks in when the user taps the login button...
12+
Based on the work flow defined above we start our app by displaying a login form, _step 2_ kicks in when the user taps the login button... Dispatching the `login` action creator:
1413

1514
# Logging In #
1615

16+
Let's jump into some code:
17+
1718
```
1819
/// actions/user.js
1920
@@ -25,9 +26,10 @@ export function login(username, password) {
2526
// view.
2627
dispatch(loginRequest())
2728
28-
// This only works in Node, use an implementation that work for the
29-
// platform you're using, e.g.: `base64-js` for React Native, or `btoa()`
30-
// for browsers, etc...
29+
// Note: This base64 encode method only works in NodeJS, so use an
30+
// implementation that works for your platform:
31+
// `base64-js` for React Native,
32+
// `btoa()` for browsers,
3133
const hash = new Buffer(`${username}:${password}`).toString('base64')
3234
return fetch('https://httpbin.org/basic-auth/admin/secret', {
3335
headers: {
@@ -54,15 +56,16 @@ export function login(username, password) {
5456
}
5557
```
5658

57-
There's a lot going on in the function above, but take comfort in the fact that
58-
the vast majority of the code is sanitizing the request process and can be abstracted away.
59-
59+
There's a lot of code in the function above, but take comfort in the fact that
60+
the majority of the code is sanitizing the request and can be abstracted away.
6061

62+
The first thing we do is dispatch an action creator:
6163
```
6264
dispatch(loginRequest())
6365
```
64-
Updates our Redux store, by setting a `isLoggingIn` property to `true`, this is used to display a loading indicator and disable the login button.
66+
Which results in our state letting us know that the user `isLoggingIn`. We use this to display an activity indicator (_spinning wheel_), and to disable the log in button in our log in view.
6567

68+
Next we base64 encode our credentials, and setup a fetch request.
6669
```
6770
const hash = new Buffer(`${username}:${password}`).toString('base64')
6871
return fetch('https://httpbin.org/basic-auth/admin/secret', {
@@ -71,22 +74,24 @@ return fetch('https://httpbin.org/basic-auth/admin/secret', {
7174
}
7275
/* ... */
7376
```
74-
Our example uses HTTP basic access authentication, so we've generated a base64 hash from the `username` and `password` and added the `Authorization` headers to our request.
7577

78+
Everything went well, so we...
7679
```
7780
dispatch(loginSuccess(hash, data.user))
7881
```
79-
If everything went well then we dispatch the `LOGIN_SUCCESS` action along with our `hash` and `user` object. The `hash` is used in subsequent API requests.
82+
Our `LOGIN_SUCCESS` action results in the `hash` been stored in our state, which will be used in every subsequent request.
8083

84+
If something went wrong then we want to let the user know...
8185
```
8286
dispatch(loginFailure(data.error || 'Log in failed')
8387
```
84-
If the request failed then we need to update the login view, removing the loading indicator, enabling the submit button, and displaying an error message.
8588

8689
_The `loginSuccess`, `loginFailure`, and `loginRequest` action creators are fairly generic and don't really warrant code samples. (See `actions/user.js`)_
8790

8891
### Reducer ###
8992

93+
Our reducer is fairly typical, simply updating our store for each action it receives.
94+
9095
```
9196
/// reducers/user.js
9297
@@ -121,4 +126,61 @@ function user(state = {
121126

122127
# Subsequent API requests #
123128

124-
Now that we have a authentication `hash` in our store.
129+
We now have an authentication hash in our store, which we can use in subsequent action creators on requests.
130+
131+
```
132+
/// actions/friends.js
133+
export function fetchFriends() {
134+
return (dispatch, getState) => {
135+
136+
dispatch(friendsRequest())
137+
138+
// Notice how we grab the
139+
const hash = getState().user.hash
140+
return fetch(`https://httpbin.org/get/friends/`, {
141+
headers: {
142+
'Authorization': `Basic ${hash}`
143+
}
144+
})
145+
.then(response => response.json().then(json => ({ json, response })))
146+
.then(({json, response}) => {
147+
if (response.ok === false) {
148+
return Promise.reject(response, json)
149+
}
150+
return json
151+
})
152+
.then(
153+
data => {
154+
// data = { friends: [ {}, {}, ... ] }
155+
dispatch(friendsSuccess(data.friends))
156+
},
157+
(response, data) => {
158+
dispatch(friendsFailure(data.error))
159+
160+
// did our request fail because our auth credentials aren't working?
161+
if (response.status == 401) {
162+
dispatch(loginFailure(data.error))
163+
}
164+
}
165+
)
166+
}
167+
}
168+
```
169+
170+
You'll find that most API requests typically dispatches the same 3 actions as above: `API_REQUEST`, `API_SUCCESS`, and `API_FAILURE`, and as such the majority of the request/ response code can be pushed into [middleware](https://github.com/reactjs/redux/issues/99#issuecomment-112198579).
171+
172+
```
173+
const hash = getState().user.hash
174+
return fetch(`https://httpbin.org/get/friends/`, {
175+
headers: {
176+
'Authorization': `Basic ${hash}`
177+
}
178+
})
179+
```
180+
We fetch the hash authentication token from the store and setup the request. If our API requests with a 401 unauthorized status code then we've got to remove our hash from the store, and present the user with a log in view.
181+
```
182+
// did our request fail because our auth credentials aren't working?
183+
if (response.status == 401) {
184+
dispatch(loginFailure(data.error))
185+
}
186+
```

0 commit comments

Comments
 (0)