feat(auth): replace cookie/api-key auth with JWT Bearer tokens, separate UI from API
- Add JWT Bearer token validation to Rust API via OIDC provider JWKS with automatic key rotation and 1-hour cache - Remove x-api-key auth support and built-in web UI from furumi-web-player, leaving it as a pure API server - Add /auth/token endpoint to Node player server to expose OIDC access tokens to the frontend - Move Node player auth endpoints from /api/* to /auth/* to avoid path conflicts with Rust API - Add static file serving to Node Express server for production single-container deployment - Fix SameSite=Strict cookie issue breaking OIDC redirect flow (use Lax) - Add Dockerfile.node-player with multi-stage Node.js build - Add CI workflows for node-player Docker image (dev + release) - Optimize Rust Dockerfiles with dependency caching layer - Update docker-compose with OIDC env vars and OLLAMA_MODEL support - Cherry-pick agent LLM client fixes from DEV branch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,6 @@ pub struct AppState {
|
||||
#[allow(dead_code)]
|
||||
pub storage_dir: Arc<PathBuf>,
|
||||
pub oidc: Option<Arc<auth::OidcState>>,
|
||||
pub api_key: Option<String>,
|
||||
}
|
||||
|
||||
pub fn build_router(state: Arc<AppState>) -> Router {
|
||||
@@ -32,37 +31,27 @@ pub fn build_router(state: Arc<AppState>) -> Router {
|
||||
.route("/stream/:slug", get(api::stream_track))
|
||||
.route("/search", get(api::search));
|
||||
|
||||
let authed = Router::new()
|
||||
.route("/", get(player_html))
|
||||
let api = Router::new()
|
||||
.nest("/api", library);
|
||||
|
||||
let requires_auth = state.oidc.is_some();
|
||||
|
||||
let app = if requires_auth {
|
||||
authed
|
||||
.route_layer(middleware::from_fn_with_state(state.clone(), auth::require_auth))
|
||||
api.route_layer(middleware::from_fn_with_state(state.clone(), auth::require_auth))
|
||||
} else {
|
||||
authed
|
||||
api
|
||||
};
|
||||
|
||||
let cors = CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods([Method::GET, Method::OPTIONS, Method::HEAD])
|
||||
.allow_headers([header::ACCEPT, header::CONTENT_TYPE, header::HeaderName::from_static("x-api-key")])
|
||||
.allow_headers([header::ACCEPT, header::CONTENT_TYPE, header::AUTHORIZATION])
|
||||
.max_age(Duration::from_secs(600));
|
||||
|
||||
Router::new()
|
||||
.route("/login", get(auth::login_page))
|
||||
.route("/logout", get(auth::logout))
|
||||
.route("/auth/login", get(auth::oidc_login))
|
||||
.route("/auth/callback", get(auth::oidc_callback))
|
||||
.merge(app)
|
||||
.layer(cors)
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn player_html() -> axum::response::Html<String> {
|
||||
let html = include_str!("player.html")
|
||||
.replace("<!-- VERSION_PLACEHOLDER -->", option_env!("FURUMI_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")));
|
||||
axum::response::Html(html)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user