@@ -152,64 +152,46 @@ async fn configure(&self, mut provider: Provider) -> Result<Provider> {
152152 . and_then ( |response| check_issuer ( response, & provider) ) ?;
153153
154154 if provider. authorization_url . is_none ( ) {
155- response
156- . get ( "authorization_endpoint" )
157- . and_then ( JsonValue :: as_str)
158- . map ( Url :: parse)
159- . transpose ( ) ?
160- . or_else ( || make_url ( & provider, "/authorize" ) . ok ( ) )
161- . map ( |url| provider. authorization_url . replace ( url) ) ;
155+ provider. authorization_url = match provider. brand . as_str ( ) {
156+ | "github" => "https://api.github.com/authorize" . try_into ( ) . ok ( ) ,
157+ | _ =>
158+ Some ( assert_and_parse_url ( provider. id ( ) , & response, "authorization_endpoint" ) ?) ,
159+ } ;
162160 }
163161
164162 if provider. revocation_url . is_none ( ) {
165- response
166- . get ( "revocation_endpoint" )
167- . and_then ( JsonValue :: as_str)
168- . map ( Url :: parse)
169- . transpose ( ) ?
170- . or_else ( || make_url ( & provider, "/revocation" ) . ok ( ) )
171- . map ( |url| provider. revocation_url . replace ( url) ) ;
163+ provider. revocation_url = match provider. brand . as_str ( ) {
164+ | "github" => "https://api.github.com/revocation"
165+ . try_into ( )
166+ . ok ( ) ,
167+ | _ => Some ( assert_and_parse_url ( provider. id ( ) , & response, "revocation_endpoint" ) ?) ,
168+ } ;
172169 }
173170
174171 if provider. introspection_url . is_none ( ) {
175- response
176- . get ( "introspection_endpoint" )
177- . and_then ( JsonValue :: as_str )
178- . map ( Url :: parse )
179- . transpose ( ) ?
180- . or_else ( || make_url ( & provider , "/introspection" ) . ok ( ) )
181- . map ( |url| provider . introspection_url . replace ( url ) ) ;
172+ provider . introspection_url = match provider . brand . as_str ( ) {
173+ | "github" => "https://api.github.com/introspection"
174+ . try_into ( )
175+ . ok ( ) ,
176+ | _ =>
177+ Some ( assert_and_parse_url ( provider . id ( ) , & response , "introspection_endpoint" ) ? ) ,
178+ } ;
182179 }
183180
184181 if provider. userinfo_url . is_none ( ) {
185- response
186- . get ( "userinfo_endpoint" )
187- . and_then ( JsonValue :: as_str)
188- . map ( Url :: parse)
189- . transpose ( ) ?
190- . or_else ( || match provider. brand . as_str ( ) {
191- | "github" => "https://api.github.com/user" . try_into ( ) . ok ( ) ,
192- | _ => make_url ( & provider, "/userinfo" ) . ok ( ) ,
193- } )
194- . map ( |url| provider. userinfo_url . replace ( url) ) ;
182+ provider. userinfo_url = match provider. brand . as_str ( ) {
183+ | "github" => "https://api.github.com/user" . try_into ( ) . ok ( ) ,
184+ | _ => Some ( assert_and_parse_url ( provider. id ( ) , & response, "userinfo_endpoint" ) ?) ,
185+ } ;
195186 }
196187
197188 if provider. token_url . is_none ( ) {
198- response
199- . get ( "token_endpoint" )
200- . and_then ( JsonValue :: as_str)
201- . map ( Url :: parse)
202- . transpose ( ) ?
203- . or_else ( || {
204- let path = if provider. brand == "github" {
205- "/access_token"
206- } else {
207- "/token"
208- } ;
209-
210- make_url ( & provider, path) . ok ( )
211- } )
212- . map ( |url| provider. token_url . replace ( url) ) ;
189+ provider. token_url = match provider. brand . as_str ( ) {
190+ | "github" => "https://api.github.com/access_token"
191+ . try_into ( )
192+ . ok ( ) ,
193+ | _ => Some ( assert_and_parse_url ( provider. id ( ) , & response, "token_endpoint" ) ?) ,
194+ } ;
213195 }
214196
215197 Ok ( provider)
@@ -235,25 +217,54 @@ pub async fn discover(&self, provider: &Provider) -> Result<JsonValue> {
235217/// Compute the location of the `/.well-known/openid-configuration` based on the
236218/// local provider config.
237219fn discovery_url ( provider : & Provider ) -> Result < Url > {
238- let default_url = provider
239- . discovery
240- . then ( || make_url ( provider, "/.well-known/openid-configuration" ) )
241- . transpose ( ) ?;
242-
243- let Some ( url) = provider
244- . discovery_url
245- . clone ( )
246- . filter ( |_| provider. discovery )
247- . or ( default_url)
248- else {
220+ if !provider. discovery {
249221 return Err ! ( Config (
250222 "discovery_url" ,
251- "Failed to determine URL for discovery of provider {}" ,
223+ "Provider has discovery disabled {}" ,
224+ provider. id( )
225+ ) ) ;
226+ }
227+
228+ if let Some ( url) = & provider. discovery_url {
229+ return Ok ( url. to_owned ( ) ) ;
230+ }
231+
232+ let issuer = provider
233+ . issuer_url
234+ . as_ref ( )
235+ . expect ( "Can we assert this exists at this point?" ) ;
236+
237+ let issuer_path = issuer. path ( ) ;
238+
239+ let ressource_path = ".well-known/openid-configuration" ;
240+
241+ let base_url = if issuer_path. ends_with ( '/' ) {
242+ issuer. to_owned ( )
243+ } else {
244+ let mut url = issuer. to_owned ( ) ;
245+ url. set_path ( ( issuer_path. to_owned ( ) + "/" ) . as_str ( ) ) ;
246+ url
247+ } ;
248+
249+ let Some ( base_path) = provider. base_path . as_ref ( ) else {
250+ return Ok ( base_url. join ( ressource_path) ?) ;
251+ } ;
252+
253+ if base_path. is_empty ( ) {
254+ return Err ! ( Config (
255+ "base_path" ,
256+ "Provider '{}' has an empty base_path. Remove the key or add a value" ,
252257 provider. id( )
253258 ) ) ;
259+ }
260+
261+ let base_path = if base_path. ends_with ( "/" ) {
262+ base_path. to_owned ( )
263+ } else {
264+ base_path. to_owned ( ) + "/"
254265 } ;
255266
256- Ok ( url )
267+ Ok ( base_url . join ( ( base_path + ressource_path ) . as_ref ( ) ) ? )
257268}
258269
259270/// Validate that the locally configured `issuer_url` matches the issuer claimed
@@ -282,20 +293,21 @@ fn check_issuer(
282293 Ok ( response)
283294}
284295
285- /// Generate a full URL for a request to the idp based on the idp's derived
286- /// configuration.
287- fn make_url ( provider : & Provider , path : & str ) -> Result < Url > {
288- let mut suffix = provider. base_path . clone ( ) . unwrap_or_default ( ) ;
289-
290- suffix. push_str ( path) ;
291- let url = provider
292- . issuer_url
293- . as_ref ( )
294- . ok_or_else ( || {
295- let id = & provider. client_id ;
296- err ! ( Config ( "issuer_url" , "Provider {id:?} required field" ) )
297- } ) ?
298- . join ( & suffix) ?;
296+ /// Assert that a url exists in the response and parse it
297+ fn assert_and_parse_url (
298+ provider_id : & str ,
299+ response_map : & serde_json:: Map < String , serde_json:: Value > ,
300+ url_name : & str ,
301+ ) -> Result < Url > {
302+ let Some ( url_value) = response_map. get ( url_name) else {
303+ return Err ( tuwunel_core:: Error :: Err (
304+ format ! (
305+ "Error building oidc provider '{}': {} is missing from openid discovery response" ,
306+ provider_id, url_name,
307+ )
308+ . into ( ) ,
309+ ) ) ;
310+ } ;
299311
300- Ok ( url )
312+ Ok ( url_value . as_str ( ) . unwrap_or_default ( ) . parse ( ) ? )
301313}
0 commit comments