1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use http_types::{headers::LOCATION, StatusCode};
use tide::sessions::{MemoryStore, SessionMiddleware};
use tide_openidconnect::{ClientId, ClientSecret, IssuerUrl, OpenIdConnectRequestExt, RedirectUrl};
pub mod authorizeurl;
pub mod cookiejar;
pub mod oidc_emulator;
const SECRET: [u8; 32] = *b"secrets must be >= 32 bytes long";
pub fn get_config(issuer_url: &IssuerUrl) -> tide_openidconnect::Config {
tide_openidconnect::Config {
issuer_url: issuer_url.clone(),
client_id: ClientId::new("CLIENT-ID".to_string()),
client_secret: ClientSecret::new("CLIENT-SECRET".to_string()),
redirect_url: RedirectUrl::new("http://localhost/callback".to_string()).unwrap(),
idp_logout_url: None,
}
}
pub fn create_test_server() -> tide::Server<()> {
// Create the Tide server and our (required-by-OpenIdConnectMiddleware)
// session middleware. We do *not* add the OpenIdConnectMiddleware
// in this function; we let the caller do that so that it can configure
// the middleware according to the requirements of the test.
let mut app = tide::new();
app.with(
SessionMiddleware::new(MemoryStore::new(), &SECRET)
.with_same_site_policy(tide::http::cookies::SameSite::Lax),
);
// Add the `/` route, which we use to check the authed/unauthed
// status status of the request. Note that we would also like
// to use this handler to confirm that session state is preserved
// across the auth procedure (which it is), but that behavior
// is mostly a side-effect of the SessionMiddleware's cookie
// setting (which *this* middleware requires be set to Lax).
// So we could test that, but A) we would only be testing that
// we set that to Lax earlier in this test function, and more
// importantly B) wouldn't actually be testing the SameSite
// setting anyway, since we are manually flowing the session
// cookie across requests without even paying attention to the
// SameSite attribute in the response. However, even with all
// of that said, we still want to store (and later, check)
// app-level session state, since we want to confirm that our
// logout setting -- clear auth data vs. destroy session --
// preserves/does not preserve that app state. Note that if
// we destroy the session then the session id (and thus, cookie)
// is no longer valid, whereas if we just clear the auth state
// then the id is still valid and it is only the auth state
// inside that session that is removed.
app.at("/").get(|mut req: tide::Request<()>| async move {
// Get/Update the request counter (used to verify session state
// across login/logout operations).
let session = req.session_mut();
let visits: usize = session.get::<usize>("visits").unwrap_or_default() + 1;
session.insert("visits", visits).unwrap();
// Return the string that we use to validate the request state.
Ok(if req.is_authenticated() {
format!(
"authed visits={} access_token={} scopes={:?} userid={}",
visits,
req.access_token().unwrap(),
req.scopes().unwrap(),
req.user_id().unwrap(),
)
} else {
format!("unauthed visits={}", visits)
})
});
// Return the server.
app
}
pub async fn assert_response(res: &mut surf::Response, expected_body: impl AsRef<str>) {
if res.status() != StatusCode::Ok {
panic!(
"Response should have HTTP Status `Ok`, instead it had `{}`",
res.status()
);
}
let body = res.body_string().await.unwrap();
if body != expected_body.as_ref() {
panic!(
"Expected body `{}`, found `{}` instead.",
expected_body.as_ref(),
body
);
}
}
pub fn assert_redirect(res: &surf::Response, expected_target: impl AsRef<str>) {
if res.status() != StatusCode::Found {
panic!(
"Redirect should have HTTP Status `Found`, instead it had `{}`",
res.status()
);
}
let target = res.header(LOCATION).unwrap().get(0).unwrap();
if target != expected_target.as_ref() {
panic!(
"Expected redirect to location `{}`, found `{}` instead.",
expected_target.as_ref(),
target
);
}
}