Skip to main content

oauth_toolkit/
redirect.rs

1//! Redirect URI validation for OAuth2.
2
3/// Exact-match a redirect URI against a registered allowlist.
4pub fn redirect_uri_allowed(requested: &str, registered: &[String]) -> bool {
5    registered.iter().any(|r| r == requested)
6}
7
8/// Build a provider callback redirect URI from a base URL and provider name.
9///
10/// Returns `"{base_url}/auth/callback/{provider}"`.
11pub fn build_redirect_uri(base_url: &str, provider: &str) -> String {
12    format!(
13        "{}/auth/callback/{}",
14        base_url.trim_end_matches('/'),
15        provider
16    )
17}
18
19#[cfg(test)]
20// Test code: unwrap/expect are the idiomatic way to assert setup success.
21#[allow(clippy::unwrap_used, clippy::expect_used)]
22mod tests {
23    use super::*;
24
25    #[test]
26    fn redirect_uri_allowed_exact() {
27        assert!(redirect_uri_allowed(
28            "https://app.com/callback",
29            &["https://app.com/callback".into()]
30        ));
31    }
32
33    #[test]
34    fn redirect_uri_allowed_mismatch() {
35        assert!(!redirect_uri_allowed(
36            "https://evil.com/callback",
37            &["https://app.com/callback".into()]
38        ));
39    }
40
41    #[test]
42    fn redirect_uri_allowed_empty() {
43        assert!(!redirect_uri_allowed("https://app.com", &[]));
44    }
45
46    #[test]
47    fn build_redirect_uri_works() {
48        assert_eq!(
49            build_redirect_uri("https://app.com", "github"),
50            "https://app.com/auth/callback/github"
51        );
52    }
53
54    #[test]
55    fn build_redirect_uri_trailing_slash() {
56        assert_eq!(
57            build_redirect_uri("https://app.com/", "google"),
58            "https://app.com/auth/callback/google"
59        );
60    }
61}