Skip to content

Commit bd834b7

Browse files
author
Scott Prue
committed
feat(auth): add custom claims - #741 - @joerex
Manual cherry-pick of commit by @joerex - 7f8f693
1 parent 1782f92 commit bd834b7

File tree

3 files changed

+65
-12
lines changed

3 files changed

+65
-12
lines changed

docs/auth.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ class SomeComponent extends Component {
4949
export default firebaseConnect()(SomeComponent) // or withFirebase(SomeComponent)
5050
```
5151

52+
#### Custom Claims
53+
54+
Firebase has a secure way of identifying and making claims about users with [custom claims](https://firebase.google.com/docs/auth/admin/custom-claims). This is a good way to provide roles for users.
55+
56+
If `enableClaims` config option is used along with `userProfile` you will find custom claims in `state.firebase.profile.token.claims`.
57+
58+
**Note**: If a claim is added to a user who is already logged in those changes will not necessarily be propagated to the client. In order to assure the change is observed, use a `refreshToken` property in your `userProfile` collection and update it's value after the custom claim has been added. Because `react-redux-firebase` watches for profile changes, the custom claim will be fetched along with the `refreshToken` update.
59+
5260
For examples of how to use this API, checkout the [auth recipes section](/docs/recipes/auth.html).
5361

5462
## login(credentials)

docs/getting_started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const fbConfig = {}
4545
const rrfConfig = {
4646
userProfile: 'users',
4747
// useFirestoreForProfile: true // Firestore for Profile instead of Realtime DB
48+
// enableClaims: true // Get custom claims along with the profile
4849
}
4950

5051
// Initialize firebase instance

src/actions/auth.js

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ function getProfileFromSnap(snap) {
7171
export function handleProfileWatchResponse(
7272
dispatch,
7373
firebase,
74-
userProfileSnap
74+
userProfileSnap,
75+
token
7576
) {
7677
const {
7778
profileParamsToPopulate,
@@ -88,7 +89,10 @@ export function handleProfileWatchResponse(
8889
if (useFirestoreForProfile && profileParamsToPopulate) {
8990
console.warn('Profile population is not yet supported for Firestore') // eslint-disable-line no-console
9091
}
91-
dispatch({ type: actionTypes.SET_PROFILE, profile })
92+
dispatch({
93+
type: actionTypes.SET_PROFILE,
94+
profile: token ? { ...profile, token } : profile
95+
})
9296
} else {
9397
// Convert array of populate config into an array of once query promises
9498
promisesForPopulate(
@@ -111,14 +115,21 @@ export function handleProfileWatchResponse(
111115
})
112116
if (!autoPopulateProfile) {
113117
// Dispatch action with profile combined with populated parameters
114-
dispatch({ type: actionTypes.SET_PROFILE, profile })
118+
dispatch({
119+
type: actionTypes.SET_PROFILE,
120+
profile: token ? { ...profile, token } : profile
121+
})
115122
} else {
116123
// Auto Populate profile
117124
const populates = getPopulateObjs(profileParamsToPopulate)
118125
const profile = userProfileSnap.val()
119126
dispatch({
120127
type: actionTypes.SET_PROFILE,
121-
profile: populate({ profile, data }, 'profile', populates)
128+
profile: populate(
129+
{ profile: token ? { ...profile, token } : profile, data },
130+
'profile',
131+
populates
132+
)
122133
})
123134
}
124135
})
@@ -177,7 +188,7 @@ function createProfileWatchErrorHandler(dispatch, firebase) {
177188
export const watchUserProfile = (dispatch, firebase) => {
178189
const {
179190
authUid,
180-
config: { userProfile, useFirestoreForProfile }
191+
config: { userProfile, useFirestoreForProfile, enableClaims }
181192
} = firebase._
182193
unWatchUserProfile(firebase)
183194

@@ -187,20 +198,53 @@ export const watchUserProfile = (dispatch, firebase) => {
187198
.firestore()
188199
.collection(userProfile)
189200
.doc(authUid)
190-
.onSnapshot(
191-
userProfileSnap =>
192-
handleProfileWatchResponse(dispatch, firebase, userProfileSnap),
193-
createProfileWatchErrorHandler(dispatch, firebase)
194-
)
201+
.onSnapshot(userProfileSnap => {
202+
return enableClaims
203+
? firebase
204+
.auth()
205+
.currentUser.getIdTokenResult(true)
206+
.then(token =>
207+
handleProfileWatchResponse(
208+
dispatch,
209+
firebase,
210+
userProfileSnap,
211+
token
212+
)
213+
)
214+
: handleProfileWatchResponse(
215+
dispatch,
216+
firebase,
217+
userProfileSnap,
218+
null
219+
)
220+
}, createProfileWatchErrorHandler(dispatch, firebase))
195221
} else if (firebase.database) {
196222
firebase._.profileWatch = firebase // eslint-disable-line no-param-reassign
197223
.database()
198224
.ref()
199225
.child(`${userProfile}/${authUid}`)
200226
.on(
201227
'value',
202-
userProfileSnap =>
203-
handleProfileWatchResponse(dispatch, firebase, userProfileSnap),
228+
userProfileSnap => {
229+
enableClaims
230+
? firebase
231+
.auth()
232+
.currentUser.getIdTokenResult(true)
233+
.then(token =>
234+
handleProfileWatchResponse(
235+
dispatch,
236+
firebase,
237+
userProfileSnap,
238+
token
239+
)
240+
)
241+
: handleProfileWatchResponse(
242+
dispatch,
243+
firebase,
244+
userProfileSnap,
245+
null
246+
)
247+
},
204248
createProfileWatchErrorHandler(dispatch, firebase)
205249
)
206250
} else {

0 commit comments

Comments
 (0)