Skip to content

Commit ba40182

Browse files
authored
Merge pull request #40 from dmurvihill/get-id-token-result
Add support for getIdTokenResult
2 parents ea6f039 + d00a542 commit ba40182

File tree

8 files changed

+658
-46
lines changed

8 files changed

+658
-46
lines changed

API.md

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ Only `MockFirebase` methods are included here. For details on normal Firebase AP
1212
- [`fakeEvent(event [, key] [, data] [, previousChild] [, priority])`](#fakeeventevent--key--data--previouschild--priority---ref)
1313
- [`getFlushQueue()`](#getflushqueue---array)
1414
- [Auth](#auth)
15-
- [`changeAuthState(user)`](#changeauthstateauthdata---undefined)
16-
- [`getUserByEmail(email)`](#getemailuseremail---objectnull)
17-
- [`getUser(uid)`](#getemailuseremail---objectnull)
15+
- [`changeAuthState(user)`](#changeauthstateuser---undefined)
16+
- [`getUserByEmail(email)`](#getuserbyemailemail---promiseobject)
17+
- [`getUser(uid)`](#getuseruid---promiseobject)
18+
- [`updateUser(user)`](#updateuseruser---promisemockuser)
1819
- [Server Timestamps](#server-timestamps)
1920
- [`setClock(fn)`](#firebasesetclockfn---undefined)
2021
- [`restoreClock()`](#firebasesetclockfn---undefined)
@@ -117,7 +118,7 @@ function onValue (_snapshot_) {
117118
}
118119
ref.on('value', onValue);
119120
ref.set({
120-
foo: 'bar';
121+
foo: 'bar',
121122
});
122123
ref.flush();
123124
console.assert(ref.getData().foo === 'bar', 'data has foo');
@@ -169,26 +170,46 @@ ref.flush(); // added foo after null
169170

170171
Authentication methods for simulating changes to the auth state of a Firebase reference.
171172

172-
##### `changeAuthState(authData)` -> `undefined`
173+
##### `changeAuthState(user)` -> `undefined`
173174

174-
Changes the active authentication credentials to the `authData` object. Before changing the authentication state, `changeAuthState` checks whether the `authData` object is deeply equal to the current authentication data. `onAuth` listeners will only be triggered if the data is not deeply equal. To simulate no user being authenticated, pass `null` for `authData`. This operation is queued until the next `flush`.
175+
Changes the active authentication credentials to the `authData` object.
176+
Before changing the authentication state, `changeAuthState` checks the
177+
`user` object against the current authentication data.
178+
`onAuthStateChanged` listeners will only be triggered if the data is not
179+
deeply equal.
175180

176-
`authData` should adhere to the [documented schema](https://www.firebase.com/docs/web/api/firebase/onauth.html).
181+
`user` should be a `MockUser` object or an object with the same fields
182+
as `MockUser`. To simulate no user being authenticated, pass `null` for
183+
`user`. This operation is queued until the next `flush`.
177184

178185
Example:
179186

180187
```js
181-
ref.changeAuthState({
188+
ref.changeAuthState(new MockUser(ref, {
182189
uid: 'theUid',
183-
provider: 'github',
184-
token: 'theToken',
185-
expires: Math.floor(new Date() / 1000) + 24 * 60 * 60, // expire in 24 hours
186-
auth: {
187-
myAuthProperty: true
188-
}
189-
});
190+
191+
emailVerified: true,
192+
displayName: 'Mr. Meeseeks',
193+
phoneNumber: '+1-508-123-4567',
194+
photoURL: 'https://example.com/image.png',
195+
isAnonymous: false,
196+
providerId: 'github',
197+
providerData: [],
198+
refreshToken: '123e4567-e89b-12d3-a456-426655440000',
199+
metadata: {}, // firebase-mock offers limited support for this field
200+
customClaims: {
201+
isAdmin: true,
202+
// etc.
203+
},
204+
_idtoken: 'theToken',
205+
_tokenValidity: {
206+
authTime: '2019-11-22T08:46:15Z',
207+
issuedAtTime: '2019-11-22T08:46:15Z',
208+
expirationTime: '2019-11-22T09:46:15Z',
209+
},
210+
}));
190211
ref.flush();
191-
console.assert(ref.getAuth().auth.myAuthProperty, 'authData has custom property');
212+
console.assert(ref.getAuth().displayName === 'Mr. Meeseeks', 'Auth name is correct');
192213
```
193214

194215
<hr>
@@ -201,6 +222,13 @@ Finds a user previously created with [`createUser`](https://www.firebase.com/doc
201222

202223
Finds a user previously created with [`createUser`](https://www.firebase.com/docs/web/api/firebase/createuser.html). If no user was created with the specified `email`, the promise is rejected.
203224

225+
##### `updateUser(user)` -> `Promise<MockUser>`
226+
227+
Replace the existing user with a new one, by matching uid. Throws an
228+
error if no user exists whose uid matches the given user's uid. Resolves
229+
with the updated user when complete. This operation is queued until the
230+
next flush.
231+
204232
## Server Timestamps
205233

206234
MockFirebase allow you to simulate the behavior of [server timestamps](https://www.firebase.com/docs/web/api/servervalue/timestamp.html) when using a real Firebase instance. Unless you use `Firebase.setClock`, `Firebase.ServerValue.TIMESTAMP` will be transformed to the current date (`Date.now()`) when your data change is flushed.

src/firebase-auth.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@ FirebaseAuth.prototype.getUser = function (uid, onComplete) {
7575
var user = null;
7676
err = err || self._validateExistingUid(uid);
7777
if (!err) {
78-
user = _.find(self._auth.users, function(u) {
79-
return u.uid == uid;
80-
});
78+
user = self._getUser(uid);
8179
if (onComplete) {
8280
onComplete(err, user.clone());
8381
}
@@ -91,6 +89,21 @@ FirebaseAuth.prototype.getUser = function (uid, onComplete) {
9189
});
9290
};
9391

92+
FirebaseAuth.prototype.updateUser = function (newUser) {
93+
const self = this;
94+
return new Promise((resolve, reject) => {
95+
this._defer('updateUser', _.toArray(arguments), () => {
96+
const i = _.findIndex(self._auth.users, u => u.uid === newUser.uid);
97+
if (i === -1) {
98+
reject(new Error('Tried to update a nonexistent user'));
99+
} else {
100+
self._auth.users[i] = newUser.clone();
101+
resolve(newUser);
102+
}
103+
});
104+
});
105+
};
106+
94107
// number of arguments
95108
var authMethods = {
96109
authWithCustomToken: 2,
@@ -205,6 +218,12 @@ FirebaseAuth.prototype._triggerAuthEvent = function () {
205218
});
206219
};
207220

221+
FirebaseAuth.prototype._getUser = function (uid) {
222+
return _.find(this._auth.users, function(u) {
223+
return u.uid === uid;
224+
});
225+
};
226+
208227
FirebaseAuth.prototype.getAuth = function () {
209228
return this.currentUser;
210229
};
@@ -279,7 +298,8 @@ FirebaseAuth.prototype._createUser = function (method, credentials, onComplete)
279298
phoneNumber: credentials.phoneNumber,
280299
emailVerified: credentials.emailVerified,
281300
displayName: credentials.displayName,
282-
photoURL: credentials.photoURL
301+
photoURL: credentials.photoURL,
302+
_tokenValidity: credentials._tokenValidity
283303
});
284304
self._auth.users.push(user);
285305
if (onComplete) {

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
var MockFirestoreDeltaDocumentSnapshot = require('./firestore-delta-document-snapshot');
44

55
exports.MockAuthentication = require('./auth');
6+
exports.MockUser = require('./user');
67
exports.MockFirebase = require('./firebase');
78
exports.MockFirebaseSdk = require('./sdk');
89
exports.MockFirestore = require('./firestore');

src/user.js

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ var Promise = require('rsvp').Promise;
66
function MockFirebaseUser(ref, data) {
77
this._auth = ref;
88
this._idtoken = Math.random().toString();
9-
this.customClaims = {};
9+
this._tokenValidity = _tokenValidity(
10+
data._tokenValidity ? data._tokenValidity : {}
11+
);
12+
this.customClaims = data.customClaims || {};
1013
this.uid = data.uid;
1114
this.email = data.email;
1215
this.password = data.password;
@@ -21,10 +24,19 @@ function MockFirebaseUser(ref, data) {
2124
this.refreshToken = data.refreshToken;
2225
}
2326

27+
MockFirebaseUser.msg_tokenExpiresBeforeIssuance =
28+
'Auth token expires before it is issued';
29+
MockFirebaseUser.msg_tokenIssuedBeforeAuth =
30+
'Auth token was issued before the user authenticated';
31+
MockFirebaseUser.msg_tokenAuthedInTheFuture =
32+
'Auth token shows user authenticating in the future';
33+
MockFirebaseUser.msg_tokenIssuedInTheFuture =
34+
'Auth token was issued in the future';
35+
2436
MockFirebaseUser.prototype.clone = function () {
2537
var user = new MockFirebaseUser(this._auth, this);
2638
user._idtoken = this._idtoken;
27-
user.customClaims = this.customClaims;
39+
user.customClaims = _.cloneDeep(this.customClaims);
2840
return user;
2941
};
3042

@@ -105,10 +117,65 @@ MockFirebaseUser.prototype.getIdToken = function (forceRefresh) {
105117
var self = this;
106118
return new Promise(function(resolve) {
107119
if (forceRefresh) {
108-
self._idtoken = Math.random().toString();
120+
self._refreshIdToken();
109121
}
110122
resolve(self._idtoken);
111123
});
112124
};
113125

126+
MockFirebaseUser.prototype.getIdTokenResult = function (forceRefresh) {
127+
if (forceRefresh) {
128+
this._refreshIdToken();
129+
}
130+
131+
return Promise.resolve({
132+
authTime: this._tokenValidity.authTime.toISOString(),
133+
issuedAtTime: this._tokenValidity.issuedAtTime.toISOString(),
134+
expirationTime: this._tokenValidity.expirationTime.toISOString(),
135+
signInProvider: this.providerId || null,
136+
claims: this.customClaims,
137+
token: this._idtoken,
138+
});
139+
};
140+
141+
MockFirebaseUser.prototype._refreshIdToken = function () {
142+
this._tokenValidity.issuedAtTime = new Date();
143+
this._tokenValidity.expirationTime = defaultExpirationTime(new Date());
144+
this._idtoken = Math.random().toString();
145+
return this._auth.updateUser(this)
146+
.then(() => this.getIdTokenResult())
147+
.catch(() => this.getIdTokenResult());
148+
};
149+
150+
/** Create a user's internal token validity store
151+
*
152+
* @param data the `data.idTokenResult` object from the User constructor
153+
* @return object that is to become the User's _tokenValidity member
154+
*/
155+
function _tokenValidity(data) {
156+
const now = new Date();
157+
const authTime = data.authTime ?
158+
data.authTime : new Date();
159+
const issuedTime = data.issuedAtTime || new Date(authTime.getTime());
160+
const expirationTime = data.expirationTime ?
161+
data.expirationTime : defaultExpirationTime(issuedTime);
162+
if (expirationTime < issuedTime) {
163+
throw new Error(MockFirebaseUser.msg_tokenExpiresBeforeIssuance);
164+
} else if (issuedTime < authTime) {
165+
throw new Error(MockFirebaseUser.msg_tokenIssuedBeforeAuth);
166+
} else if (now < authTime) {
167+
throw new Error(MockFirebaseUser.msg_tokenAuthedInTheFuture);
168+
} else if (now < issuedTime) {
169+
throw new Error(MockFirebaseUser.msg_tokenIssuedInTheFuture);
170+
} else return {
171+
authTime: authTime,
172+
issuedAtTime: issuedTime,
173+
expirationTime: expirationTime,
174+
};
175+
}
176+
177+
function defaultExpirationTime(issuedTime) {
178+
return new Date(issuedTime.getTime() + 3600000);
179+
}
180+
114181
module.exports = MockFirebaseUser;

0 commit comments

Comments
 (0)