use rocket::http::Status; use rocket::local::asynchronous::Client; use rocket::uri; use scraper::{Html, Selector}; use tracing::debug; use seensite::auth::*; use seensite::Context; #[rocket::async_test] async fn test_login() { super::setup(); let client = Client::tracked(seensite::rocket()).await.unwrap(); let dex = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) .cookie_store(true) .build() .unwrap(); let ctx: &Context = client.rocket().state().unwrap(); // First, initiate the login process let req = client.get(uri![oidc_login]); let res = req.dispatch().await; assert_eq!(res.status(), Status::SeeOther); let mut location = res.headers().get_one("Location").unwrap().to_string(); // Next, follow the redirect URL provided by the login page. // This will redirect again. let res = loop { debug!("Redirect: {}", location); let res = dex.get(location).send().await.unwrap(); if res.status() == reqwest::StatusCode::FOUND { let base_url = res.url().clone(); location = base_url .join(res.headers().get("Location").unwrap().to_str().unwrap()) .unwrap() .to_string(); continue; } break res; }; // After all the redirects, we end up on the IdP login form. assert_eq!(res.status(), reqwest::StatusCode::OK); // Obtain the login form target let base_url = res.url().clone(); let body = res.text().await; let doc = Html::parse_fragment(&body.unwrap()); let sel = Selector::parse("form").unwrap(); let form = doc.select(&sel).next().unwrap(); let action = form.attr("action").unwrap(); let url = base_url.join(action).unwrap(); // Post the user credentials to the IdP login form let res = dex .post(url) .form(&[("login", "user@example.com"), ("password", "password")]) .send() .await .unwrap(); assert_eq!(res.status(), reqwest::StatusCode::SEE_OTHER); // The result of the IdP login form submission is another redirect // to the OIDC callback of our application. let location = reqwest::Url::parse( res.headers().get("Location").unwrap().to_str().unwrap(), ) .unwrap(); let callback = format!("{}?{}", location.path(), location.query().unwrap()); debug!("Callback: {}", callback); // Finally, make the callback request to finish the login process. let res = client.get(callback).dispatch().await; assert_eq!(res.status(), Status::SeeOther); let location = res.headers().get_one("Location").unwrap().to_string(); assert_eq!(location, "/"); let cookie = res.cookies().get("auth.token").unwrap(); debug!("Cookie: {:?}", cookie); // Check to ensure the cookie contains a valid token let user = ctx.decode_jwt(cookie.value()).unwrap(); debug!("User: {:?}", user); assert!(!user.id().is_empty()); }