Files
seensite/tests/integration/auth.rs
Dustin C. Hatch a50dca7fae auth: Introduce User struct
The `UserClaims` structure is an implementation detail of how the JWT
encoding process works.  We do not need to expose all of the details of
the JWT, such as issuer, audience, expiration, etc. to rest of the
application.  Route handlers should only be concerned with the
information about the user, rather than the metadata about how the user
was authenticated.
2025-04-08 20:29:47 -05:00

87 lines
2.9 KiB
Rust

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());
}