refactor: general improvements, better comments, structuring of oauth flow (but still broken)

This commit is contained in:
Ryan Walters
2025-09-24 13:13:10 -05:00
parent 655c3c68d5
commit bdd3c74a2d
5 changed files with 237 additions and 180 deletions
+1
View File
@@ -17,6 +17,7 @@ use tracing::{debug, debug_span, Instrument};
static CRYPTO_INIT: Once = Once::new();
/// Test configuration for integration tests
/// Do not destructure this struct if you need the database, it will be dropped implicitly, which will kill the database container prematurely.
#[allow(dead_code)]
pub struct TestContext {
pub config: Config,
+19 -43
View File
@@ -1,8 +1,11 @@
use std::{collections::HashMap, sync::Arc};
use pacman_server::auth::{
provider::{MockOAuthProvider, OAuthProvider},
AuthRegistry,
use pacman_server::{
auth::{
provider::{MockOAuthProvider, OAuthProvider},
AuthRegistry,
},
session,
};
use pretty_assertions::assert_eq;
use time::Duration;
@@ -42,10 +45,10 @@ async fn test_oauth_callback_handling() {
#[tokio::test]
async fn test_oauth_authorization_flow() {
let mut mock = MockOAuthProvider::new();
mock.expect_authorize().returning(|_| {
mock.expect_authorize().returning(|encoding_key| {
Ok(pacman_server::auth::provider::AuthorizeInfo {
authorize_url: "https://example.com".parse().unwrap(),
session_token: "a_token".to_string(),
session_token: session::create_pkce_session("verifier", "state", encoding_key),
})
});
@@ -54,7 +57,7 @@ async fn test_oauth_authorization_flow() {
providers: HashMap::from([("mock", provider)]),
};
let TestContext { server, .. } = test_context().auth_registry(mock_registry).call().await;
let TestContext { server, app_state, .. } = test_context().auth_registry(mock_registry).call().await;
// Test that valid handlers redirect
let response = server.get("/auth/mock").await;
@@ -73,7 +76,8 @@ async fn test_oauth_authorization_flow() {
};
assert_eq!(cookies.len(), 1);
assert_eq!(cookies[0].name(), "session");
assert_eq!(cookies[0].value(), "a_token");
let claims = session::decode_jwt(cookies[0].value(), &app_state.jwt_decoding_key).unwrap();
assert!(session::is_pkce_session(&claims));
// Test that link parameter redirects and sets a link cookie
let response = server.get("/auth/mock?link=true").await;
@@ -164,10 +168,10 @@ async fn test_account_linking_flow() {
let initial_provider: Arc<dyn OAuthProvider> = Arc::new(initial_provider_mock);
let mut link_provider_mock = MockOAuthProvider::new();
link_provider_mock.expect_authorize().returning(|_| {
link_provider_mock.expect_authorize().returning(|encoding_key| {
Ok(pacman_server::auth::provider::AuthorizeInfo {
authorize_url: "https://example.com".parse().unwrap(),
session_token: "b_token".to_string(),
session_token: session::create_pkce_session("verifier", "state", encoding_key),
})
});
link_provider_mock.expect_handle_callback().returning(|_, _, _, _| {
@@ -208,36 +212,15 @@ async fn test_account_linking_flow() {
}
// 2. Create a session for this user
let session_cookie = {
let response = context.server.get("/auth/mock_initial/callback?code=a&state=b").await;
assert_eq!(response.status_code(), 302);
assert!(response.maybe_cookie("session").is_some(), "Session cookie should be set");
response.cookie("session").clone()
};
tracing::debug!(cookie = %session_cookie, "Session cookie acquired");
let response = context.server.get("/auth/mock_initial/callback?code=a&state=b").await;
assert_eq!(response.status_code(), 302);
// Begin linking flow
let link_cookie = {
let response = context
.server
.get("/auth/mock_link?link=true")
.add_cookie(session_cookie.clone())
.await;
assert_eq!(response.status_code(), 303);
assert_eq!(response.maybe_cookie("link").unwrap().value(), "1");
response.cookie("link").clone()
};
tracing::debug!(cookie = %link_cookie, "Link cookie acquired");
let response = context.server.get("/auth/mock_link?link=true").await;
assert_eq!(response.status_code(), 303);
// 3. Perform the linking call
let response = context
.server
.get("/auth/mock_link/callback?code=a&state=b")
.add_cookie(link_cookie)
.add_cookie(session_cookie.clone())
.await;
let response = context.server.get("/auth/mock_link/callback?code=a&state=b").await;
assert_eq!(response.status_code(), 303, "Post-linking response should be a redirect");
assert_eq!(
@@ -415,14 +398,7 @@ async fn test_logout_functionality() {
let session_cookie = response.cookie("session").clone();
// Test that the logout handler clears the session cookie and redirects
let response = context
.server
.get("/logout")
.add_cookie(cookie::Cookie::new(
session_cookie.name().to_string(),
session_cookie.value().to_string(),
))
.await;
let response = context.server.get("/logout").await;
// Redirect assertions
assert_eq!(response.status_code(), 302);
+39 -7
View File
@@ -1,18 +1,50 @@
mod common;
use crate::common::{test_context, TestContext};
use cookie::Cookie;
use pacman_server::session;
use pretty_assertions::assert_eq;
/// Test session management endpoints
#[tokio::test]
async fn test_session_management() {
let TestContext { server, .. } = test_context().use_database(true).call().await;
let context = test_context().use_database(true).call().await;
// Test logout endpoint (should redirect)
let response = server.get("/logout").await;
assert_eq!(response.status_code(), 302); // Redirect to home
// 1. Create a user
let user =
pacman_server::data::user::create_user(&context.app_state.db, "testuser", None, None, None, "test_provider", "123")
.await
.unwrap();
// Test profile endpoint without session (should be unauthorized)
let response = server.get("/profile").await;
// 2. Create a session token for the user
let provider_account = pacman_server::data::user::list_user_providers(&context.app_state.db, user.id)
.await
.unwrap()
.into_iter()
.find(|p| p.provider == "test_provider")
.unwrap();
let auth_user = pacman_server::auth::provider::AuthUser {
id: provider_account.provider_user_id,
username: provider_account.username.unwrap(),
name: provider_account.display_name,
email: user.email,
avatar_url: provider_account.avatar_url,
};
let token = session::create_jwt_for_user("test_provider", &auth_user, &context.app_state.jwt_encoding_key);
// 3. Make a request to the protected route WITH the session, expect success
let response = context
.server
.get("/profile")
.add_cookie(Cookie::new(session::SESSION_COOKIE_NAME, token))
.await;
assert_eq!(response.status_code(), 200);
// 4. Sign out
let response = context.server.get("/logout").await;
assert_eq!(response.status_code(), 302); // Redirect after logout
// 5. Make a request to the protected route without a session, expect failure
let response = context.server.get("/profile").await;
assert_eq!(response.status_code(), 401); // Unauthorized without session
}