190 lines
4.9 KiB
Plaintext
190 lines
4.9 KiB
Plaintext
For a kiosk machine that needs to always auto-login, here's the most practical approach for your Rust/Axum stack:
|
|
|
|
## Recommended Solution: Device-Specific Token Authentication
|
|
|
|
### 1. Database Schema
|
|
```sql
|
|
-- Add to your PostgreSQL database
|
|
CREATE TABLE kiosk_devices (
|
|
id SERIAL PRIMARY KEY,
|
|
device_name VARCHAR(255) NOT NULL,
|
|
device_token VARCHAR(255) UNIQUE NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
last_accessed TIMESTAMP,
|
|
is_active BOOLEAN DEFAULT true
|
|
);
|
|
```
|
|
|
|
### 2. Axum Route Handler
|
|
```rust
|
|
use axum::{
|
|
extract::{Query, State},
|
|
response::{Html, Redirect},
|
|
http::StatusCode,
|
|
};
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize)]
|
|
struct AutoLoginQuery {
|
|
token: String,
|
|
}
|
|
|
|
async fn auto_login(
|
|
Query(params): Query<AutoLoginQuery>,
|
|
State(app_state): State<AppState>, // Your app state with DB pool
|
|
) -> Result<Redirect, StatusCode> {
|
|
// Validate the device token
|
|
let device = sqlx::query!(
|
|
"SELECT id, device_name FROM kiosk_devices
|
|
WHERE device_token = $1 AND is_active = true",
|
|
params.token
|
|
)
|
|
.fetch_optional(&app_state.db)
|
|
.await
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
match device {
|
|
Some(device) => {
|
|
// Update last accessed
|
|
sqlx::query!(
|
|
"UPDATE kiosk_devices SET last_accessed = CURRENT_TIMESTAMP WHERE id = $1",
|
|
device.id
|
|
)
|
|
.execute(&app_state.db)
|
|
.await
|
|
.ok();
|
|
|
|
// Set session/cookie and redirect to dashboard
|
|
Ok(Redirect::to("/kiosk-dashboard"))
|
|
}
|
|
None => Err(StatusCode::UNAUTHORIZED),
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Generate Device Token
|
|
```rust
|
|
use uuid::Uuid;
|
|
use sha2::{Sha256, Digest};
|
|
|
|
fn generate_device_token() -> String {
|
|
let uuid = Uuid::new_v4();
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(uuid.as_bytes());
|
|
format!("{:x}", hasher.finalize())[..32].to_string()
|
|
}
|
|
|
|
// Store this in your database for the kiosk
|
|
async fn register_kiosk_device(db: &PgPool, device_name: &str) -> Result<String, sqlx::Error> {
|
|
let token = generate_device_token();
|
|
|
|
sqlx::query!(
|
|
"INSERT INTO kiosk_devices (device_name, device_token) VALUES ($1, $2)",
|
|
device_name,
|
|
token
|
|
)
|
|
.execute(db)
|
|
.await?;
|
|
|
|
Ok(token)
|
|
}
|
|
```
|
|
|
|
### 4. Kiosk Setup
|
|
Configure the kiosk machine to:
|
|
|
|
**Option A: Browser Homepage**
|
|
Set browser homepage to: `https://yoursite.com/auto-login?token=YOUR_GENERATED_TOKEN`
|
|
|
|
**Option B: Desktop Shortcut**
|
|
Create a desktop shortcut with the auto-login URL
|
|
|
|
**Option C: Startup Script**
|
|
```bash
|
|
#!/bin/bash
|
|
# startup.sh - runs on boot
|
|
sleep 10 # wait for network
|
|
chromium-browser --kiosk --no-sandbox "https://yoursite.com/auto-login?token=YOUR_TOKEN"
|
|
```
|
|
|
|
### 5. Enhanced Security (Optional)
|
|
If you want additional security:
|
|
|
|
```rust
|
|
#[derive(Deserialize)]
|
|
struct AutoLoginQuery {
|
|
token: String,
|
|
timestamp: Option<i64>,
|
|
signature: Option<String>,
|
|
}
|
|
|
|
async fn auto_login_with_signature(
|
|
Query(params): Query<AutoLoginQuery>,
|
|
// ... other params
|
|
) -> Result<Redirect, StatusCode> {
|
|
// Verify timestamp (within 5 minutes)
|
|
if let (Some(timestamp), Some(signature)) = (params.timestamp, params.signature) {
|
|
let now = chrono::Utc::now().timestamp();
|
|
if (now - timestamp).abs() > 300 { // 5 minutes
|
|
return Err(StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
// Verify signature (HMAC with shared secret)
|
|
let expected_sig = create_signature(¶ms.token, timestamp);
|
|
if signature != expected_sig {
|
|
return Err(StatusCode::UNAUTHORIZED);
|
|
}
|
|
}
|
|
|
|
// ... rest of validation
|
|
}
|
|
```
|
|
|
|
### 6. Session Management
|
|
After successful auto-login, set a session cookie:
|
|
|
|
```rust
|
|
use tower_cookies::{Cookie, Cookies};
|
|
|
|
async fn auto_login(
|
|
Query(params): Query<AutoLoginQuery>,
|
|
cookies: Cookies,
|
|
// ... other params
|
|
) -> Result<Redirect, StatusCode> {
|
|
// ... validation logic ...
|
|
|
|
if let Some(device) = device {
|
|
// Set session cookie
|
|
let session_cookie = Cookie::build(("kiosk_session", device.id.to_string()))
|
|
.path("/")
|
|
.http_only(true)
|
|
.max_age(time::Duration::hours(24))
|
|
.build();
|
|
|
|
cookies.add(session_cookie);
|
|
|
|
Ok(Redirect::to("/kiosk-dashboard"))
|
|
} else {
|
|
Err(StatusCode::UNAUTHORIZED)
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7. Route Configuration
|
|
```rust
|
|
let app = Router::new()
|
|
.route("/auto-login", get(auto_login))
|
|
.route("/kiosk-dashboard", get(kiosk_dashboard))
|
|
// ... other routes
|
|
.layer(CookieManagerLayer::new())
|
|
.with_state(app_state);
|
|
```
|
|
|
|
This approach gives you:
|
|
- Secure device authentication
|
|
- Easy kiosk setup
|
|
- Audit trail of access
|
|
- Ability to revoke access
|
|
- No user interaction required
|
|
|
|
The kiosk will automatically authenticate and access your application whenever it starts up or navigates to the URL. |