1- import { isEmpty } from '@ember/utils' ;
2- import { run , later , cancel } from '@ember/runloop' ;
31import { A , makeArray } from '@ember/array' ;
42import { warn } from '@ember/debug' ;
53import { getOwner } from '@ember/application' ;
64import BaseAuthenticator from './base' ;
75import isFastBoot from '../utils/is-fastboot' ;
86import { waitFor } from '@ember/test-waiters' ;
97import { isTesting } from '@embroider/macros' ;
8+ import type { Timer } from '@ember/runloop' ;
9+ import { run } from '@ember/runloop' ;
10+ import { cancel } from '@ember/runloop' ;
11+ import { later } from '@ember/runloop' ;
12+
13+ type OAuthResponseSuccess = {
14+ access_token : string ;
15+ token_type : string ;
16+ expires_in ?: number ;
17+ expires_at ?: number ;
18+ refresh_token ?: string ;
19+ scope ?: string ;
20+ } ;
21+
22+ type OAuthPasswordRequestData = {
23+ grant_type : string ;
24+ username : string ;
25+ password : string ;
26+ client_id ?: string ;
27+ scope ?: string ;
28+ } ;
29+
30+ type OAuthInvalidateRequestData = {
31+ token_type_hint : 'access_token' | 'refresh_token' ;
32+ token : string ;
33+ client_id ?: string ;
34+ scope ?: string ;
35+ } ;
36+
37+ type OAuthRefreshRequestData = {
38+ grant_type : 'refresh_token' ;
39+ refresh_token : string ;
40+ scope ?: string ;
41+ client_id ?: string ;
42+ } ;
43+
44+ type MakeRequestData =
45+ | OAuthPasswordRequestData
46+ | OAuthInvalidateRequestData
47+ | OAuthRefreshRequestData ;
1048
1149/**
1250 Authenticator that conforms to OAuth 2
@@ -46,7 +84,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
4684 @default null
4785 @public
4886 */
49- clientId = null ;
87+ clientId : string | null = null ;
5088
5189 /**
5290 The endpoint on the server that authentication and token refresh requests
@@ -58,7 +96,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
5896 @default '/token'
5997 @public
6098 */
61- serverTokenEndpoint = '/token' ;
99+ serverTokenEndpoint : string = '/token' ;
62100
63101 /**
64102 The endpoint on the server that token revocation requests are sent to. Only
@@ -75,7 +113,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
75113 @default null
76114 @public
77115 */
78- serverTokenRevocationEndpoint = null ;
116+ serverTokenRevocationEndpoint : string | null = null ;
79117
80118 /**
81119 Sets whether the authenticator automatically refreshes access tokens if the
@@ -124,7 +162,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
124162 return ( Math . floor ( Math . random ( ) * ( max - min ) ) + min ) * 1000 ;
125163 }
126164
127- _refreshTokenTimeout = null ;
165+ _refreshTokenTimeout : Timer | undefined = undefined ;
128166
129167 /**
130168 Restores the session from a session data object; __will return a resolving
@@ -144,16 +182,17 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
144182 @return {Promise } A promise that when it resolves results in the session becoming or remaining authenticated. If restoration fails, the promise will reject with the server response (in case the access token had expired and was refreshed using a refresh token); however, the authenticator reads that response already so if you need to read it again you need to clone the response object first
145183 @public
146184 */
147- restore ( data ) {
185+ restore ( data : OAuthResponseSuccess ) {
148186 return new Promise ( ( resolve , reject ) => {
149187 const now = new Date ( ) . getTime ( ) ;
150188 const refreshAccessTokens = this . get ( 'refreshAccessTokens' ) ;
151- if ( ! isEmpty ( data [ 'expires_at' ] ) && data [ 'expires_at' ] < now ) {
189+ if ( data [ 'expires_at' ] && data [ 'expires_at' ] < now ) {
152190 if ( refreshAccessTokens ) {
153- this . _refreshAccessToken ( data [ 'expires_in' ] , data [ 'refresh_token' ] , data [ 'scope' ] ) . then (
154- resolve ,
155- reject
156- ) ;
191+ this . _refreshAccessToken (
192+ data [ 'expires_in' ] ,
193+ data [ 'refresh_token' ] as string ,
194+ data [ 'scope' ]
195+ ) . then ( resolve , reject ) ;
157196 } else {
158197 reject ( ) ;
159198 }
@@ -228,13 +267,17 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
228267 @return {Promise } A promise that when it resolves results in the session becoming authenticated. If authentication fails, the promise will reject with the server response; however, the authenticator reads that response already so if you need to read it again you need to clone the response object first
229268 @public
230269 */
231- authenticate ( identification , password , scope = [ ] , headers = { } ) {
270+ authenticate ( identification : string , password : string , scope = [ ] , headers = { } ) {
232271 return new Promise ( ( resolve , reject ) => {
233- const data = { grant_type : 'password' , username : identification , password } ;
272+ const data : OAuthPasswordRequestData = {
273+ grant_type : 'password' ,
274+ username : identification ,
275+ password,
276+ } ;
234277 const serverTokenEndpoint = this . get ( 'serverTokenEndpoint' ) ;
235278
236279 const scopesString = makeArray ( scope ) . join ( ' ' ) ;
237- if ( ! isEmpty ( scopesString ) ) {
280+ if ( scopesString . trim ( ) . length > 0 ) {
238281 data . scope = scopesString ;
239282 }
240283 this . makeRequest ( serverTokenEndpoint , data , headers ) . then (
@@ -250,7 +293,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
250293 expiresAt ,
251294 response [ 'refresh_token' ]
252295 ) ;
253- if ( ! isEmpty ( expiresAt ) ) {
296+ if ( expiresAt ) {
254297 response = Object . assign ( response , { expires_at : expiresAt } ) ;
255298 }
256299
@@ -279,21 +322,21 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
279322 @return {Promise } A promise that when it resolves results in the session being invalidated. If invalidation fails, the promise will reject with the server response (in case token revocation is used); however, the authenticator reads that response already so if you need to read it again you need to clone the response object first
280323 @public
281324 */
282- invalidate ( data ) {
325+ invalidate ( data : OAuthResponseSuccess ) {
283326 const serverTokenRevocationEndpoint = this . get ( 'serverTokenRevocationEndpoint' ) ;
284- function success ( resolve ) {
327+ const success = ( resolve : ( value ?: unknown ) => void ) => {
285328 cancel ( this . _refreshTokenTimeout ) ;
286329 delete this . _refreshTokenTimeout ;
287330 resolve ( ) ;
288- }
331+ } ;
289332 return new Promise ( resolve => {
290- if ( isEmpty ( serverTokenRevocationEndpoint ) ) {
291- success . apply ( this , [ resolve ] ) ;
333+ if ( ! serverTokenRevocationEndpoint ) {
334+ success ( resolve ) ;
292335 } else {
293- const requests = [ ] ;
294- A ( [ 'access_token' , 'refresh_token' ] ) . forEach ( tokenType => {
336+ const requests : Promise < OAuthResponseSuccess > [ ] = [ ] ;
337+ ( [ 'access_token' , 'refresh_token' ] as const ) . forEach ( tokenType => {
295338 const token = data [ tokenType ] ;
296- if ( ! isEmpty ( token ) ) {
339+ if ( token ) {
297340 requests . push (
298341 this . makeRequest ( serverTokenRevocationEndpoint , {
299342 token_type_hint : tokenType ,
@@ -322,18 +365,29 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
322365 @protected
323366 */
324367 @waitFor
325- makeRequest ( url , data , headers = { } ) {
368+ makeRequest (
369+ url : string ,
370+ data : MakeRequestData ,
371+ headers : Record < string , string > = { }
372+ ) : Promise < OAuthResponseSuccess & { responseText : string } & { responseJSON : string } > {
326373 headers [ 'Content-Type' ] = 'application/x-www-form-urlencoded' ;
327374
328375 const clientId = this . get ( 'clientId' ) ;
329- if ( ! isEmpty ( clientId ) ) {
330- data [ ' client_id' ] = this . get ( ' clientId' ) ;
376+ if ( clientId ) {
377+ data . client_id = clientId ;
331378 }
332379
333380 const body = Object . keys ( data )
334381 . map ( key => {
335- return `${ encodeURIComponent ( key ) } =${ encodeURIComponent ( data [ key ] ) } ` ;
382+ const value = data [ key as keyof MakeRequestData ] ;
383+
384+ if ( value ) {
385+ return `${ encodeURIComponent ( key ) } =${ encodeURIComponent ( value ) } ` ;
386+ } else {
387+ return null ;
388+ }
336389 } )
390+ . filter ( Boolean )
337391 . join ( '&' ) ;
338392
339393 const options = {
@@ -349,13 +403,15 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
349403 try {
350404 let json = JSON . parse ( text ) ;
351405 if ( ! response . ok ) {
352- response . responseJSON = json ;
406+ // @TODO : migrate the old AJAX API.
407+ ( response as any ) . responseJSON = json ;
353408 reject ( response ) ;
354409 } else {
355410 resolve ( json ) ;
356411 }
357412 } catch ( SyntaxError ) {
358- response . responseText = text ;
413+ // @TODO : migrate the old AJAX API.
414+ ( response as any ) . responseText = text ;
359415 reject ( response ) ;
360416 }
361417 } ) ;
@@ -364,34 +420,41 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
364420 } ) ;
365421 }
366422
367- _scheduleAccessTokenRefresh ( expiresIn , expiresAt , refreshToken ) {
423+ _scheduleAccessTokenRefresh (
424+ expiresIn : number | undefined ,
425+ expiresAt : number | null | undefined ,
426+ refreshToken : string | undefined
427+ ) {
368428 const refreshAccessTokens = this . get ( 'refreshAccessTokens' ) && ! isFastBoot ( getOwner ( this ) ) ;
369429 if ( refreshAccessTokens ) {
370430 const now = new Date ( ) . getTime ( ) ;
371- if ( isEmpty ( expiresAt ) && ! isEmpty ( expiresIn ) ) {
431+ if ( ! expiresAt && expiresIn ) {
372432 expiresAt = new Date ( now + expiresIn * 1000 ) . getTime ( ) ;
373433 }
374434 const offset = this . get ( 'tokenRefreshOffset' ) ;
375- if ( ! isEmpty ( refreshToken ) && ! isEmpty ( expiresAt ) && expiresAt > now - offset ) {
435+ if ( refreshToken && expiresAt && expiresAt > now - offset ) {
376436 cancel ( this . _refreshTokenTimeout ) ;
377437 delete this . _refreshTokenTimeout ;
378438 if ( ! isTesting ( ) ) {
379439 this . _refreshTokenTimeout = later (
380- this ,
381- this . _refreshAccessToken ,
382- expiresIn ,
383- refreshToken ,
384- expiresAt - now - offset
440+ ( ) => {
441+ this . _refreshAccessToken ( expiresIn , refreshToken ) ;
442+ } ,
443+ ( expiresAt as number ) - now - offset
385444 ) ;
386445 }
387446 }
388447 }
389448 }
390449
391- _refreshAccessToken ( expiresIn , refreshToken , scope ) {
392- const data = { grant_type : 'refresh_token' , refresh_token : refreshToken } ;
450+ _refreshAccessToken ( expiresIn : number | undefined , refreshToken : string , scope ?: string ) {
451+ const data : OAuthRefreshRequestData = {
452+ grant_type : 'refresh_token' ,
453+ refresh_token : refreshToken ,
454+ scope : '' ,
455+ } ;
393456 const refreshAccessTokensWithScope = this . get ( 'refreshAccessTokensWithScope' ) ;
394- if ( refreshAccessTokensWithScope && ! isEmpty ( scope ) ) {
457+ if ( refreshAccessTokensWithScope && scope ) {
395458 data . scope = scope ;
396459 }
397460
@@ -409,7 +472,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
409472 expires_at : expiresAt ,
410473 refresh_token : refreshToken ,
411474 } ) ;
412- if ( refreshAccessTokensWithScope && ! isEmpty ( scope ) ) {
475+ if ( refreshAccessTokensWithScope && scope ) {
413476 data . scope = scope ;
414477 }
415478 this . _scheduleAccessTokenRefresh ( expiresIn , null , refreshToken ) ;
@@ -429,13 +492,13 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator
429492 } ) ;
430493 }
431494
432- _absolutizeExpirationTime ( expiresIn ) {
433- if ( ! isEmpty ( expiresIn ) ) {
495+ _absolutizeExpirationTime ( expiresIn : number | undefined ) {
496+ if ( expiresIn ) {
434497 return new Date ( new Date ( ) . getTime ( ) + expiresIn * 1000 ) . getTime ( ) ;
435498 }
436499 }
437500
438- _validate ( data ) {
439- return ! isEmpty ( data [ 'access_token' ] ) ;
501+ _validate ( data : OAuthResponseSuccess ) {
502+ return Boolean ( data [ 'access_token' ] ) ;
440503 }
441504}
0 commit comments