@@ -303,7 +303,7 @@ public async Task ExchangeCredentialsTest() {
303
303
304
304
var mockHandler = new Mock < HttpMessageHandler > ( MockBehavior . Strict ) ;
305
305
mockHandler . Protected ( )
306
- . Setup < Task < HttpResponseMessage > > (
306
+ . SetupSequence < Task < HttpResponseMessage > > (
307
307
"SendAsync" ,
308
308
ItExpr . Is < HttpRequestMessage > ( req =>
309
309
req . RequestUri == new Uri ( $ "https://{ config . Credentials . Config . ApiTokenIssuer } /oauth/token") &&
@@ -314,7 +314,15 @@ public async Task ExchangeCredentialsTest() {
314
314
StatusCode = HttpStatusCode . OK ,
315
315
Content = Utils . CreateJsonStringContent ( new OAuth2Client . AccessTokenResponse ( ) {
316
316
AccessToken = "some-token" ,
317
- ExpiresIn = 20000 ,
317
+ ExpiresIn = 86400 ,
318
+ TokenType = "Bearer"
319
+ } ) ,
320
+ } )
321
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
322
+ StatusCode = HttpStatusCode . OK ,
323
+ Content = Utils . CreateJsonStringContent ( new OAuth2Client . AccessTokenResponse ( ) {
324
+ AccessToken = "some-token" ,
325
+ ExpiresIn = 86400 ,
318
326
TokenType = "Bearer"
319
327
} ) ,
320
328
} ) ;
@@ -326,11 +334,16 @@ public async Task ExchangeCredentialsTest() {
326
334
req . Headers . Contains ( "Authorization" ) &&
327
335
req . Headers . Authorization . Equals ( new AuthenticationHeaderValue ( "Bearer" , "some-token" ) ) ) ;
328
336
mockHandler . Protected ( )
329
- . Setup < Task < HttpResponseMessage > > (
337
+ . SetupSequence < Task < HttpResponseMessage > > (
330
338
"SendAsync" ,
331
339
readAuthorizationModelsMockExpression ,
332
340
ItExpr . IsAny < CancellationToken > ( )
333
341
)
342
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
343
+ StatusCode = HttpStatusCode . OK ,
344
+ Content = Utils . CreateJsonStringContent (
345
+ new ReadAuthorizationModelsResponse ( ) { AuthorizationModels = { } } ) ,
346
+ } )
334
347
. ReturnsAsync ( new HttpResponseMessage ( ) {
335
348
StatusCode = HttpStatusCode . OK ,
336
349
Content = Utils . CreateJsonStringContent (
@@ -341,6 +354,7 @@ public async Task ExchangeCredentialsTest() {
341
354
var openFgaApi = new OpenFgaApi ( config , httpClient ) ;
342
355
343
356
var response = await openFgaApi . ReadAuthorizationModels ( null , null ) ;
357
+ var response2 = await openFgaApi . ReadAuthorizationModels ( null , null ) ;
344
358
345
359
mockHandler . Protected ( ) . Verify (
346
360
"SendAsync" ,
@@ -352,7 +366,105 @@ public async Task ExchangeCredentialsTest() {
352
366
) ;
353
367
mockHandler . Protected ( ) . Verify (
354
368
"SendAsync" ,
355
- Times . Exactly ( 1 ) ,
369
+ Times . Exactly ( 2 ) ,
370
+ readAuthorizationModelsMockExpression ,
371
+ ItExpr . IsAny < CancellationToken > ( )
372
+ ) ;
373
+ mockHandler . Protected ( ) . Verify (
374
+ "SendAsync" ,
375
+ Times . Exactly ( 0 ) ,
376
+ ItExpr . Is < HttpRequestMessage > ( req =>
377
+ req . RequestUri == new Uri ( $ "{ config . BasePath } /stores/{ config . StoreId } /check") &&
378
+ req . Method == HttpMethod . Post ) ,
379
+ ItExpr . IsAny < CancellationToken > ( )
380
+ ) ;
381
+ }
382
+
383
+ /// <summary>
384
+ /// Test that a network call is issued to get the token at the first request if client id is provided, and then again before the next call if expired
385
+ /// </summary>
386
+ [ Fact ]
387
+ public async Task ExchangeCredentialsAfterExpiryTest ( ) {
388
+ var config = new Configuration . Configuration ( ) {
389
+ StoreId = _storeId ,
390
+ ApiHost = _host ,
391
+ Credentials = new Credentials ( ) {
392
+ Method = CredentialsMethod . ClientCredentials ,
393
+ Config = new CredentialsConfig ( ) {
394
+ ClientId = "some-id" ,
395
+ ClientSecret = "some-secret" ,
396
+ ApiTokenIssuer = "tokenissuer.fga.example" ,
397
+ ApiAudience = "some-audience" ,
398
+ }
399
+ }
400
+ } ;
401
+
402
+ var mockHandler = new Mock < HttpMessageHandler > ( MockBehavior . Strict ) ;
403
+ mockHandler . Protected ( )
404
+ . SetupSequence < Task < HttpResponseMessage > > (
405
+ "SendAsync" ,
406
+ ItExpr . Is < HttpRequestMessage > ( req =>
407
+ req . RequestUri == new Uri ( $ "https://{ config . Credentials . Config . ApiTokenIssuer } /oauth/token") &&
408
+ req . Method == HttpMethod . Post ) ,
409
+ ItExpr . IsAny < CancellationToken > ( )
410
+ )
411
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
412
+ StatusCode = HttpStatusCode . OK ,
413
+ Content = Utils . CreateJsonStringContent ( new OAuth2Client . AccessTokenResponse ( ) {
414
+ AccessToken = "some-token" ,
415
+ ExpiresIn = 1 ,
416
+ TokenType = "Bearer"
417
+ } ) ,
418
+ } )
419
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
420
+ StatusCode = HttpStatusCode . OK ,
421
+ Content = Utils . CreateJsonStringContent ( new OAuth2Client . AccessTokenResponse ( ) {
422
+ AccessToken = "some-token" ,
423
+ ExpiresIn = 86400 ,
424
+ TokenType = "Bearer"
425
+ } ) ,
426
+ } ) ;
427
+
428
+ var readAuthorizationModelsMockExpression = ItExpr . Is < HttpRequestMessage > ( req =>
429
+ req . RequestUri . ToString ( )
430
+ . StartsWith ( $ "{ config . BasePath } /stores/{ config . StoreId } /authorization-models") &&
431
+ req . Method == HttpMethod . Get &&
432
+ req . Headers . Contains ( "Authorization" ) &&
433
+ req . Headers . Authorization . Equals ( new AuthenticationHeaderValue ( "Bearer" , "some-token" ) ) ) ;
434
+ mockHandler . Protected ( )
435
+ . SetupSequence < Task < HttpResponseMessage > > (
436
+ "SendAsync" ,
437
+ readAuthorizationModelsMockExpression ,
438
+ ItExpr . IsAny < CancellationToken > ( )
439
+ )
440
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
441
+ StatusCode = HttpStatusCode . OK ,
442
+ Content = Utils . CreateJsonStringContent (
443
+ new ReadAuthorizationModelsResponse ( ) { AuthorizationModels = { } } ) ,
444
+ } )
445
+ . ReturnsAsync ( new HttpResponseMessage ( ) {
446
+ StatusCode = HttpStatusCode . OK ,
447
+ Content = Utils . CreateJsonStringContent (
448
+ new ReadAuthorizationModelsResponse ( ) { AuthorizationModels = { } } ) ,
449
+ } ) ;
450
+
451
+ var httpClient = new HttpClient ( mockHandler . Object ) ;
452
+ var openFgaApi = new OpenFgaApi ( config , httpClient ) ;
453
+
454
+ var response = await openFgaApi . ReadAuthorizationModels ( null , null ) ;
455
+ var response2 = await openFgaApi . ReadAuthorizationModels ( null , null ) ;
456
+
457
+ mockHandler . Protected ( ) . Verify (
458
+ "SendAsync" ,
459
+ Times . Exactly ( 2 ) ,
460
+ ItExpr . Is < HttpRequestMessage > ( req =>
461
+ req . RequestUri == new Uri ( $ "https://{ config . Credentials . Config . ApiTokenIssuer } /oauth/token") &&
462
+ req . Method == HttpMethod . Post ) ,
463
+ ItExpr . IsAny < CancellationToken > ( )
464
+ ) ;
465
+ mockHandler . Protected ( ) . Verify (
466
+ "SendAsync" ,
467
+ Times . Exactly ( 2 ) ,
356
468
readAuthorizationModelsMockExpression ,
357
469
ItExpr . IsAny < CancellationToken > ( )
358
470
) ;
0 commit comments