@@ -17,13 +17,20 @@ use actix_web::{
1717use anyhow:: { anyhow, Context } ;
1818use awc:: Client ;
1919use chrono:: Utc ;
20- use openidconnect:: core:: CoreJsonWebKey ;
21- use openidconnect:: IdTokenVerifier ;
20+ use openidconnect:: core:: {
21+ CoreAuthDisplay , CoreAuthPrompt , CoreErrorResponseType , CoreGenderClaim , CoreJsonWebKey ,
22+ CoreJweContentEncryptionAlgorithm , CoreJwsSigningAlgorithm , CoreRevocableToken ,
23+ CoreRevocationErrorResponse , CoreTokenIntrospectionResponse , CoreTokenType ,
24+ } ;
2225use openidconnect:: {
2326 core:: CoreAuthenticationFlow , url:: Url , AsyncHttpClient , Audience , CsrfToken , EndpointMaybeSet ,
2427 EndpointNotSet , EndpointSet , IssuerUrl , Nonce , OAuth2TokenResponse , RedirectUrl , Scope ,
2528 TokenResponse ,
2629} ;
30+ use openidconnect:: {
31+ EmptyExtraTokenFields , IdTokenFields , IdTokenVerifier , StandardErrorResponse ,
32+ StandardTokenResponse ,
33+ } ;
2734use serde:: { Deserialize , Serialize } ;
2835use std:: sync:: Mutex ;
2936
@@ -197,12 +204,12 @@ impl OidcState {
197204 fn get_token_claims (
198205 & self ,
199206 id_token : & OidcToken ,
200- state : & OidcLoginState ,
207+ state : Option < & OidcLoginState > ,
201208 ) -> anyhow:: Result < OidcClaims > {
202209 // Do not refresh the client on every check
203210 let client = & self . client . lock ( ) . expect ( "oidc client" ) . client ;
204211 let verifier = self . config . create_id_token_verifier ( client) ;
205- let nonce_verifier = |nonce : Option < & Nonce > | check_nonce ( nonce, & state. nonce ) ;
212+ let nonce_verifier = |nonce : Option < & Nonce > | check_nonce ( nonce, state) ;
206213 let claims: OidcClaims = id_token
207214 . claims ( & verifier, nonce_verifier)
208215 . with_context ( || format ! ( "Could not verify the ID token: {id_token:?}" ) ) ?
@@ -321,11 +328,7 @@ where
321328
322329 let oidc_state = Arc :: clone ( & self . oidc_state ) ;
323330 Box :: pin ( async move {
324- let response = build_auth_provider_redirect_response (
325- & oidc_state. get_client ( ) . await . client ,
326- & oidc_state. config ,
327- & request,
328- ) ;
331+ let response = build_auth_provider_redirect_response ( & oidc_state, & request) ;
329332 Ok ( request. into_response ( response) )
330333 } )
331334 }
@@ -338,18 +341,12 @@ where
338341
339342 Box :: pin ( async move {
340343 let query_string = request. query_string ( ) ;
341- let client = oidc_state. get_client ( ) . await ;
342- match process_oidc_callback ( & client. client , & oidc_state. config , query_string, & request)
343- . await
344- {
344+ match process_oidc_callback ( & oidc_state, query_string, & request) . await {
345345 Ok ( response) => Ok ( request. into_response ( response) ) ,
346346 Err ( e) => {
347347 log:: error!( "Failed to process OIDC callback with params {query_string}: {e}" ) ;
348- let resp = build_auth_provider_redirect_response (
349- & client. client ,
350- & oidc_state. config ,
351- & request,
352- ) ;
348+ oidc_state. refresh ( ) . await ;
349+ let resp = build_auth_provider_redirect_response ( & oidc_state, & request) ;
353350 Ok ( request. into_response ( resp) )
354351 }
355352 }
@@ -412,8 +409,7 @@ where
412409}
413410
414411async fn process_oidc_callback (
415- oidc_client : & OidcClient ,
416- oidc_config : & OidcConfig ,
412+ oidc_state : & OidcState ,
417413 query_string : & str ,
418414 request : & ServiceRequest ,
419415) -> anyhow:: Result < HttpResponse > {
@@ -434,22 +430,23 @@ async fn process_oidc_callback(
434430 return Err ( anyhow ! ( "Invalid CSRF token: {}" , params. state. secret( ) ) ) ;
435431 }
436432
433+ let client = oidc_state. get_client ( ) . await ;
437434 log:: debug!( "Processing OIDC callback with params: {params:?}. Requesting token..." ) ;
438- let token_response = exchange_code_for_token ( oidc_client , http_client, params) . await ?;
435+ let token_response = exchange_code_for_token ( & client . client , http_client, params) . await ?;
439436 log:: debug!( "Received token response: {token_response:?}" ) ;
440437
441438 let redirect_target = validate_redirect_url ( state. initial_url ) ;
442439 log:: info!( "Redirecting to {redirect_target} after a successful login" ) ;
443440 let mut response = build_redirect_response ( redirect_target) ;
444- set_auth_cookie ( & mut response, & token_response, oidc_client , oidc_config ) ?;
441+ set_auth_cookie ( & mut response, & token_response, oidc_state ) ?;
445442 Ok ( response)
446443}
447444
448445async fn exchange_code_for_token (
449446 oidc_client : & OidcClient ,
450447 http_client : & awc:: Client ,
451448 oidc_callback_params : OidcCallbackParams ,
452- ) -> anyhow:: Result < openidconnect :: core :: CoreTokenResponse > {
449+ ) -> anyhow:: Result < OidcTokenResponse > {
453450 let token_response = oidc_client
454451 . exchange_code ( openidconnect:: AuthorizationCode :: new (
455452 oidc_callback_params. code ,
@@ -461,19 +458,16 @@ async fn exchange_code_for_token(
461458
462459fn set_auth_cookie (
463460 response : & mut HttpResponse ,
464- token_response : & openidconnect:: core:: CoreTokenResponse ,
465- oidc_client : & OidcClient ,
466- oidc_config : & OidcConfig ,
461+ token_response : & OidcTokenResponse ,
462+ oidc_state : & OidcState ,
467463) -> anyhow:: Result < ( ) > {
468464 let access_token = token_response. access_token ( ) ;
469465 log:: trace!( "Received access token: {}" , access_token. secret( ) ) ;
470466 let id_token = token_response
471467 . id_token ( )
472468 . context ( "No ID token found in the token response. You may have specified an oauth2 provider that does not support OIDC." ) ?;
473469
474- let id_token_verifier = oidc_config. create_id_token_verifier ( oidc_client) ;
475- let nonce_verifier = |_nonce : Option < & Nonce > | Ok ( ( ) ) ; // The nonce will be verified in request handling
476- let claims = id_token. claims ( & id_token_verifier, nonce_verifier) ?;
470+ let claims = oidc_state. get_token_claims ( id_token, None ) ?;
477471 let expiration = claims. expiration ( ) ;
478472 let max_age_seconds = expiration. signed_duration_since ( Utc :: now ( ) ) . num_seconds ( ) ;
479473
@@ -499,11 +493,10 @@ fn set_auth_cookie(
499493}
500494
501495fn build_auth_provider_redirect_response (
502- oidc_client : & OidcClient ,
503- oidc_config : & OidcConfig ,
496+ oidc_state : & OidcState ,
504497 request : & ServiceRequest ,
505498) -> HttpResponse {
506- let AuthUrl { url, params } = build_auth_url ( oidc_client , & oidc_config . scopes ) ;
499+ let AuthUrl { url, params } = build_auth_url ( oidc_state ) ;
507500 let state_cookie = create_state_cookie ( request, params) ;
508501 HttpResponse :: TemporaryRedirect ( )
509502 . append_header ( ( "Location" , url. to_string ( ) ) )
@@ -530,7 +523,7 @@ fn get_authenticated_user_info(
530523 . with_context ( || format ! ( "Invalid SQLPage auth cookie: {cookie_value:?}" ) ) ?;
531524
532525 let state = get_state_from_cookie ( request) ?;
533- let claims = oidc_state. get_token_claims ( & id_token, & state) ?;
526+ let claims = oidc_state. get_token_claims ( & id_token, Some ( & state) ) ?;
534527 log:: debug!( "The current user is: {claims:?}" ) ;
535528 Ok ( Some ( claims) )
536529}
@@ -606,14 +599,38 @@ impl std::fmt::Display for AwcWrapperError {
606599 std:: fmt:: Display :: fmt ( & self . 0 , f)
607600 }
608601}
609- type OidcClient = openidconnect:: core:: CoreClient <
602+
603+ type OidcTokenResponse = StandardTokenResponse <
604+ IdTokenFields <
605+ OidcAdditionalClaims ,
606+ EmptyExtraTokenFields ,
607+ CoreGenderClaim ,
608+ CoreJweContentEncryptionAlgorithm ,
609+ CoreJwsSigningAlgorithm ,
610+ > ,
611+ CoreTokenType ,
612+ > ;
613+
614+ type OidcClient = openidconnect:: Client <
615+ OidcAdditionalClaims ,
616+ CoreAuthDisplay ,
617+ CoreGenderClaim ,
618+ CoreJweContentEncryptionAlgorithm ,
619+ CoreJsonWebKey ,
620+ CoreAuthPrompt ,
621+ StandardErrorResponse < CoreErrorResponseType > ,
622+ OidcTokenResponse ,
623+ CoreTokenIntrospectionResponse ,
624+ CoreRevocableToken ,
625+ CoreRevocationErrorResponse ,
610626 EndpointSet ,
611627 EndpointNotSet ,
612628 EndpointNotSet ,
613629 EndpointNotSet ,
614630 EndpointMaybeSet ,
615631 EndpointMaybeSet ,
616632> ;
633+
617634impl std:: error:: Error for AwcWrapperError {
618635 fn source ( & self ) -> Option < & ( dyn std:: error:: Error + ' static ) > {
619636 self . 0 . source ( )
@@ -650,12 +667,9 @@ fn make_oidc_client(
650667 ) ) ?;
651668 }
652669 log:: info!( "OIDC redirect URL for {}: {redirect_url}" , config. client_id) ;
653- let client = openidconnect:: core:: CoreClient :: from_provider_metadata (
654- provider_metadata,
655- client_id,
656- Some ( client_secret) ,
657- )
658- . set_redirect_uri ( redirect_url) ;
670+ let client =
671+ OidcClient :: from_provider_metadata ( provider_metadata, client_id, Some ( client_secret) )
672+ . set_redirect_uri ( redirect_url) ;
659673
660674 Ok ( client)
661675}
@@ -676,10 +690,13 @@ struct AuthUrlParams {
676690 nonce : Nonce ,
677691}
678692
679- fn build_auth_url ( oidc_client : & OidcClient , scopes : & [ Scope ] ) -> AuthUrl {
693+ fn build_auth_url ( oidc_state : & OidcState ) -> AuthUrl {
680694 let nonce_source = Nonce :: new_random ( ) ;
681695 let hashed_nonce = Nonce :: new ( hash_nonce ( & nonce_source) ) ;
682- let ( url, csrf_token, _nonce) = oidc_client
696+ let scopes = & oidc_state. config . scopes ;
697+ let client_lock = oidc_state. client . lock ( ) . unwrap ( ) ;
698+ let ( url, csrf_token, _nonce) = client_lock
699+ . client
683700 . authorize_url (
684701 CoreAuthenticationFlow :: AuthorizationCode ,
685702 CsrfToken :: new_random,
@@ -722,9 +739,15 @@ fn hash_nonce(nonce: &Nonce) -> String {
722739 hash. to_string ( )
723740}
724741
725- fn check_nonce ( id_token_nonce : Option < & Nonce > , state_nonce : & Nonce ) -> Result < ( ) , String > {
742+ fn check_nonce (
743+ id_token_nonce : Option < & Nonce > ,
744+ login_state : Option < & OidcLoginState > ,
745+ ) -> Result < ( ) , String > {
746+ let Some ( state) = login_state else {
747+ return Ok ( ( ) ) ; // No login state, no nonce to check
748+ } ;
726749 match id_token_nonce {
727- Some ( id_token_nonce) => nonce_matches ( id_token_nonce, state_nonce ) ,
750+ Some ( id_token_nonce) => nonce_matches ( id_token_nonce, & state . nonce ) ,
728751 None => Err ( "No nonce found in the ID token" . to_string ( ) ) ,
729752 }
730753}
0 commit comments