@@ -326,6 +326,101 @@ retrieve_hs256_test() ->
326
326
327
327
ok .
328
328
329
+ retrieve_hs256_with_max_clock_skew_test () ->
330
+ PrivDir = code :priv_dir (oidcc ),
331
+
332
+ {ok , _ } = application :ensure_all_started (oidcc ),
333
+
334
+ {ok , ConfigurationBinary } = file :read_file (PrivDir ++ " /test/fixtures/example-metadata.json" ),
335
+ {ok ,
336
+ # oidcc_provider_configuration {token_endpoint = TokenEndpoint , issuer = Issuer } =
337
+ Configuration } =
338
+ oidcc_provider_configuration :decode_configuration (jose :decode (ConfigurationBinary )),
339
+
340
+ ClientId = <<" client_id" >>,
341
+ ClientSecret = <<" at_least_32_character_client_secret" >>,
342
+ LocalEndpoint = <<" https://my.server/auth" >>,
343
+ AuthCode = <<" 1234567890" >>,
344
+ AccessToken = <<" access_token" >>,
345
+ RefreshToken = <<" refresh_token" >>,
346
+ Claims =
347
+ #{
348
+ <<" iss" >> => Issuer ,
349
+ <<" sub" >> => <<" sub" >>,
350
+ <<" aud" >> => ClientId ,
351
+ <<" nbf" >> => erlang :system_time (second ) + 5 ,
352
+ <<" iat" >> => erlang :system_time (second ) + 5 ,
353
+ <<" exp" >> => erlang :system_time (second ) + 15 ,
354
+ <<" at_hash" >> => <<" hrOQHuo3oE6FR82RIiX1SA" >>
355
+ },
356
+
357
+ Jwk = jose_jwk :from_oct (<<" at_least_32_character_client_secret" >>),
358
+
359
+ Jwt = jose_jwt :from (Claims ),
360
+ Jws = #{<<" alg" >> => <<" HS256" >>},
361
+ {_Jws , Token } = jose_jws :compact (jose_jwt :sign (Jwk , Jws , Jwt )),
362
+
363
+ OtherJwk = jose_jwk :from_file (PrivDir ++ " /test/fixtures/openid-certification-jwks.json" ),
364
+
365
+ TokenData =
366
+ jsx :encode (#{
367
+ <<" access_token" >> => AccessToken ,
368
+ <<" token_type" >> => <<" Bearer" >>,
369
+ <<" id_token" >> => Token ,
370
+ <<" scope" >> => <<" profile openid" >>,
371
+ <<" refresh_token" >> => RefreshToken
372
+ }),
373
+
374
+ ClientContext = oidcc_client_context :from_manual (
375
+ Configuration , OtherJwk , ClientId , ClientSecret
376
+ ),
377
+
378
+ ok = meck :new (httpc , [no_link ]),
379
+ HttpFun =
380
+ fun (
381
+ post ,
382
+ {ReqTokenEndpoint , _Header , " application/x-www-form-urlencoded" , _Body },
383
+ _HttpOpts ,
384
+ _Opts
385
+ ) ->
386
+ TokenEndpoint = ReqTokenEndpoint ,
387
+ {ok , {{" HTTP/1.1" , 200 , " OK" }, [{" content-type" , " application/json" }], TokenData }}
388
+ end ,
389
+ ok = meck :expect (httpc , request , HttpFun ),
390
+
391
+ ? assertMatch (
392
+ {error , token_not_yet_valid },
393
+ oidcc_token :retrieve (
394
+ AuthCode ,
395
+ ClientContext ,
396
+ #{redirect_uri => LocalEndpoint }
397
+ )
398
+ ),
399
+
400
+ application :set_env (oidcc , max_clock_skew , 10 ),
401
+
402
+ ? assertMatch (
403
+ {ok , # oidcc_token {
404
+ id = # oidcc_token_id {token = Token , claims = Claims },
405
+ access = # oidcc_token_access {token = AccessToken },
406
+ refresh = # oidcc_token_refresh {token = RefreshToken },
407
+ scope = [<<" profile" >>, <<" openid" >>]
408
+ }},
409
+ oidcc_token :retrieve (
410
+ AuthCode ,
411
+ ClientContext ,
412
+ #{redirect_uri => LocalEndpoint }
413
+ )
414
+ ),
415
+
416
+ true = meck :validate (httpc ),
417
+
418
+ meck :unload (httpc ),
419
+
420
+ application :unset_env (oidcc , max_clock_skew ),
421
+
422
+ ok .
423
+
329
424
auth_method_client_secret_jwt_test () ->
330
425
PrivDir = code :priv_dir (oidcc ),
331
426
@@ -454,6 +549,105 @@ auth_method_client_secret_jwt_test() ->
454
549
455
550
ok .
456
551
552
+ auth_method_client_secret_jwt_with_max_clock_skew_test () ->
553
+ PrivDir = code :priv_dir (oidcc ),
554
+
555
+ {ok , _ } = application :ensure_all_started (oidcc ),
556
+
557
+ {ok , ConfigurationBinary } = file :read_file (PrivDir ++ " /test/fixtures/example-metadata.json" ),
558
+ {ok , Configuration0 } = oidcc_provider_configuration :decode_configuration (
559
+ jose :decode (ConfigurationBinary )
560
+ ),
561
+
562
+ # oidcc_provider_configuration {token_endpoint = TokenEndpoint , issuer = Issuer } =
563
+ Configuration = Configuration0 # oidcc_provider_configuration {
564
+ token_endpoint_auth_methods_supported = [
565
+ <<" client_secret_jwt" >>, <<" client_secret_basic" >>
566
+ ],
567
+ token_endpoint_auth_signing_alg_values_supported = [<<" HS256" >>]
568
+ },
569
+
570
+ ClientId = <<" client_id" >>,
571
+ ClientSecret = <<" client_secret" >>,
572
+ LocalEndpoint = <<" https://my.server/auth" >>,
573
+ AuthCode = <<" 1234567890" >>,
574
+ AccessToken = <<" access_token" >>,
575
+ RefreshToken = <<" refresh_token" >>,
576
+ Claims =
577
+ #{
578
+ <<" iss" >> => Issuer ,
579
+ <<" sub" >> => <<" sub" >>,
580
+ <<" aud" >> => ClientId ,
581
+ <<" iat" >> => erlang :system_time (second ),
582
+ <<" exp" >> => erlang :system_time (second ) + 10 ,
583
+ <<" at_hash" >> => <<" hrOQHuo3oE6FR82RIiX1SA" >>
584
+ },
585
+
586
+ Jwk = jose_jwk :from_pem_file (PrivDir ++ " /test/fixtures/jwk.pem" ),
587
+
588
+ Jwt = jose_jwt :from (Claims ),
589
+ Jws = #{<<" alg" >> => <<" RS256" >>},
590
+ {_Jws , Token } =
591
+ jose_jws :compact (
592
+ jose_jwt :sign (Jwk , Jws , Jwt )
593
+ ),
594
+
595
+ TokenData =
596
+ jsx :encode (#{
597
+ <<" access_token" >> => AccessToken ,
598
+ <<" token_type" >> => <<" Bearer" >>,
599
+ <<" id_token" >> => Token ,
600
+ <<" scope" >> => <<" profile openid" >>,
601
+ <<" refresh_token" >> => RefreshToken
602
+ }),
603
+
604
+ ClientContext = oidcc_client_context :from_manual (Configuration , Jwk , ClientId , ClientSecret ),
605
+
606
+ ok = meck :new (httpc , [no_link ]),
607
+ HttpFun =
608
+ fun (
609
+ post ,
610
+ {ReqTokenEndpoint , _ , " application/x-www-form-urlencoded" , Body },
611
+ _HttpOpts ,
612
+ _Opts
613
+ ) ->
614
+ TokenEndpoint = ReqTokenEndpoint ,
615
+ BodyMap = maps :from_list (uri_string :dissect_query (Body )),
616
+
617
+ ClientAssertion = maps :get (" client_assertion" , BodyMap ),
618
+
619
+ {true , ClientAssertionJwt , _ } = jose_jwt :verify (
620
+ jose_jwk :from_oct (ClientSecret ), binary :list_to_bin (ClientAssertion )
621
+ ),
622
+
623
+ # jose_jwt {
624
+ fields = #{
625
+ <<" nbf" >> := ClientTokenNbf
626
+ }
627
+ } = ClientAssertionJwt ,
628
+
629
+ ? assert (ClientTokenNbf < os :system_time (seconds ) - 5 ),
630
+
631
+ {ok , {{" HTTP/1.1" , 200 , " OK" }, [{" content-type" , " application/json" }], TokenData }}
632
+ end ,
633
+ ok = meck :expect (httpc , request , HttpFun ),
634
+
635
+ application :set_env (oidcc , max_clock_skew , 10 ),
636
+
637
+ oidcc_token :retrieve (
638
+ AuthCode ,
639
+ ClientContext ,
640
+ #{redirect_uri => LocalEndpoint }
641
+ ),
642
+
643
+ true = meck :validate (httpc ),
644
+
645
+ meck :unload (httpc ),
646
+
647
+ application :unset_env (oidcc , max_clock_skew ),
648
+
649
+ ok .
650
+
457
651
auth_method_private_key_jwt_no_supported_alg_test () ->
458
652
PrivDir = code :priv_dir (oidcc ),
459
653
0 commit comments