@@ -3,7 +3,7 @@ use crate::{
33 InnerConfig , LogoutOptions , OAuth2Error ,
44 client:: { Client , LoginContext , expires} ,
55 } ,
6- config:: openid,
6+ config:: openid:: { self , MetadataSource , MetadataUrls } ,
77 context:: { Authentication , OAuth2Context } ,
88} ;
99use async_trait:: async_trait;
@@ -36,7 +36,7 @@ const DEFAULT_POST_LOGOUT_DIRECT_NAME: &str = "post_logout_redirect_uri";
3636/// An OpenID Connect based client implementation
3737#[ derive( Clone , Debug ) ]
3838pub struct OpenIdClient {
39- /// The http clietn
39+ /// The http client
4040 http_client : openidconnect:: reqwest:: Client ,
4141 /// The client
4242 client : ExtendedClient ,
@@ -96,21 +96,47 @@ impl Client for OpenIdClient {
9696 ) ;
9797
9898 async fn from_config ( config : Self :: Configuration ) -> Result < Self , OAuth2Error > {
99- match (
100- & config. jwks_url ,
101- & config. auth_url ,
102- & config. token_url ,
103- & config. user_info_url ,
104- ) {
105- ( Some ( _) , Some ( _) , Some ( _) , Some ( _) ) => {
106- log:: debug!( "Constructing client from feeded urls" ) ;
107- Self :: from_urls ( config) . await
99+ let openid:: Config {
100+ client_id,
101+ issuer_url,
102+ metadata_source,
103+ end_session_url,
104+ after_logout_url,
105+ post_logout_redirect_name,
106+ additional_trusted_audiences,
107+ require_issuer_match,
108+ } = config;
109+
110+ let http_client = openidconnect:: reqwest:: ClientBuilder :: new ( )
111+ // Following redirects opens the client up to SSRF vulnerabilities.
112+ // .redirect(openidconnect::reqwest::redirect::Policy::none())
113+ . build ( )
114+ . map_err ( |err| {
115+ OAuth2Error :: Configuration ( format ! ( "Failed to build HTTP client: {err}" ) )
116+ } ) ?;
117+
118+ let issuer = IssuerUrl :: new ( issuer_url)
119+ . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid issuer URL: {err}" ) ) ) ?;
120+
121+ let ( client, end_session_url) = match metadata_source {
122+ MetadataSource :: Discovery => {
123+ Self :: build_client_from_discovery ( & http_client, issuer, client_id, end_session_url)
124+ . await ?
108125 }
109- _ => {
110- log :: debug! ( "Constructing client from provider metadata" ) ;
111- Self :: from_metadata ( config ) . await
126+ MetadataSource :: Manual ( urls ) => {
127+ Self :: build_client_from_urls ( & http_client , issuer , client_id , end_session_url , urls )
128+ . await ?
112129 }
113- }
130+ } ;
131+ Ok ( Self {
132+ http_client,
133+ client,
134+ end_session_url,
135+ after_logout_url,
136+ post_logout_redirect_name,
137+ additional_trusted_audiences,
138+ require_issuer_match,
139+ } )
114140 }
115141
116142 fn set_redirect_uri ( mut self , url : Url ) -> Self {
@@ -284,29 +310,13 @@ impl OpenIdClient {
284310 window ( ) . location ( ) . href ( ) . ok ( )
285311 }
286312 }
287- async fn from_metadata ( config : openid:: Config ) -> Result < Self , OAuth2Error > {
288- let openid:: Config {
289- client_id,
290- issuer_url,
291- end_session_url,
292- after_logout_url,
293- post_logout_redirect_name,
294- additional_trusted_audiences,
295- require_issuer_match,
296- ..
297- } = config;
298-
299- let http_client = openidconnect:: reqwest:: ClientBuilder :: new ( )
300- // we can't control redirect here, as we're using WASM request, which basically is the browser
301- . build ( )
302- . map_err ( |err| {
303- OAuth2Error :: Configuration ( format ! ( "Failed to build HTTP client: {err}" ) )
304- } ) ?;
305-
306- let issuer = IssuerUrl :: new ( issuer_url)
307- . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid issuer URL: {err}" ) ) ) ?;
308-
309- let metadata = ExtendedProviderMetadata :: discover_async ( issuer, & http_client)
313+ async fn build_client_from_discovery (
314+ http_client : & openidconnect:: reqwest:: Client ,
315+ issuer : IssuerUrl ,
316+ client_id : String ,
317+ end_session_url : Option < String > ,
318+ ) -> Result < ( ExtendedClient , Option < Url > ) , OAuth2Error > {
319+ let metadata = ExtendedProviderMetadata :: discover_async ( issuer, http_client)
310320 . await
311321 . map_err ( |err| {
312322 OAuth2Error :: Configuration ( format ! ( "Failed to discover client: {err}" ) )
@@ -328,7 +338,6 @@ impl OpenIdClient {
328338 OAuth2Error :: Configuration ( "Provider missing required auth info endpoint" . into ( ) )
329339 } ) ?
330340 . clone ( ) ;
331-
332341 let end_session_url = end_session_url
333342 . map ( |url| Url :: parse ( & url) )
334343 . transpose ( )
@@ -337,92 +346,38 @@ impl OpenIdClient {
337346 } ) ?
338347 . or_else ( || metadata. additional_metadata ( ) . end_session_endpoint . clone ( ) ) ;
339348
340- let client = CoreClient :: from_provider_metadata ( metadata, ClientId :: new ( client_id) , None )
341- . set_auth_uri ( auth_uri)
342- . set_token_uri ( token_uri)
343- . set_user_info_url ( user_info_uri) ;
344- Ok ( Self {
345- http_client,
346- client,
349+ Ok ( (
350+ CoreClient :: from_provider_metadata ( metadata, ClientId :: new ( client_id) , None )
351+ . set_auth_uri ( auth_uri)
352+ . set_token_uri ( token_uri)
353+ . set_user_info_url ( user_info_uri) ,
347354 end_session_url,
348- after_logout_url,
349- post_logout_redirect_name,
350- additional_trusted_audiences,
351- require_issuer_match,
352- } )
355+ ) )
353356 }
354357
355- async fn from_urls ( config : openid:: Config ) -> Result < Self , OAuth2Error > {
356- let openid:: Config {
357- client_id,
358- issuer_url,
359- end_session_url,
360- after_logout_url,
361- post_logout_redirect_name,
362- additional_trusted_audiences,
363- require_issuer_match,
364- jwks_url,
365- auth_url,
366- token_url,
367- user_info_url,
368- } = config;
369-
370- let http_client = openidconnect:: reqwest:: ClientBuilder :: new ( )
371- // Following redirects opens the client up to SSRF vulnerabilities.
372- // .redirect(openidconnect::reqwest::redirect::Policy::none())
373- . build ( )
374- . map_err ( |err| {
375- OAuth2Error :: Configuration ( format ! ( "Failed to build HTTP client: {err}" ) )
376- } ) ?;
377-
378- let issuer = IssuerUrl :: new ( issuer_url)
379- . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid issuer URL: {err}" ) ) ) ?;
380-
381- let auth_uri = auth_url
382- . map ( AuthUrl :: new)
383- . transpose ( )
384- . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid auth URL: {err}" ) ) ) ?
385- . ok_or_else ( || {
386- OAuth2Error :: Configuration (
387- "Cannot build Auth2 client without metadata: missing auth url" . to_string ( ) ,
388- )
389- } ) ?;
358+ async fn build_client_from_urls (
359+ http_client : & openidconnect:: reqwest:: Client ,
360+ issuer : IssuerUrl ,
361+ client_id : String ,
362+ end_session_url : Option < String > ,
363+ urls : MetadataUrls ,
364+ ) -> Result < ( ExtendedClient , Option < Url > ) , OAuth2Error > {
365+ let auth_uri = AuthUrl :: new ( urls. auth )
366+ . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid auth URL: {err}" ) ) ) ?;
390367
391- let token_uri = token_url
392- . map ( TokenUrl :: new)
393- . transpose ( )
394- . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid token URL: {err}" ) ) ) ?
395- . ok_or_else ( || {
396- OAuth2Error :: Configuration (
397- "Cannot build Auth2 client without metadata: missing token url" . to_string ( ) ,
398- )
399- } ) ?;
368+ let token_uri = TokenUrl :: new ( urls. token )
369+ . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid token URL: {err}" ) ) ) ?;
400370
401- let jwks_uri = jwks_url
402- . map ( JsonWebKeySetUrl :: new)
403- . transpose ( )
404- . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid jwks URL: {err}" ) ) ) ?
405- . ok_or_else ( || {
406- OAuth2Error :: Configuration (
407- "Cannot build Auth2 client without metadata: missing jwks url" . to_string ( ) ,
408- )
409- } ) ?;
371+ let jwks_uri = JsonWebKeySetUrl :: new ( urls. jwks )
372+ . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "invalid jwks URL: {err}" ) ) ) ?;
410373
411- let jwks = JsonWebKeySet :: fetch_async ( & jwks_uri, & http_client)
374+ let jwks = JsonWebKeySet :: fetch_async ( & jwks_uri, http_client)
412375 . await
413376 . map_err ( |err| OAuth2Error :: Configuration ( format ! ( "Could not fetch jwks: {err}" ) ) ) ?;
414377
415- let user_info_url = user_info_url
416- . map ( UserInfoUrl :: new)
417- . transpose ( )
418- . map_err ( |err| {
419- OAuth2Error :: Configuration ( format ! ( "Unable to parse user_info_url: {err}" ) )
420- } ) ?
421- . ok_or_else ( || {
422- OAuth2Error :: Configuration (
423- "Cannot build Auth2 client without metadata: missing user_info url" . to_string ( ) ,
424- )
425- } ) ?;
378+ let user_info_uri = UserInfoUrl :: new ( urls. user_info ) . map_err ( |err| {
379+ OAuth2Error :: Configuration ( format ! ( "Unable to parse user_info_url: {err}" ) )
380+ } ) ?;
426381
427382 let end_session_url = end_session_url
428383 . map ( |url| Url :: parse ( & url) )
@@ -431,19 +386,12 @@ impl OpenIdClient {
431386 OAuth2Error :: Configuration ( format ! ( "Unable to parse end_session_url: {err}" ) )
432387 } ) ?;
433388
434- let client = CoreClient :: new ( ClientId :: new ( client_id) , issuer, jwks)
435- . set_auth_uri ( auth_uri)
436- . set_token_uri ( token_uri)
437- . set_user_info_url ( user_info_url) ;
438-
439- Ok ( Self {
440- http_client,
441- client,
389+ Ok ( (
390+ CoreClient :: new ( ClientId :: new ( client_id) , issuer, jwks)
391+ . set_auth_uri ( auth_uri)
392+ . set_token_uri ( token_uri)
393+ . set_user_info_url ( user_info_uri) ,
442394 end_session_url,
443- after_logout_url,
444- post_logout_redirect_name,
445- additional_trusted_audiences,
446- require_issuer_match,
447- } )
395+ ) )
448396 }
449397}
0 commit comments