145 lines
5.2 KiB
Rust
145 lines
5.2 KiB
Rust
|
|
use axum::{middleware, response::Html, routing::get, Router};
|
||
|
|
use sf_auth_middleware_axum::{auth_callback, sf_auth_middleware, SfAuthConfig, SfUser};
|
||
|
|
use tower_sessions::{MemoryStore, SessionManagerLayer};
|
||
|
|
|
||
|
|
#[tokio::main]
|
||
|
|
async fn main() {
|
||
|
|
// Set up tracing for debugging
|
||
|
|
tracing_subscriber::fmt::init();
|
||
|
|
|
||
|
|
// Configure the SF authentication middleware
|
||
|
|
// The redirect_uri should point to where users should land after authentication
|
||
|
|
let config = SfAuthConfig::new("http://localhost:3000/dashboard");
|
||
|
|
|
||
|
|
// Set up session store using in-memory storage
|
||
|
|
// In production, you'd want to use a persistent store like Redis or PostgreSQL
|
||
|
|
let session_store = MemoryStore::default();
|
||
|
|
let session_layer = SessionManagerLayer::new(session_store);
|
||
|
|
|
||
|
|
// Build the application router
|
||
|
|
let app = Router::new()
|
||
|
|
// Public route - no authentication required
|
||
|
|
.route("/", get(home))
|
||
|
|
// Authentication callback route - must be publicly accessible
|
||
|
|
// This is where the SF auth server redirects users after authentication
|
||
|
|
.route("/auth/callback", get(auth_callback))
|
||
|
|
// Protected routes - require authentication
|
||
|
|
.route("/dashboard", get(dashboard))
|
||
|
|
.route("/profile", get(profile))
|
||
|
|
// Apply authentication middleware to protected routes
|
||
|
|
.layer(middleware::from_fn(move |session, req, next| {
|
||
|
|
sf_auth_middleware(config.clone(), session, req, next)
|
||
|
|
}))
|
||
|
|
// Apply session layer (must be after the routes)
|
||
|
|
.layer(session_layer);
|
||
|
|
|
||
|
|
// Start the server
|
||
|
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
||
|
|
.await
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
println!("Server running on http://localhost:3000");
|
||
|
|
println!("Try accessing:");
|
||
|
|
println!(" - http://localhost:3000/ (public)");
|
||
|
|
println!(" - http://localhost:3000/dashboard (protected, will redirect to SF auth)");
|
||
|
|
println!(" - http://localhost:3000/profile (protected, will redirect to SF auth)");
|
||
|
|
|
||
|
|
axum::serve(listener, app).await.unwrap();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Public home page
|
||
|
|
async fn home() -> Html<&'static str> {
|
||
|
|
Html(
|
||
|
|
r#"
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<title>SF Auth Example</title>
|
||
|
|
<style>
|
||
|
|
body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
|
||
|
|
a { color: #0066cc; text-decoration: none; }
|
||
|
|
a:hover { text-decoration: underline; }
|
||
|
|
.button {
|
||
|
|
display: inline-block;
|
||
|
|
padding: 10px 20px;
|
||
|
|
background: #0066cc;
|
||
|
|
color: white;
|
||
|
|
border-radius: 4px;
|
||
|
|
margin: 10px 5px;
|
||
|
|
}
|
||
|
|
.button:hover { background: #0052a3; text-decoration: none; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<h1>Welcome to SF Auth Example</h1>
|
||
|
|
<p>This is a public page that anyone can access.</p>
|
||
|
|
<p>Try accessing protected pages:</p>
|
||
|
|
<a href="/dashboard" class="button">Go to Dashboard (Protected)</a>
|
||
|
|
<a href="/profile" class="button">Go to Profile (Protected)</a>
|
||
|
|
<p>When you try to access a protected page, you'll be redirected to the SnazzyFellas authentication server.</p>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
"#,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Protected dashboard page
|
||
|
|
async fn dashboard(user: SfUser) -> Html<String> {
|
||
|
|
Html(format!(
|
||
|
|
r#"
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<title>Dashboard</title>
|
||
|
|
<style>
|
||
|
|
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }}
|
||
|
|
.user-info {{ background: #f0f0f0; padding: 15px; border-radius: 4px; margin: 20px 0; }}
|
||
|
|
a {{ color: #0066cc; }}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<h1>Dashboard</h1>
|
||
|
|
<div class="user-info">
|
||
|
|
<h2>Authenticated User</h2>
|
||
|
|
<p><strong>Username:</strong> {}</p>
|
||
|
|
<p><strong>User ID:</strong> {}</p>
|
||
|
|
</div>
|
||
|
|
<p><a href="/">Back to Home</a> | <a href="/profile">View Profile</a></p>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
"#,
|
||
|
|
user.username(),
|
||
|
|
user.user_id()
|
||
|
|
))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Protected profile page
|
||
|
|
async fn profile(user: SfUser) -> Html<String> {
|
||
|
|
Html(format!(
|
||
|
|
r#"
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<title>Profile</title>
|
||
|
|
<style>
|
||
|
|
body {{ font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }}
|
||
|
|
.profile {{ background: #e8f4f8; padding: 20px; border-radius: 4px; margin: 20px 0; }}
|
||
|
|
a {{ color: #0066cc; }}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<h1>User Profile</h1>
|
||
|
|
<div class="profile">
|
||
|
|
<h2>{}</h2>
|
||
|
|
<p><strong>ID:</strong> {}</p>
|
||
|
|
<p>This is your protected profile page.</p>
|
||
|
|
</div>
|
||
|
|
<p><a href="/">Back to Home</a> | <a href="/dashboard">View Dashboard</a></p>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
"#,
|
||
|
|
user.username(),
|
||
|
|
user.user_id()
|
||
|
|
))
|
||
|
|
}
|