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.
87 lines
2.9 KiB
Rust
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());
|
|
}
|