@@ -20,7 +20,7 @@ import {
20
20
ThrottlingConstants ,
21
21
TimeUtils ,
22
22
} from "@azure/msal-common" ;
23
- import { ClientCredentialClient , UsernamePasswordClient } from "../../src" ;
23
+ import { AppTokenProviderParameters , ClientCredentialClient , UsernamePasswordClient } from "../../src" ;
24
24
import {
25
25
AUTHENTICATION_RESULT_DEFAULT_SCOPES ,
26
26
CONFIDENTIAL_CLIENT_AUTHENTICATION_RESULT ,
@@ -1099,71 +1099,101 @@ describe("ClientCredentialClient unit tests", () => {
1099
1099
} ) ;
1100
1100
1101
1101
it ( "Uses the extensibility AppTokenProvider callback to get a token" , async ( ) => {
1102
- sinon
1103
- . stub ( Authority . prototype , < any > "getEndpointMetadataFromNetwork" )
1104
- . resolves ( DEFAULT_OPENID_CONFIG_RESPONSE . body ) ;
1105
- // no need to stub out the token response, MSAL will use the AppTokenProvider instead
1102
+ await runAppTokenProviderTestAsync ( 3600 , 500 , 500 ) ; // refresh in is read from the AppTokenProvider
1103
+ } ) ;
1104
+
1105
+ it ( "AppTokenProvider sets a refresh in to half of expires in" , async ( ) => {
1106
+ await runAppTokenProviderTestAsync ( 7200 , undefined , 3600 ) ; // refresh in is inferred as 1/2 expires in
1107
+
1108
+
1109
+ } ) ;
1110
+ it ( "AppTokenProvider does not set refresh in if expires in < 7200" , async ( ) => {
1111
+ await runAppTokenProviderTestAsync ( 7000 , undefined , undefined ) ; // refresh in not inferred because expires in < 7200
1112
+ } ) ;
1106
1113
1114
+ function validateAppTokenProvider ( config : ClientConfiguration , authResult : AuthenticationResult , expectedExpiresIn : number , expectedRefreshIn : number | undefined ) {
1115
+
1116
+ expect ( authResult . scopes ) . toEqual ( [ TEST_CONFIG . DEFAULT_GRAPH_SCOPE [ 0 ] ] ) ;
1117
+ expect ( authResult . accessToken ) . toEqual ( "some_token" ) ;
1118
+ expect ( authResult . state ) . toHaveLength ( 0 ) ;
1119
+
1120
+ const actualExpiresIn = ( authResult . expiresOn ! . valueOf ( ) - Date . now ( ) . valueOf ( ) ) / 1000 ;
1121
+ validateDuration ( actualExpiresIn , expectedExpiresIn ! ) ;
1122
+
1123
+
1124
+ const accessTokenKey = config . storageInterface . getKeys ( ) ;
1125
+
1126
+ // const accessTokenKey = config.storageInterface?.getKeys().find(value => value.indexOf("accesstoken") >= 0);
1127
+ const accessTokenCacheItem = accessTokenKey ? config . storageInterface ?. getAccessTokenCredential ( accessTokenKey ) : null ;
1128
+ expect ( accessTokenCacheItem ) . not . toBeNull ( ) ;
1129
+ expect ( accessTokenCacheItem ?. refreshOn ) . not . toBeNull ( ) ;
1130
+
1131
+ if ( expectedRefreshIn == undefined ) {
1132
+ expect ( accessTokenCacheItem ?. refreshOn ) . toBeUndefined ( ) ;
1133
+ } else {
1134
+ const refreshOnUnixTimestamp = Number ( accessTokenCacheItem ?. refreshOn ) * 1000 ;
1135
+ const refreshOnDate = new Date ( refreshOnUnixTimestamp ) ;
1136
+ const refreshOnDiff = ( refreshOnDate . valueOf ( ) - Date . now ( ) . valueOf ( ) ) / 1000 ;
1137
+ validateDuration ( refreshOnDiff , expectedRefreshIn ! ) ;
1138
+ }
1139
+ }
1140
+
1141
+ function validateDuration ( actualDuration : number , expectedDuration : number ) {
1142
+ // small buffer for test runtime differences
1143
+ expect ( actualDuration ) . toBeLessThanOrEqual ( expectedDuration + 10 ) ;
1144
+ expect ( actualDuration ) . toBeGreaterThan ( expectedDuration - 10 ) ;
1145
+ }
1146
+
1147
+ async function runAppTokenProviderTestAsync ( actualExpiresIn : number , actualRefreshIn : number | undefined , expectedRefreshIn : number | undefined ) {
1148
+ sinon . stub ( Authority . prototype , < any > "getEndpointMetadataFromNetwork" ) . resolves ( DEFAULT_OPENID_CONFIG_RESPONSE . body ) ;
1149
+ // no need to stub out the token response, MSAL will use the AppTokenProvider instead
1150
+ const config = await ClientTestUtils . createTestClientConfiguration ( ) ;
1107
1151
const accessToken = "some_token" ;
1108
1152
const appTokenProviderResult : AppTokenProviderResult = {
1109
1153
accessToken : accessToken ,
1110
- expiresInSeconds : 1800 ,
1111
- refreshInSeconds : 900 ,
1154
+ expiresInSeconds : actualExpiresIn ,
1155
+ refreshInSeconds : actualRefreshIn ,
1112
1156
} ;
1113
-
1157
+
1114
1158
const expectedScopes = [ TEST_CONFIG . DEFAULT_GRAPH_SCOPE [ 0 ] ] ;
1115
-
1159
+
1116
1160
let callbackedCalledCount = 0 ;
1117
-
1118
- const appTokenProvider : IAppTokenProvider = (
1119
- appTokenProviderParameters
1120
- ) => {
1161
+
1162
+ const appTokenProvider : IAppTokenProvider = ( appTokenProviderParameters : AppTokenProviderParameters ) => {
1163
+
1121
1164
callbackedCalledCount ++ ;
1122
-
1165
+
1123
1166
expect ( appTokenProviderParameters . scopes ) . toEqual ( expectedScopes ) ;
1124
1167
expect ( appTokenProviderParameters . tenantId ) . toEqual ( "common" ) ;
1125
- expect ( appTokenProviderParameters . correlationId ) . toEqual (
1126
- TEST_CONFIG . CORRELATION_ID
1127
- ) ;
1168
+ expect ( appTokenProviderParameters . correlationId ) . toEqual ( TEST_CONFIG . CORRELATION_ID ) ;
1128
1169
expect ( appTokenProviderParameters . claims ) . toBeUndefined ( ) ;
1129
-
1130
- return new Promise < AppTokenProviderResult > ( ( resolve ) =>
1131
- resolve ( appTokenProviderResult )
1132
- ) ;
1170
+
1171
+ return new Promise < AppTokenProviderResult > (
1172
+ ( resolve ) => resolve ( appTokenProviderResult ) ) ;
1133
1173
} ;
1134
-
1174
+
1135
1175
// client credentials not needed
1136
1176
config . clientCredentials = undefined ;
1137
-
1177
+
1138
1178
const client = new ClientCredentialClient ( config , appTokenProvider ) ;
1139
1179
const clientCredentialRequest : CommonClientCredentialRequest = {
1140
1180
authority : TEST_CONFIG . validAuthority ,
1141
1181
correlationId : TEST_CONFIG . CORRELATION_ID ,
1142
1182
scopes : TEST_CONFIG . DEFAULT_GRAPH_SCOPE ,
1143
1183
} ;
1144
-
1145
- const authResult = ( await client . acquireToken (
1146
- clientCredentialRequest
1147
- ) ) as AuthenticationResult ;
1148
-
1184
+
1185
+ const authResult = await client . acquireToken ( clientCredentialRequest ) as AuthenticationResult ;
1186
+
1149
1187
expect ( callbackedCalledCount ) . toEqual ( 1 ) ;
1150
-
1151
- expect ( authResult . scopes ) . toEqual ( expectedScopes ) ;
1152
- expect ( authResult . accessToken ) . toEqual ( accessToken ) ;
1153
- expect ( authResult . state ) . toHaveLength ( 0 ) ;
1154
- const dateDiff =
1155
- ( authResult . expiresOn ! . valueOf ( ) - Date . now ( ) . valueOf ( ) ) / 1000 ;
1156
- expect ( dateDiff ) . toBeLessThanOrEqual ( 1900 ) ;
1157
- expect ( dateDiff ) . toBeGreaterThan ( 1700 ) ;
1158
-
1159
- const authResult2 = ( await client . acquireToken (
1160
- clientCredentialRequest
1161
- ) ) as AuthenticationResult ;
1162
-
1188
+
1189
+ validateAppTokenProvider ( config , authResult , actualExpiresIn , expectedRefreshIn ) ;
1190
+
1191
+ const authResult2 = await client . acquireToken ( clientCredentialRequest ) as AuthenticationResult ;
1192
+
1163
1193
// expect the callback to not be called again, because token comes from the cache
1164
1194
expect ( callbackedCalledCount ) . toEqual ( 1 ) ;
1165
-
1195
+
1166
1196
expect ( authResult2 . scopes ) . toEqual ( expectedScopes ) ;
1167
1197
expect ( authResult2 . accessToken ) . toEqual ( accessToken ) ;
1168
- } ) ;
1198
+ }
1169
1199
} ) ;
0 commit comments