1 #![allow(clippy::expect_fun_call)]
2 extern crate env_logger;
3 extern crate http;
4 #[macro_use]
5 extern crate log;
6 extern crate openidconnect;
7 #[macro_use]
8 extern crate pretty_assertions;
9 extern crate reqwest_ as reqwest;
10 extern crate url;
11
12 use std::collections::HashMap;
13
14 use http::header::LOCATION;
15 use http::method::Method;
16 use reqwest::{blocking::Client, redirect::Policy};
17 use url::Url;
18
19 use openidconnect::core::{
20 CoreClient, CoreClientAuthMethod, CoreClientRegistrationRequest,
21 CoreClientRegistrationResponse, CoreIdToken, CoreIdTokenClaims, CoreIdTokenVerifier,
22 CoreJsonWebKeySet, CoreJwsSigningAlgorithm, CoreProviderMetadata, CoreResponseType,
23 CoreUserInfoClaims,
24 };
25 use openidconnect::Nonce;
26 use openidconnect::{
27 AccessToken, AuthType, AuthenticationFlow, AuthorizationCode, ClaimsVerificationError,
28 CsrfToken, OAuth2TokenResponse, RequestTokenError, Scope, SignatureVerificationError,
29 UserInfoError,
30 };
31
32 #[macro_use]
33 mod rp_common;
34
35 use rp_common::{
36 get_provider_metadata, http_client, init_log, issuer_url, register_client, PanicIfFail,
37 };
38
39 struct TestState {
40 access_token: Option<AccessToken>,
41 authorization_code: Option<AuthorizationCode>,
42 client: CoreClient,
43 id_token: Option<CoreIdToken>,
44 nonce: Option<Nonce>,
45 provider_metadata: CoreProviderMetadata,
46 registration_response: CoreClientRegistrationResponse,
47 }
48 impl TestState {
init<F>(test_id: &'static str, reg_request_fn: F) -> Self where F: FnOnce(CoreClientRegistrationRequest) -> CoreClientRegistrationRequest,49 pub fn init<F>(test_id: &'static str, reg_request_fn: F) -> Self
50 where
51 F: FnOnce(CoreClientRegistrationRequest) -> CoreClientRegistrationRequest,
52 {
53 init_log(test_id);
54
55 let _issuer_url = issuer_url(test_id);
56 let provider_metadata = get_provider_metadata(test_id);
57 let registration_response = register_client(&provider_metadata, reg_request_fn);
58
59 let redirect_uri = registration_response.redirect_uris()[0].clone();
60 let client: CoreClient = CoreClient::from_provider_metadata(
61 provider_metadata.clone(),
62 registration_response.client_id().to_owned(),
63 registration_response.client_secret().cloned(),
64 )
65 .set_redirect_uri(redirect_uri);
66
67 TestState {
68 access_token: None,
69 authorization_code: None,
70 client,
71 id_token: None,
72 nonce: None,
73 provider_metadata,
74 registration_response,
75 }
76 }
77
access_token(&self) -> &AccessToken78 pub fn access_token(&self) -> &AccessToken {
79 self.access_token.as_ref().expect("no access_token")
80 }
81
authorize(mut self, scopes: &[Scope]) -> Self82 pub fn authorize(mut self, scopes: &[Scope]) -> Self {
83 let (authorization_code, nonce) = {
84 let mut authorization_request = self.client.authorize_url(
85 AuthenticationFlow::AuthorizationCode::<CoreResponseType>,
86 CsrfToken::new_random,
87 Nonce::new_random,
88 );
89 authorization_request =
90 scopes
91 .iter()
92 .fold(authorization_request, |mut authorization_request, scope| {
93 authorization_request = authorization_request.add_scope(scope.clone());
94 authorization_request
95 });
96 let (url, state, nonce) = authorization_request.url();
97 log_debug!("Authorize URL: {:?}", url);
98
99 let http_client = Client::builder().redirect(Policy::none()).build().unwrap();
100 let redirect_response = http_client
101 .execute(
102 http_client
103 .request(Method::GET, url.as_str())
104 .build()
105 .unwrap(),
106 )
107 .unwrap();
108 assert!(redirect_response.status().is_redirection());
109 let redirected_url = Url::parse(
110 redirect_response
111 .headers()
112 .get(LOCATION)
113 .unwrap()
114 .to_str()
115 .unwrap(),
116 )
117 .unwrap();
118
119 log_debug!("Authorization Server redirected to: {:?}", redirected_url);
120
121 let mut query_params = HashMap::new();
122 redirected_url.query_pairs().for_each(|(key, value)| {
123 query_params.insert(key, value);
124 });
125 log_debug!(
126 "Authorization Server returned query params: {:?}",
127 query_params
128 );
129
130 assert_eq!(
131 self.provider_metadata.issuer().as_str(),
132 query_params.get("iss").unwrap()
133 );
134 assert_eq!(state.secret(), query_params.get("state").unwrap());
135
136 log_info!("Successfully received authentication response from Authorization Server");
137
138 let authorization_code =
139 AuthorizationCode::new(query_params.get("code").unwrap().to_string());
140 log_debug!(
141 "Authorization Server returned authorization code: {}",
142 authorization_code.secret()
143 );
144
145 (authorization_code, nonce)
146 };
147
148 self.authorization_code = Some(authorization_code);
149 self.nonce = Some(nonce);
150
151 self
152 }
153
exchange_code(mut self) -> Self154 pub fn exchange_code(mut self) -> Self {
155 let token_response = self
156 .client
157 .exchange_code(
158 self.authorization_code
159 .take()
160 .expect("no authorization_code"),
161 )
162 .request(http_client)
163 .panic_if_fail("failed to exchange authorization code for token");
164 log_debug!(
165 "Authorization Server returned token response: {:?}",
166 token_response
167 );
168
169 self.access_token = Some(token_response.access_token().clone());
170
171 let id_token = (*token_response
172 .extra_fields()
173 .id_token()
174 .expect("no id_token"))
175 .clone();
176 self.id_token = Some(id_token);
177
178 self
179 }
180
id_token(&self) -> &CoreIdToken181 pub fn id_token(&self) -> &CoreIdToken {
182 self.id_token.as_ref().expect("no id_token")
183 }
184
id_token_verifier(&self, jwks: CoreJsonWebKeySet) -> CoreIdTokenVerifier185 pub fn id_token_verifier(&self, jwks: CoreJsonWebKeySet) -> CoreIdTokenVerifier {
186 CoreIdTokenVerifier::new_confidential_client(
187 self.registration_response.client_id().clone(),
188 self.registration_response
189 .client_secret()
190 .expect("no client_secret")
191 .clone(),
192 self.provider_metadata.issuer().clone(),
193 jwks,
194 )
195 }
196
id_token_claims(&self) -> &CoreIdTokenClaims197 pub fn id_token_claims(&self) -> &CoreIdTokenClaims {
198 let verifier = self.id_token_verifier(self.jwks());
199 self.id_token()
200 .claims(&verifier, self.nonce.as_ref().expect("no nonce"))
201 .panic_if_fail("failed to validate claims")
202 }
203
id_token_claims_failure(&self) -> ClaimsVerificationError204 pub fn id_token_claims_failure(&self) -> ClaimsVerificationError {
205 let verifier = self.id_token_verifier(self.jwks());
206 self.id_token()
207 .claims(&verifier, self.nonce.as_ref().expect("no nonce"))
208 .expect_err("claims verification succeeded but was expected to fail")
209 }
210
jwks(&self) -> CoreJsonWebKeySet211 pub fn jwks(&self) -> CoreJsonWebKeySet {
212 CoreJsonWebKeySet::fetch(self.provider_metadata.jwks_uri(), http_client)
213 .panic_if_fail("failed to fetch JWK set")
214 }
215
set_auth_type(mut self, auth_type: AuthType) -> Self216 pub fn set_auth_type(mut self, auth_type: AuthType) -> Self {
217 self.client = self.client.set_auth_type(auth_type);
218 self
219 }
220
user_info_claims(&self) -> CoreUserInfoClaims221 pub fn user_info_claims(&self) -> CoreUserInfoClaims {
222 self.client
223 .user_info(
224 self.access_token().to_owned(),
225 Some(self.id_token_claims().subject().clone()),
226 )
227 .unwrap()
228 .require_signed_response(false)
229 .request(http_client)
230 .panic_if_fail("failed to get UserInfo")
231 }
232
user_info_claims_failure( &self, ) -> UserInfoError<openidconnect::reqwest::HttpClientError>233 pub fn user_info_claims_failure(
234 &self,
235 ) -> UserInfoError<openidconnect::reqwest::HttpClientError> {
236 let user_info_result: Result<CoreUserInfoClaims, _> = self
237 .client
238 .user_info(
239 self.access_token().to_owned(),
240 Some(self.id_token_claims().subject().clone()),
241 )
242 .unwrap()
243 .require_signed_response(false)
244 .request(http_client);
245 match user_info_result {
246 Err(err) => err,
247 _ => panic!("claims verification succeeded but was expected to fail"),
248 }
249 }
250 }
251
252 #[test]
253 #[ignore]
rp_response_type_code()254 fn rp_response_type_code() {
255 let test_state = TestState::init("rp-response_type-code", |reg| reg).authorize(&[]);
256 assert!(
257 test_state
258 .authorization_code
259 .expect("no authorization_code")
260 .secret()
261 != ""
262 );
263 log_info!("SUCCESS");
264 }
265
266 #[test]
267 #[ignore]
rp_scope_userinfo_claims()268 fn rp_scope_userinfo_claims() {
269 let user_info_scopes = vec!["profile", "email", "address", "phone"]
270 .iter()
271 .map(|scope| Scope::new((*scope).to_string()))
272 .collect::<Vec<_>>();
273 let test_state = TestState::init("rp-scope-userinfo-claims", |reg| reg)
274 .authorize(&user_info_scopes)
275 .exchange_code();
276 let id_token_claims = test_state.id_token_claims();
277 log_debug!("ID token: {:?}", id_token_claims);
278
279 let user_info_claims = test_state.user_info_claims();
280 log_debug!("UserInfo response: {:?}", user_info_claims);
281
282 assert_eq!(id_token_claims.subject(), user_info_claims.subject());
283 assert!(!user_info_claims
284 .email()
285 .expect("no email returned by UserInfo endpoint")
286 .is_empty());
287 assert!(!user_info_claims
288 .address()
289 .expect("no address returned by UserInfo endpoint")
290 .street_address
291 .as_ref()
292 .expect("no street address returned by UserInfo endpoint")
293 .is_empty());
294 assert!(!user_info_claims
295 .phone_number()
296 .expect("no phone_number returned by UserInfo endpoint")
297 .is_empty());
298
299 log_info!("SUCCESS");
300 }
301
302 #[test]
303 #[ignore]
rp_nonce_invalid()304 fn rp_nonce_invalid() {
305 let test_state = TestState::init("rp-nonce-invalid", |reg| reg)
306 .authorize(&[])
307 .exchange_code();
308
309 match test_state.id_token_claims_failure() {
310 ClaimsVerificationError::InvalidNonce(_) => {
311 log_error!("ID token contains invalid nonce (expected result)")
312 }
313 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
314 }
315
316 log_info!("SUCCESS");
317 }
318
319 #[test]
320 #[ignore]
rp_token_endpoint_client_secret_basic()321 fn rp_token_endpoint_client_secret_basic() {
322 let test_state = TestState::init("rp-token_endpoint-client_secret_basic", |reg| {
323 reg.set_token_endpoint_auth_method(Some(CoreClientAuthMethod::ClientSecretBasic))
324 })
325 .set_auth_type(AuthType::BasicAuth)
326 .authorize(&[])
327 .exchange_code();
328
329 let id_token_claims = test_state.id_token_claims();
330 log_debug!("ID token: {:?}", id_token_claims);
331
332 log_info!("SUCCESS");
333 }
334
335 #[test]
336 #[ignore]
rp_token_endpoint_client_secret_post()337 fn rp_token_endpoint_client_secret_post() {
338 let test_state = TestState::init("rp-token_endpoint-client_secret_post", |reg| {
339 reg.set_token_endpoint_auth_method(Some(CoreClientAuthMethod::ClientSecretPost))
340 })
341 .set_auth_type(AuthType::RequestBody)
342 .authorize(&[])
343 .exchange_code();
344
345 let id_token_claims = test_state.id_token_claims();
346 log_debug!("ID token: {:?}", id_token_claims);
347
348 log_info!("SUCCESS");
349 }
350
351 #[test]
352 #[ignore]
rp_id_token_kid_absent_single_jwks()353 fn rp_id_token_kid_absent_single_jwks() {
354 let test_state = TestState::init("rp-id_token-kid-absent-single-jwks", |reg| reg)
355 .authorize(&[])
356 .exchange_code();
357
358 let id_token_claims = test_state.id_token_claims();
359 log_debug!("ID token: {:?}", id_token_claims);
360
361 log_info!("SUCCESS");
362 }
363
364 #[test]
365 #[ignore]
rp_id_token_iat()366 fn rp_id_token_iat() {
367 let mut test_state = TestState::init("rp-id_token-iat", |reg| reg).authorize(&[]);
368
369 let token_response = test_state
370 .client
371 .exchange_code(
372 test_state
373 .authorization_code
374 .take()
375 .expect("no authorization_code"),
376 )
377 .request(http_client);
378
379 match token_response {
380 Err(RequestTokenError::Parse(_, _)) => {
381 log_error!("ID token failed to parse without `iat` claim (expected result)")
382 }
383 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
384 }
385 log_info!("SUCCESS");
386 }
387
388 #[test]
389 #[ignore]
rp_id_token_aud()390 fn rp_id_token_aud() {
391 let test_state = TestState::init("rp-id_token-aud", |reg| reg)
392 .authorize(&[])
393 .exchange_code();
394
395 match test_state.id_token_claims_failure() {
396 ClaimsVerificationError::InvalidAudience(_) => {
397 log_error!("ID token has invalid audience (expected result)")
398 }
399 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
400 }
401
402 log_info!("SUCCESS");
403 }
404
405 #[test]
406 #[ignore]
rp_id_token_kid_absent_multiple_jwks()407 fn rp_id_token_kid_absent_multiple_jwks() {
408 let test_state = TestState::init("rp-id_token-kid-absent-multiple-jwks", |reg| reg)
409 .authorize(&[])
410 .exchange_code();
411
412 match test_state.id_token_claims_failure() {
413 ClaimsVerificationError::SignatureVerification(
414 SignatureVerificationError::AmbiguousKeyId(_),
415 ) => log_error!("ID token has ambiguous key identification without KID (expected result)"),
416 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
417 }
418
419 log_info!("SUCCESS");
420 }
421
422 #[test]
423 #[ignore]
rp_id_token_sig_none()424 fn rp_id_token_sig_none() {
425 let test_state = TestState::init("rp-id_token-sig-none", |reg| reg)
426 .authorize(&[])
427 .exchange_code();
428
429 let verifier = test_state
430 .id_token_verifier(test_state.jwks())
431 .insecure_disable_signature_check();
432
433 let id_token_claims = test_state
434 .id_token()
435 .claims(&verifier, test_state.nonce.as_ref().expect("no nonce"))
436 .panic_if_fail("failed to validate claims");
437 log_debug!("ID token: {:?}", id_token_claims);
438
439 log_info!("SUCCESS");
440 }
441
442 #[test]
443 #[ignore]
rp_id_token_sig_rs256()444 fn rp_id_token_sig_rs256() {
445 let test_state = TestState::init("rp-id_token-sig-rs256", |reg| reg)
446 .authorize(&[])
447 .exchange_code();
448
449 let id_token_claims = test_state.id_token_claims();
450 log_debug!("ID token: {:?}", id_token_claims);
451
452 log_info!("SUCCESS");
453 }
454
455 #[test]
456 #[ignore]
rp_id_token_sig_hs256()457 fn rp_id_token_sig_hs256() {
458 let test_state = TestState::init("rp-id_token-sig-hs256", |reg| reg)
459 .authorize(&[])
460 .exchange_code();
461
462 let verifier = test_state
463 .id_token_verifier(test_state.jwks())
464 .set_allowed_algs(vec![CoreJwsSigningAlgorithm::HmacSha256]);
465 let id_token_claims = test_state
466 .id_token()
467 .claims(&verifier, test_state.nonce.as_ref().expect("no nonce"))
468 .panic_if_fail("failed to validate claims");
469 log_debug!("ID token: {:?}", id_token_claims);
470
471 log_info!("SUCCESS");
472 }
473
474 #[test]
475 #[ignore]
rp_id_token_sub()476 fn rp_id_token_sub() {
477 let mut test_state = TestState::init("rp-id_token-sub", |reg| reg).authorize(&[]);
478
479 let token_response = test_state
480 .client
481 .exchange_code(
482 test_state
483 .authorization_code
484 .take()
485 .expect("no authorization_code"),
486 )
487 .request(http_client);
488
489 match token_response {
490 Err(RequestTokenError::Parse(_, _)) => {
491 log_error!("ID token failed to parse without `sub` claim (expected result)")
492 }
493 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
494 }
495 log_info!("SUCCESS");
496 }
497
498 #[test]
499 #[ignore]
rp_id_token_bad_sig_rs256()500 fn rp_id_token_bad_sig_rs256() {
501 let test_state = TestState::init("rp-id_token-bad-sig-rs256", |reg| reg)
502 .authorize(&[])
503 .exchange_code();
504
505 match test_state.id_token_claims_failure() {
506 ClaimsVerificationError::SignatureVerification(
507 SignatureVerificationError::CryptoError(_),
508 ) => log_error!("ID token has invalid signature (expected result)"),
509 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
510 }
511
512 log_info!("SUCCESS");
513 }
514
515 #[test]
516 #[ignore]
rp_id_token_bad_sig_hs256()517 fn rp_id_token_bad_sig_hs256() {
518 let test_state = TestState::init("rp-id_token-bad-sig-hs256", |reg| reg)
519 .authorize(&[])
520 .exchange_code();
521
522 let verifier = test_state
523 .id_token_verifier(test_state.jwks())
524 .set_allowed_algs(vec![CoreJwsSigningAlgorithm::HmacSha256]);
525 let id_token_err = test_state
526 .id_token()
527 .claims(&verifier, test_state.nonce.as_ref().expect("no nonce"))
528 .expect_err("claims verification succeeded but was expected to fail");
529 match id_token_err {
530 ClaimsVerificationError::SignatureVerification(
531 SignatureVerificationError::CryptoError(_),
532 ) => log_error!("ID token has invalid signature (expected result)"),
533 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
534 }
535
536 log_info!("SUCCESS");
537 }
538
539 #[test]
540 #[ignore]
rp_id_token_issuer_mismatch()541 fn rp_id_token_issuer_mismatch() {
542 let test_state = TestState::init("rp-id_token-issuer-mismatch", |reg| reg)
543 .authorize(&[])
544 .exchange_code();
545
546 match test_state.id_token_claims_failure() {
547 ClaimsVerificationError::InvalidIssuer(_) => {
548 log_error!("ID token has invalid issuer (expected result)")
549 }
550 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
551 }
552
553 log_info!("SUCCESS");
554 }
555
556 #[test]
557 #[ignore]
rp_userinfo_bad_sub_claim()558 fn rp_userinfo_bad_sub_claim() {
559 let test_state = TestState::init("rp-userinfo-bad-sub-claim", |reg| reg)
560 .authorize(&[Scope::new("profile".to_string())])
561 .exchange_code();
562 let id_token_claims = test_state.id_token_claims();
563 log_debug!("ID token: {:?}", id_token_claims);
564
565 match test_state.user_info_claims_failure() {
566 UserInfoError::ClaimsVerification(ClaimsVerificationError::InvalidSubject(_)) => {
567 log_error!("UserInfo response has invalid subject (expected result)")
568 }
569 other => panic!("Unexpected result verifying ID token claims: {:?}", other),
570 }
571 log_info!("SUCCESS");
572 }
573
574 #[test]
575 #[ignore]
rp_userinfo_bearer_header()576 fn rp_userinfo_bearer_header() {
577 let test_state = TestState::init("rp-userinfo-bearer-header", |reg| reg)
578 .authorize(&[Scope::new("profile".to_string())])
579 .exchange_code();
580 let id_token_claims = test_state.id_token_claims();
581 log_debug!("ID token: {:?}", id_token_claims);
582
583 let user_info_claims = test_state.user_info_claims();
584 log_debug!("UserInfo response: {:?}", user_info_claims);
585 log_info!("SUCCESS");
586 }
587
588 #[test]
589 #[ignore]
rp_userinfo_sig()590 fn rp_userinfo_sig() {
591 let test_state = TestState::init("rp-userinfo-sig", |reg| {
592 reg.set_userinfo_signed_response_alg(Some(CoreJwsSigningAlgorithm::RsaSsaPkcs1V15Sha256))
593 })
594 .authorize(&[Scope::new("profile".to_string())])
595 .exchange_code();
596 let id_token_claims = test_state.id_token_claims();
597 log_debug!("ID token: {:?}", id_token_claims);
598
599 let user_info_claims: CoreUserInfoClaims = test_state
600 .client
601 .user_info(
602 test_state.access_token().to_owned(),
603 Some(id_token_claims.subject().clone()),
604 )
605 .unwrap()
606 // For some reason, the test suite omits these claims even though the Core spec says
607 // that the RP SHOULD verify these.
608 .require_audience_match(false)
609 .require_issuer_match(false)
610 .request(http_client)
611 .panic_if_fail("failed to get UserInfo");
612
613 log_debug!("UserInfo response: {:?}", user_info_claims);
614 log_info!("SUCCESS");
615 }
616