1717 */
1818
1919use std:: collections:: HashSet ;
20- use std:: sync:: LazyLock ;
21- use std:: time:: Instant ;
2220
2321use actix_web:: http:: StatusCode ;
2422use actix_web:: {
@@ -28,9 +26,7 @@ use actix_web::{
2826 web,
2927} ;
3028use chrono:: { Duration , TimeDelta } ;
31- use dashmap:: DashMap ;
3229use openid:: Bearer ;
33- use rand:: distributions:: { Alphanumeric , DistString } ;
3430use regex:: Regex ;
3531use serde:: Deserialize ;
3632use ulid:: Ulid ;
@@ -54,31 +50,6 @@ use crate::{
5450 } ,
5551} ;
5652
57- /// In-memory store mapping OAuth state nonces to redirect URLs.
58- /// Entries expire after 10 minutes to prevent stale nonces.
59- const OAUTH_NONCE_TTL : std:: time:: Duration = std:: time:: Duration :: from_secs ( 600 ) ;
60- static OAUTH_NONCE_STORE : LazyLock < DashMap < String , ( String , Instant ) > > =
61- LazyLock :: new ( DashMap :: new) ;
62-
63- /// Generate a cryptographic nonce, store it with the redirect URL, and return the nonce.
64- fn store_oauth_nonce ( redirect : & str ) -> String {
65- // Evict expired entries opportunistically
66- OAUTH_NONCE_STORE . retain ( |_, ( _, created) | created. elapsed ( ) < OAUTH_NONCE_TTL ) ;
67- let nonce = Alphanumeric . sample_string ( & mut rand:: thread_rng ( ) , 32 ) ;
68- OAUTH_NONCE_STORE . insert ( nonce. clone ( ) , ( redirect. to_string ( ) , Instant :: now ( ) ) ) ;
69- nonce
70- }
71-
72- /// Look up and consume a nonce, returning the associated redirect URL if valid.
73- fn consume_oauth_nonce ( nonce : & str ) -> Option < String > {
74- let ( _, ( redirect, created) ) = OAUTH_NONCE_STORE . remove ( nonce) ?;
75- if created. elapsed ( ) < OAUTH_NONCE_TTL {
76- Some ( redirect)
77- } else {
78- None
79- }
80- }
81-
8253/// Struct representing query params returned from oidc provider
8354#[ derive( Deserialize , Debug ) ]
8455pub struct Login {
@@ -113,10 +84,9 @@ pub async fn login(
11384 ( None , None ) => return Ok ( redirect_no_oauth_setup ( query. redirect . clone ( ) ) ) ,
11485 ( None , Some ( client) ) => {
11586 let redirect = query. into_inner ( ) . redirect . to_string ( ) ;
116- let nonce = store_oauth_nonce ( & redirect) ;
11787
11888 let scope = PARSEABLE . options . scope . to_string ( ) ;
119- let mut auth_url: String = client. read ( ) . await . auth_url ( & scope, Some ( nonce ) ) . into ( ) ;
89+ let mut auth_url: String = client. read ( ) . await . auth_url ( & scope, Some ( redirect ) ) . into ( ) ;
12090
12191 auth_url. push_str ( "&access_type=offline&prompt=consent" ) ;
12292 return Ok ( HttpResponse :: TemporaryRedirect ( )
@@ -169,12 +139,11 @@ pub async fn login(
169139 Users . remove_session ( & key) ;
170140 if let Some ( oidc_client) = oidc_client {
171141 let redirect = query. into_inner ( ) . redirect . to_string ( ) ;
172- let nonce = store_oauth_nonce ( & redirect) ;
173142 let scope = PARSEABLE . options . scope . to_string ( ) ;
174143 let mut auth_url: String = oidc_client
175144 . read ( )
176145 . await
177- . auth_url ( & scope, Some ( nonce ) )
146+ . auth_url ( & scope, Some ( redirect ) )
178147 . into ( ) ;
179148 auth_url. push_str ( "&access_type=offline&prompt=consent" ) ;
180149 HttpResponse :: TemporaryRedirect ( )
@@ -339,13 +308,14 @@ fn resolve_roles(
339308 // Inherit roles from a native user with the same email (e.g. tenant owner via OAuth)
340309 if roles. is_empty ( )
341310 && let Some ( email) = & user_info. email
342- && let Some ( native) = metadata. users . iter ( ) . find ( |u| {
343- matches ! ( u. ty, UserType :: Native ( _) )
344- && u. userid ( ) == email. as_str ( )
345- && !u. roles . is_empty ( )
346- } ) {
347- roles. clone_from ( & native. roles ) ;
348- }
311+ && let Some ( native) = metadata. users . iter ( ) . find ( |u| {
312+ matches ! ( u. ty, UserType :: Native ( _) )
313+ && u. userid ( ) == email. as_str ( )
314+ && !u. roles . is_empty ( )
315+ } )
316+ {
317+ roles. clone_from ( & native. roles ) ;
318+ }
349319
350320 roles
351321}
@@ -376,7 +346,6 @@ fn build_login_response(
376346
377347 if is_xhr {
378348 let mut response = HttpResponse :: Ok ( ) ;
379- response. insert_header ( ( actix_web:: http:: header:: CACHE_CONTROL , "no-store" ) ) ;
380349 for cookie in cookies {
381350 response. cookie ( cookie) ;
382351 }
@@ -388,8 +357,7 @@ fn build_login_response(
388357 } else {
389358 let redirect_url = login_query
390359 . state
391- . as_deref ( )
392- . and_then ( consume_oauth_nonce)
360+ . clone ( )
393361 . unwrap_or_else ( || PARSEABLE . options . address . to_string ( ) ) ;
394362
395363 redirect_to_client ( & redirect_url, cookies)
0 commit comments