1616 *
1717 */
1818
19- use std:: collections:: HashSet ;
19+ use std:: { collections:: HashSet , sync :: Arc } ;
2020
2121use actix_web:: {
2222 HttpRequest , HttpResponse ,
2323 cookie:: { Cookie , SameSite , time} ,
2424 http:: header:: { self , ContentType } ,
25- web:: { self , Data } ,
25+ web,
2626} ;
2727use chrono:: { Duration , TimeDelta } ;
2828use http:: StatusCode ;
2929use openid:: { Bearer , Options , Token , Userinfo } ;
3030use regex:: Regex ;
3131use serde:: Deserialize ;
32+ use tokio:: sync:: RwLock ;
3233use ulid:: Ulid ;
3334use url:: Url ;
3435
3536use crate :: {
36- handlers:: { COOKIE_AGE_DAYS , SESSION_COOKIE_NAME , USER_COOKIE_NAME , USER_ID_COOKIE_NAME } ,
37+ handlers:: {
38+ COOKIE_AGE_DAYS , SESSION_COOKIE_NAME , USER_COOKIE_NAME , USER_ID_COOKIE_NAME ,
39+ http:: {
40+ API_BASE_PATH , API_VERSION ,
41+ modal:: { GlobalClient , OIDC_CLIENT } ,
42+ } ,
43+ } ,
3744 oidc:: { Claims , DiscoveredClient } ,
3845 parseable:: PARSEABLE ,
3946 rbac:: {
@@ -73,20 +80,18 @@ pub async fn login(
7380 ) ) ;
7481 }
7582
76- let oidc_client = match req. app_data :: < Data < Option < DiscoveredClient > > > ( ) {
77- Some ( client) => {
78- let c = client. clone ( ) . into_inner ( ) ;
79- c. as_ref ( ) . clone ( )
80- }
83+ let oidc_client = match OIDC_CLIENT . get ( ) {
84+ Some ( c) => c. as_ref ( ) . cloned ( ) ,
8185 None => None ,
8286 } ;
87+
8388 let session_key = extract_session_key_from_req ( & req) . ok ( ) ;
8489 let ( session_key, oidc_client) = match ( session_key, oidc_client) {
8590 ( None , None ) => return Ok ( redirect_no_oauth_setup ( query. redirect . clone ( ) ) ) ,
8691 ( None , Some ( client) ) => {
8792 return Ok ( redirect_to_oidc (
8893 query,
89- & client,
94+ client. read ( ) . await . client ( ) ,
9095 PARSEABLE . options . scope . to_string ( ) . as_str ( ) ,
9196 ) ) ;
9297 }
@@ -131,7 +136,7 @@ pub async fn login(
131136 if let Some ( oidc_client) = oidc_client {
132137 redirect_to_oidc (
133138 query,
134- & oidc_client,
139+ oidc_client. read ( ) . await . client ( ) ,
135140 PARSEABLE . options . scope . to_string ( ) . as_str ( ) ,
136141 )
137142 } else {
@@ -144,13 +149,11 @@ pub async fn login(
144149}
145150
146151pub async fn logout ( req : HttpRequest , query : web:: Query < RedirectAfterLogin > ) -> HttpResponse {
147- let oidc_client = match req. app_data :: < Data < Option < DiscoveredClient > > > ( ) {
148- Some ( client) => {
149- let c = client. clone ( ) . into_inner ( ) ;
150- c. as_ref ( ) . clone ( )
151- }
152+ let oidc_client = match OIDC_CLIENT . get ( ) {
153+ Some ( c) => Some ( c. as_ref ( ) . unwrap ( ) . read ( ) . await . client ( ) . clone ( ) ) ,
152154 None => None ,
153155 } ;
156+
154157 let Some ( session) = extract_session_key_from_req ( & req) . ok ( ) else {
155158 return redirect_to_client ( query. redirect . as_str ( ) , None ) ;
156159 } ;
@@ -170,16 +173,21 @@ pub async fn logout(req: HttpRequest, query: web::Query<RedirectAfterLogin>) ->
170173
171174/// Handler for code callback
172175/// User should be redirected to page they were trying to access with cookie
173- pub async fn reply_login (
174- req : HttpRequest ,
175- login_query : web:: Query < Login > ,
176- ) -> Result < HttpResponse , OIDCError > {
177- let oidc_client = req. app_data :: < Data < Option < DiscoveredClient > > > ( ) . unwrap ( ) ;
178- let oidc_client = oidc_client. clone ( ) . into_inner ( ) . as_ref ( ) . clone ( ) . unwrap ( ) ;
179- let Ok ( ( mut claims, user_info, bearer) ) : Result < ( Claims , Userinfo , Bearer ) , anyhow:: Error > =
180- request_token ( oidc_client, & login_query) . await
181- else {
182- return Ok ( HttpResponse :: Unauthorized ( ) . finish ( ) ) ;
176+ pub async fn reply_login ( login_query : web:: Query < Login > ) -> Result < HttpResponse , OIDCError > {
177+ let oidc_client = if let Some ( oidc_client) = OIDC_CLIENT . get ( )
178+ && let Some ( oidc_client) = oidc_client
179+ {
180+ oidc_client
181+ } else {
182+ return Err ( OIDCError :: Unauthorized ) ;
183+ } ;
184+
185+ let ( mut claims, user_info, bearer) = match request_token ( oidc_client, & login_query) . await {
186+ Ok ( v) => v,
187+ Err ( e) => {
188+ tracing:: error!( "reply_login call failed- {e}" ) ;
189+ return Ok ( HttpResponse :: Unauthorized ( ) . finish ( ) ) ;
190+ }
183191 } ;
184192 let username = user_info
185193 . name
@@ -351,6 +359,7 @@ pub fn redirect_to_client(
351359 response. cookie ( cookie) ;
352360 }
353361 response. insert_header ( ( header:: CACHE_CONTROL , "no-store" ) ) ;
362+
354363 response. finish ( )
355364}
356365
@@ -387,19 +396,46 @@ pub fn cookie_userid(user_id: &str) -> Cookie<'static> {
387396}
388397
389398pub async fn request_token (
390- oidc_client : DiscoveredClient ,
399+ oidc_client : & Arc < RwLock < GlobalClient > > ,
391400 login_query : & Login ,
392401) -> anyhow:: Result < ( Claims , Userinfo , Bearer ) > {
393- let mut token: Token < Claims > = oidc_client. request_token ( & login_query. code ) . await ?. into ( ) ;
394- let Some ( id_token) = token. id_token . as_mut ( ) else {
402+ let old_client = oidc_client. read ( ) . await . client ( ) . clone ( ) ;
403+ let mut token: Token < Claims > = old_client. request_token ( & login_query. code ) . await ?. into ( ) ;
404+
405+ let id_token = if let Some ( token) = token. id_token . as_mut ( ) {
406+ token
407+ } else {
395408 return Err ( anyhow:: anyhow!( "No id_token provided" ) ) ;
396409 } ;
397410
398- oidc_client. decode_token ( id_token) ?;
399- oidc_client. validate_token ( id_token, None , None ) ?;
411+ if let Err ( e) = old_client. decode_token ( id_token) {
412+ tracing:: error!( "error while decoding the id_token- {e}" ) ;
413+ let new_client = PARSEABLE
414+ . options
415+ . openid ( )
416+ . unwrap ( )
417+ . connect ( & format ! ( "{API_BASE_PATH}/{API_VERSION}/o/code" ) )
418+ . await ?;
419+
420+ // Reuse the already-obtained token, just decode with new client's JWKS
421+ new_client. decode_token ( id_token) ?;
422+ new_client. validate_token ( id_token, None , None ) ?;
423+ let claims = id_token. payload ( ) . expect ( "payload is decoded" ) . clone ( ) ;
424+
425+ let userinfo = new_client. request_userinfo ( & token) . await ?;
426+ let bearer = token. bearer ;
427+
428+ // replace old client with new one
429+ drop ( old_client) ;
430+
431+ oidc_client. write ( ) . await . set ( new_client) ;
432+ return Ok ( ( claims, userinfo, bearer) ) ;
433+ }
434+
435+ old_client. validate_token ( id_token, None , None ) ?;
400436 let claims = id_token. payload ( ) . expect ( "payload is decoded" ) . clone ( ) ;
401437
402- let userinfo = oidc_client . request_userinfo ( & token) . await ?;
438+ let userinfo = old_client . request_userinfo ( & token) . await ?;
403439 let bearer = token. bearer ;
404440 Ok ( ( claims, userinfo, bearer) )
405441}
0 commit comments