Feat/auth server integration#1013
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “Cosmian authentication server” integration across the KMS server and ckms client by adding a dedicated server middleware (CosmianAuth) and a client-side ckms login cosmian flow.
Changes:
- Add server-side
CosmianAuthmiddleware that validates Bearer JWTs using JWKS (including a “nokid” verification strategy that tries all keys). - Extend configuration (CLI/TOML + wizard) to enable Cosmian auth and control JWKS fetching (including an “accept invalid certs” option).
- Add client-side login helper (
cosmian_login) and CLI subcommand (ckms login cosmian) to extract the_ea_cookie value and store/forward it as an access token.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
crate/server/src/start_kms_server.rs |
Wires Cosmian auth into the authenticated scopes and builds a dedicated JWKS manager for it. |
crate/server/src/middlewares/mod.rs |
Exposes the new CosmianAuth middleware. |
crate/server/src/middlewares/jwt/jwks.rs |
Adds JWKS options (accept_invalid_certs) and a find_any() helper for key iteration. |
crate/server/src/middlewares/cosmian_auth/mod.rs |
Introduces the Cosmian auth middleware module. |
crate/server/src/middlewares/cosmian_auth/cosmian_auth_middleware.rs |
Actix middleware wrapper that injects AuthenticatedUser after token validation. |
crate/server/src/middlewares/cosmian_auth/cosmian_auth_token.rs |
Token parsing + validation logic (no-kid behavior, algorithm allowlist, claim mapping). |
crate/server/src/main.rs |
Updates test config construction to include cosmian_auth. |
crate/server/src/config/wizard/mod.rs |
Plumbs wizard output into server config for Cosmian auth. |
crate/server/src/config/wizard/auth_wizard.rs |
Adds interactive wizard support for Cosmian auth server + JWKS options. |
crate/server/src/config/params/server_params.rs |
Adds cosmian_auth_config to computed server params. |
crate/server/src/config/command_line/mod.rs |
Registers the new Cosmian auth config module. |
crate/server/src/config/command_line/cosmian_auth_config.rs |
Defines CosmianAuthConfig and its CLI/env/TOML mapping. |
crate/server/src/config/command_line/clap_config.rs |
Adds cosmian_auth to ClapConfig and debug output. |
crate/clients/client/src/http_client/mod.rs |
Re-exports Cosmian login helpers/config. |
crate/clients/client/src/http_client/login.rs |
Implements CosmianLoginConfig + cosmian_login() extracting _ea_ cookie. |
crate/clients/client/src/http_client/client.rs |
Adds cosmian_conf to client config deserialization. |
crate/clients/clap/src/actions/login.rs |
Changes ckms login into subcommands (oauth / cosmian). |
crate/clients/ckms/src/tests/login_tests.rs |
Updates existing tests to call ckms login oauth. |
CHANGELOG/feat_auth-server-integration.md |
Adds branch changelog entry describing the feature. |
.gitignore |
Ignores .github/agents/. |
| #[derive(Debug)] | ||
| pub struct JwksManager { | ||
| pub(crate) uris: Vec<String>, | ||
| pub(crate) jwks: RwLock<HashMap<String, JwkSet>>, | ||
| pub(crate) last_update: RwLock<Option<DateTime<Utc>>>, | ||
| pub(crate) proxy_params: Option<ProxyParams>, | ||
| /// When `true`, the JWKS fetch client skips TLS certificate verification. | ||
| /// Only set this for development/test environments (e.g. self-signed certs). | ||
| pub(crate) accept_invalid_certs: bool, | ||
| } |
| let jwks_uri = cosmian_cfg.jwks_uri().ok_or_else(|| { | ||
| KmsError::ServerError( | ||
| "Cosmian auth is enabled but no server URL is configured".to_owned(), | ||
| ) | ||
| })?; | ||
| let proxy_params = kms_server.params.proxy_params.clone(); |
| // Determine if Cosmian Auth Server should be used for authentication. | ||
| let (use_cosmian_auth, cosmian_auth_jwks_manager) = if let Some(ref cosmian_cfg) = | ||
| kms_server.params.cosmian_auth_config | ||
| { | ||
| let jwks_uri = cosmian_cfg.jwks_uri().ok_or_else(|| { | ||
| KmsError::ServerError( | ||
| "Cosmian auth is enabled but no server URL is configured".to_owned(), | ||
| ) | ||
| })?; | ||
| let proxy_params = kms_server.params.proxy_params.clone(); | ||
| let mgr = Arc::new( | ||
| JwksManager::new_with_options( | ||
| vec![jwks_uri], | ||
| proxy_params.as_ref(), | ||
| cosmian_cfg.cosmian_auth_accept_invalid_certs, | ||
| ) | ||
| .await | ||
| .map_err(|e| { | ||
| KmsError::ServerError(format!( | ||
| "Failed to initialise Cosmian auth JWKS manager: {e}" | ||
| )) | ||
| })?, | ||
| ); | ||
| (true, Some(mgr)) | ||
| } else { | ||
| (false, None) | ||
| }; |
| /// ```toml | ||
| /// [cosmian_auth] | ||
| /// server_url = "https://localhost:8443" | ||
| /// accept_invalid_certs = false # set true only for dev/test | ||
| /// ``` | ||
| /// | ||
| /// The JWKS endpoint is derived automatically from `server_url` as | ||
| /// `{server_url}/.well-known/jwks.json` unless overridden via `jwks_uri`. |
| let server_url: String = Input::with_theme(&theme) | ||
| .with_prompt("Cosmian auth server URL (e.g. https://auth.example.com)") | ||
| .interact_text() | ||
| .map_err(|e| KmsError::ServerError(format!("Prompt error: {e}")))?; | ||
| let jwks_uri: String = Input::with_theme(&theme) |
| let url = format!( | ||
| "{}/login?realm={}", | ||
| config.server_url.trim_end_matches('/'), | ||
| config.realm | ||
| ); |
| cmd.env(CKMS_CONF_ENV, &conf_path).arg("login").arg("oauth"); | ||
|
|
||
| let output = recover_cmd_logs(&mut cmd); | ||
| assert!( |
| // Fetch all public keys — Cosmian tokens have no `kid` so we try them all. | ||
| let jwks = jwks_manager.find_any()?; | ||
| if jwks.is_empty() { | ||
| // JWKS cache might be stale; attempt one refresh. | ||
| jwks_manager.refresh().await?; | ||
| } | ||
| let jwks = jwks_manager.find_any()?; |
Manuthor
left a comment
There was a problem hiding this comment.
Just a few basic suggestions, not much
Great job!
|
|
||
| #[command(flatten)] | ||
| #[clap(flatten)] | ||
| pub cosmian_auth: CosmianAuthConfig, |
There was a problem hiding this comment.
CosmianAuth being a little vague. Do you think we can find an alternative at least for this CosmianAuthConfig?
What about CosmianAuthServerConfig or CosmianSessionAuthConfig?
About auth server name, we definitely need to find a proper name for the auth server. Should we stick to Cosmian Authentication server or Cosmian IdP since it tends to be an IdP?
| /// realm = "kms" | ||
| /// ``` | ||
| #[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] | ||
| pub struct CosmianLoginConfig { |
There was a problem hiding this comment.
| pub struct CosmianLoginConfig { | |
| pub struct CosmianAuthServerLoginConfig { |
?
There was a problem hiding this comment.
We can rename the filename to be more accurate
There was a problem hiding this comment.
Rename to .../cosmian_auth_server/middleware.rs please
| let auth_header = req | ||
| .headers() | ||
| .get(header::AUTHORIZATION) | ||
| .and_then(|h| h.to_str().ok()) | ||
| .unwrap_or(""); | ||
|
|
||
| // Parse "Bearer <token>". | ||
| let mut parts = auth_header.splitn(2, ' '); | ||
| let scheme = parts.next().unwrap_or(""); | ||
| let token = parts.next().unwrap_or("").trim(); | ||
|
|
||
| if !scheme.eq_ignore_ascii_case("Bearer") || token.is_empty() { | ||
| return Err(KmsError::Unauthorized( | ||
| "Cosmian auth: missing or malformed Authorization header (expected Bearer token)" | ||
| .to_owned(), | ||
| )); | ||
| } |
There was a problem hiding this comment.
We could mutualize this code with similar token-extraction code from api_token_auth.rs (probably create a shared function)
Closes #879