|
1 | 1 | 'use strict'; |
2 | 2 |
|
3 | | -var expect = require('chai').use(require('sinon-chai')).expect; |
4 | | -var sinon = require('sinon'); |
5 | | -var User = require('../../src/user'); |
6 | | -var Firebase = require('../../').MockFirebase; |
7 | | - |
8 | | -describe('User', function () { |
9 | | - var auth; |
10 | | - beforeEach(function () { |
| 3 | +const expect = require('chai').use(require('sinon-chai')).expect; |
| 4 | +const sinon = require('sinon'); |
| 5 | +const User = require('../../src/user'); |
| 6 | +const Firebase = require('../../').MockFirebase; |
| 7 | +const _isEqual = require('lodash.isequal'); |
| 8 | +const _cloneDeep = require('lodash.clonedeep'); |
| 9 | + |
| 10 | +describe('User', function() { |
| 11 | + let auth; |
| 12 | + let now; |
| 13 | + let clock; |
| 14 | + |
| 15 | + beforeEach(function() { |
11 | 16 | auth = new Firebase().child('data'); |
12 | 17 | auth.autoFlush(); |
| 18 | + now = new Date(randomTimestamp()); |
| 19 | + clock = sinon.useFakeTimers(now); |
| 20 | + }); |
| 21 | + |
| 22 | + afterEach(() => { |
| 23 | + clock.restore(); |
| 24 | + }); |
| 25 | + |
| 26 | + describe('#constructor', function() { |
| 27 | + |
| 28 | + it('should reject ID tokens that expire before the issuance time', () => { |
| 29 | + expect(() => { |
| 30 | + const t = randomTimestamp(); |
| 31 | + new User(auth, { |
| 32 | + _tokenValidity: { |
| 33 | + authTime: new Date(t - 2), |
| 34 | + issuedAtTime: new Date(t), |
| 35 | + expirationTime: new Date(t - 1), |
| 36 | + } |
| 37 | + }); |
| 38 | + }).to.throw(User.msg_tokenExpiresBeforeIssuance); |
| 39 | + }); |
| 40 | + |
| 41 | + it('should reject ID tokens that are issued before the auth time', () => { |
| 42 | + expect(() => { |
| 43 | + const t = randomTimestamp(); |
| 44 | + new User(auth, { |
| 45 | + _tokenValidity: { |
| 46 | + authTime: new Date(t), |
| 47 | + issuedAtTime: new Date(t - 1), |
| 48 | + } |
| 49 | + }); |
| 50 | + }).to.throw(User.msg_tokenIssuedBeforeAuth); |
| 51 | + }); |
| 52 | + |
| 53 | + it('should reject ID tokens that are issued in the future', () => { |
| 54 | + expect(() => new User(auth, { |
| 55 | + _tokenValidity: { |
| 56 | + authTime: new Date(now.getTime() - 1), |
| 57 | + issuedAtTime: new Date(now.getTime() + 1), |
| 58 | + }, |
| 59 | + })).to.throw(User.msg_tokenIssuedInTheFuture); |
| 60 | + }); |
| 61 | + |
| 62 | + it('should reject ID tokens that show the user authenticating in the future', () => { |
| 63 | + expect(() => { |
| 64 | + new User(auth, { |
| 65 | + _tokenValidity: { |
| 66 | + authTime: new Date(now.getTime() + 1), |
| 67 | + }, |
| 68 | + }); |
| 69 | + }).to.throw(User.msg_tokenAuthedInTheFuture); |
| 70 | + }); |
13 | 71 | }); |
14 | 72 |
|
15 | 73 | describe('#delete', function() { |
@@ -135,9 +193,222 @@ describe('User', function () { |
135 | 193 | }); |
136 | 194 |
|
137 | 195 | it('should refresh token', function() { |
138 | | - var user = new User(auth, {}); |
139 | | - var token = user._idtoken; |
| 196 | + const user = new User(auth, {}); |
| 197 | + const token = user._idtoken; |
140 | 198 | return expect(user.getIdToken(true)).to.eventually.not.equal(token); |
141 | 199 | }); |
| 200 | + |
| 201 | + it('should refresh token result', function() { |
| 202 | + const authTime = new Date(randomPastTimestamp()); |
| 203 | + const user = new User(auth, { |
| 204 | + _tokenValidity: { |
| 205 | + authTime: authTime, |
| 206 | + issuedAtTime: authTime, |
| 207 | + }, |
| 208 | + }); |
| 209 | + return expect(user.getIdToken(true) |
| 210 | + .then(() => user.getIdTokenResult(false)) |
| 211 | + .then(r => |
| 212 | + r.issuedAtTime === now.toISOString() && |
| 213 | + r.expirationTime === new Date(now.getTime() + 3600000).toISOString() |
| 214 | + ) |
| 215 | + ).to.eventually.equal(true); |
| 216 | + }); |
| 217 | + }); |
| 218 | + |
| 219 | + describe('#getIdTokenResult', function() { |
| 220 | + |
| 221 | + it('should use defaults if the id token result param is omitted', () => { |
| 222 | + expect(new User(auth, {}).getIdTokenResult()) |
| 223 | + .to.eventually.deep.equal(new User(auth, {_tokenValidity: {}})); |
| 224 | + }); |
| 225 | + |
| 226 | + describe('without forceRefresh', () => { |
| 227 | + describe('.authTime', () => { |
| 228 | + it('should use provided auth time', () => { |
| 229 | + const authTime = new Date(randomPastTimestamp()); |
| 230 | + const user = new User(auth, { |
| 231 | + _tokenValidity: { |
| 232 | + authTime: authTime, |
| 233 | + }, |
| 234 | + }); |
| 235 | + return expect(user.getIdTokenResult().then(r => r.authTime)) |
| 236 | + .to.eventually.equal(authTime.toISOString()); |
| 237 | + }); |
| 238 | + |
| 239 | + it('should default auth time to current time', () => { |
| 240 | + const user = new User(auth, {_tokenValidity: {}}); |
| 241 | + return expect(user.getIdTokenResult().then(r => r.authTime)) |
| 242 | + .to.eventually.equal(now.toISOString()); |
| 243 | + }); |
| 244 | + }); |
| 245 | + |
| 246 | + describe('.issuedAtTime', () => { |
| 247 | + it('should use provided issued-at time', () => { |
| 248 | + const issuedTs = randomPastTimestamp(); |
| 249 | + const issuedTime = new Date(issuedTs); |
| 250 | + const user = new User(auth, { |
| 251 | + _tokenValidity: { |
| 252 | + authTime: new Date(issuedTs - 1), |
| 253 | + issuedAtTime: issuedTime, |
| 254 | + }, |
| 255 | + }); |
| 256 | + return expect(user.getIdTokenResult().then(r => r.issuedAtTime)) |
| 257 | + .to.eventually.equal(issuedTime.toISOString()); |
| 258 | + }); |
| 259 | + |
| 260 | + it('should default to auth time', () => { |
| 261 | + const authTime = new Date(randomPastTimestamp()); |
| 262 | + const user = new User(auth, { |
| 263 | + _tokenValidity: { |
| 264 | + authTime: authTime, |
| 265 | + }, |
| 266 | + }); |
| 267 | + return expect(user.getIdTokenResult().then(r => r.issuedAtTime)) |
| 268 | + .to.eventually.equal(authTime.toISOString()); |
| 269 | + }); |
| 270 | + }); |
| 271 | + |
| 272 | + describe('.expirationTime', () => { |
| 273 | + it('should use provided expiration time', () => { |
| 274 | + const expTime = new Date(now.getTime() + 1); |
| 275 | + const user = new User(auth, { |
| 276 | + _tokenValidity: { |
| 277 | + expirationTime: expTime, |
| 278 | + }, |
| 279 | + }); |
| 280 | + return expect(user.getIdTokenResult().then(r => r.expirationTime)) |
| 281 | + .to.eventually.equal(expTime.toISOString()); |
| 282 | + }); |
| 283 | + |
| 284 | + it('should default to issued at time plus 1 hour', () => { |
| 285 | + const authTime = new Date(now.getTime() - 1); |
| 286 | + const issuedTime = new Date(now.getTime()); |
| 287 | + const expTime = new Date(now.getTime() + 3600000); |
| 288 | + const user = new User(auth, { |
| 289 | + _tokenValidity: { |
| 290 | + authTime: authTime, |
| 291 | + issuedAtTime: issuedTime, |
| 292 | + }, |
| 293 | + }); |
| 294 | + return expect(user.getIdTokenResult().then(r => r.expirationTime)) |
| 295 | + .to.eventually.equal(expTime.toISOString()); |
| 296 | + }); |
| 297 | + }); |
| 298 | + |
| 299 | + describe('.signInProvider', () => { |
| 300 | + it('should use User\'s providerId string', () => { |
| 301 | + const providerName = 'google'; |
| 302 | + const user = new User(auth, { |
| 303 | + providerId: providerName, |
| 304 | + _tokenValidity: {}, |
| 305 | + }); |
| 306 | + return expect(user.getIdTokenResult() |
| 307 | + .then(r => r.signInProvider) |
| 308 | + ).to.eventually.equal(providerName); |
| 309 | + }); |
| 310 | + |
| 311 | + it('should default to null', () => { |
| 312 | + const user = new User(auth, {_tokenValidity: {}}); |
| 313 | + return expect(user.getIdTokenResult() |
| 314 | + .then(r => r.signInProvider) |
| 315 | + ).to.eventually.equal(null); |
| 316 | + }); |
| 317 | + }); |
| 318 | + |
| 319 | + describe('.claims', () => { |
| 320 | + it('should use user\'s customClaims object', () => { |
| 321 | + const claims = {'testclaim': 'abcd'}; |
| 322 | + const user = new User(auth, { |
| 323 | + _tokenValidity: {}, |
| 324 | + customClaims: claims, |
| 325 | + }); |
| 326 | + return expect(user.getIdTokenResult().then(r => r.claims)) |
| 327 | + .to.eventually.deep.equal(claims); |
| 328 | + }); |
| 329 | + |
| 330 | + it('should default to empty object', () => { |
| 331 | + const user = new User(auth, {_tokenValidity: {},}); |
| 332 | + return expect(user.getIdTokenResult().then(r => r.claims)) |
| 333 | + .to.eventually.deep.equal({}); |
| 334 | + }); |
| 335 | + }); |
| 336 | + |
| 337 | + describe('.token', () => { |
| 338 | + it('should be the same as returned from getIdToken', () => { |
| 339 | + const user = new User(auth, {_tokenValidity: {},}); |
| 340 | + return expect(Promise.all([ |
| 341 | + user.getIdTokenResult().then(r => r.token), |
| 342 | + user.getIdToken() |
| 343 | + ]).then(([t1, t2]) => t1 === t2)).to.eventually.equal(true); |
| 344 | + }); |
| 345 | + }); |
| 346 | + }); |
| 347 | + |
| 348 | + describe('with forceRefresh', () => { |
| 349 | + it('persists the new token', () => { |
| 350 | + const user = new User(auth, { |
| 351 | + _tokenValidity: { |
| 352 | + authTime: new Date(randomPastTimestamp()), |
| 353 | + }, |
| 354 | + }); |
| 355 | + return expect(user.getIdTokenResult(true) |
| 356 | + .then(_t1 => { |
| 357 | + const t1 = _cloneDeep(_t1); |
| 358 | + return user.getIdTokenResult(false).then(t2 => |
| 359 | + _isEqual(t1, t2) |
| 360 | + ); |
| 361 | + }) |
| 362 | + ).to.eventually.equal(true); |
| 363 | + }); |
| 364 | + |
| 365 | + it('should use authTime from previous token', () => { |
| 366 | + const authTime = new Date(randomPastTimestamp()); |
| 367 | + const user = new User(auth, { |
| 368 | + _tokenValidity: |
| 369 | + {authTime: authTime}, |
| 370 | + }); |
| 371 | + return expect(user.getIdTokenResult(true).then(r => r.authTime)) |
| 372 | + .to.eventually.equal(authTime.toISOString()); |
| 373 | + }); |
| 374 | + |
| 375 | + it('should use current time as issuance time', () => { |
| 376 | + const user = new User(auth, { |
| 377 | + _tokenValidity: { |
| 378 | + authTime: new Date(randomPastTimestamp()), |
| 379 | + } |
| 380 | + }); |
| 381 | + return expect(user.getIdTokenResult(true).then(r => r.issuedAtTime)) |
| 382 | + .to.eventually.equal(now.toISOString()); |
| 383 | + }); |
| 384 | + |
| 385 | + it('should expire one hour after issuance', () => { |
| 386 | + const user = new User(auth, { |
| 387 | + _tokenValidity: { |
| 388 | + authTime: new Date(randomPastTimestamp()), |
| 389 | + }, |
| 390 | + }); |
| 391 | + const expTime = new Date(now.getTime() + 3600000); |
| 392 | + return expect(user.getIdTokenResult(true).then(r => r.expirationTime)) |
| 393 | + .to.eventually.equal(expTime.toISOString()); |
| 394 | + }); |
| 395 | + |
| 396 | + it('should generate a new token', () => { |
| 397 | + const user = new User(auth, {_tokenValidity: {},}); |
| 398 | + return expect(user.getIdToken(false) |
| 399 | + .then(oldToken => user.getIdTokenResult(true) |
| 400 | + .then(newTokenResult => oldToken === newTokenResult.token) |
| 401 | + ) |
| 402 | + ).to.eventually.equal(false); |
| 403 | + }); |
| 404 | + }); |
142 | 405 | }); |
143 | 406 | }); |
| 407 | + |
| 408 | +function randomTimestamp() { |
| 409 | + return Math.floor(Math.random() * 4000000000000); |
| 410 | +} |
| 411 | + |
| 412 | +function randomPastTimestamp() { |
| 413 | + return Math.floor(Math.random() * Date.now()); |
| 414 | +} |
0 commit comments