create-ekka-desktop-app 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ use std::path::PathBuf;
|
|
|
10
10
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
11
11
|
|
|
12
12
|
/// Check if a valid HOME grant exists for the given auth context
|
|
13
|
-
pub fn check_home_grant(home_path: &PathBuf, auth: &AuthContext) -> Result<bool, String> {
|
|
13
|
+
pub fn check_home_grant(home_path: &PathBuf, auth: &AuthContext, verify_key: &str) -> Result<bool, String> {
|
|
14
14
|
let grants_path = home_path.join("grants.json");
|
|
15
15
|
|
|
16
16
|
// No grants file = no grant
|
|
@@ -18,11 +18,7 @@ pub fn check_home_grant(home_path: &PathBuf, auth: &AuthContext) -> Result<bool,
|
|
|
18
18
|
return Ok(false);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
let key_b64 = match std::env::var("ENGINE_GRANT_VERIFY_KEY_B64") {
|
|
23
|
-
Ok(k) => k,
|
|
24
|
-
Err(_) => return Err("ENGINE_GRANT_VERIFY_KEY_B64 not set".to_string()),
|
|
25
|
-
};
|
|
21
|
+
let key_b64 = verify_key;
|
|
26
22
|
|
|
27
23
|
// Load and verify grants
|
|
28
24
|
let store = GrantStore::new(grants_path, &key_b64).map_err(|e| e.to_string())?;
|
|
@@ -101,8 +97,21 @@ pub fn get_home_status(state: &EngineState) -> (HomeState, PathBuf, bool, Option
|
|
|
101
97
|
None => return (HomeState::BootstrapPreLogin, home_path, false, None),
|
|
102
98
|
};
|
|
103
99
|
|
|
100
|
+
// Get verify key from state (fetched from well-known endpoint)
|
|
101
|
+
let verify_key = match state.get_grant_verify_key() {
|
|
102
|
+
Some(k) => k,
|
|
103
|
+
None => {
|
|
104
|
+
return (
|
|
105
|
+
HomeState::AuthenticatedNoHomeGrant,
|
|
106
|
+
home_path,
|
|
107
|
+
false,
|
|
108
|
+
Some("Grant verification key not loaded. Waiting for engine configuration.".to_string()),
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
104
113
|
// Check for valid HOME grant
|
|
105
|
-
match check_home_grant(&home_path, &auth) {
|
|
114
|
+
match check_home_grant(&home_path, &auth, &verify_key) {
|
|
106
115
|
Ok(true) => (HomeState::HomeGranted, home_path, true, None),
|
|
107
116
|
Ok(false) => (
|
|
108
117
|
HomeState::AuthenticatedNoHomeGrant,
|
|
@@ -11,6 +11,7 @@ mod config;
|
|
|
11
11
|
mod device_secret;
|
|
12
12
|
mod engine_process;
|
|
13
13
|
mod grants;
|
|
14
|
+
mod well_known;
|
|
14
15
|
mod handlers;
|
|
15
16
|
mod node_auth;
|
|
16
17
|
mod node_credentials;
|
|
@@ -30,14 +31,11 @@ use tauri::Manager;
|
|
|
30
31
|
|
|
31
32
|
fn main() {
|
|
32
33
|
// Load .env.local for development (before anything else)
|
|
33
|
-
// This provides
|
|
34
|
-
|
|
34
|
+
// This provides EKKA_SECURITY_EPOCH and other dev-time overrides.
|
|
35
|
+
// Note: ENGINE_GRANT_VERIFY_KEY_B64 is now fetched from /.well-known/ekka-configuration
|
|
36
|
+
if let Err(_) = dotenvy::from_filename(".env.local") {
|
|
35
37
|
// Also try parent directory (when running from src-tauri)
|
|
36
38
|
let _ = dotenvy::from_filename("../.env.local");
|
|
37
|
-
// Silence error in production where .env.local may not exist
|
|
38
|
-
if std::env::var("ENGINE_GRANT_VERIFY_KEY_B64").is_err() {
|
|
39
|
-
eprintln!("Warning: .env.local not loaded and ENGINE_GRANT_VERIFY_KEY_B64 not set: {}", e);
|
|
40
|
-
}
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
// Initialize tracing for runner logs
|
|
@@ -65,14 +63,12 @@ fn main() {
|
|
|
65
63
|
// Attempt to spawn engine process
|
|
66
64
|
tracing::info!(op = "desktop.startup", "EKKA Desktop starting");
|
|
67
65
|
|
|
68
|
-
// Log
|
|
69
|
-
let grant_key_set = std::env::var("ENGINE_GRANT_VERIFY_KEY_B64").is_ok();
|
|
66
|
+
// Log security epoch status (still needed for home bootstrap)
|
|
70
67
|
let security_epoch_set = std::env::var("EKKA_SECURITY_EPOCH").is_ok();
|
|
71
68
|
tracing::info!(
|
|
72
69
|
op = "desktop.required_env.loaded",
|
|
73
|
-
ENGINE_GRANT_VERIFY_KEY_B64 = grant_key_set,
|
|
74
70
|
EKKA_SECURITY_EPOCH = security_epoch_set,
|
|
75
|
-
"
|
|
71
|
+
"Security epoch env var status"
|
|
76
72
|
);
|
|
77
73
|
|
|
78
74
|
// Log build-time baked engine URL presence (not the URL itself)
|
|
@@ -107,6 +103,28 @@ fn main() {
|
|
|
107
103
|
let node_auth_holder = state_handle.node_auth_token.clone();
|
|
108
104
|
let node_auth_state = state_handle.node_auth_state.clone();
|
|
109
105
|
|
|
106
|
+
// Fetch grant verification key from engine's well-known endpoint
|
|
107
|
+
// This runs async and caches the key in state for grant verification
|
|
108
|
+
let app_handle = app.app_handle().clone();
|
|
109
|
+
tauri::async_runtime::spawn(async move {
|
|
110
|
+
let state = app_handle.state::<EngineState>();
|
|
111
|
+
match well_known::fetch_and_cache_verify_key(&state).await {
|
|
112
|
+
Ok(()) => {
|
|
113
|
+
tracing::info!(
|
|
114
|
+
op = "desktop.well_known.loaded",
|
|
115
|
+
"Grant verification key loaded from engine"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
Err(e) => {
|
|
119
|
+
tracing::warn!(
|
|
120
|
+
op = "desktop.well_known.failed",
|
|
121
|
+
error = %e,
|
|
122
|
+
"Failed to fetch grant verification key - grants will not be verified"
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
110
128
|
// Spawn engine in background thread to not block UI
|
|
111
129
|
let engine = engine_for_setup.clone();
|
|
112
130
|
std::thread::spawn(move || {
|
|
@@ -325,6 +325,8 @@ pub struct EngineState {
|
|
|
325
325
|
pub node_auth_state: Arc<NodeAuthStateHolder>,
|
|
326
326
|
/// External engine process (Phase 3A)
|
|
327
327
|
pub engine_process: Option<Arc<EngineProcess>>,
|
|
328
|
+
/// Cached grant verification key (fetched from /.well-known/ekka-configuration)
|
|
329
|
+
pub grant_verify_key: RwLock<Option<String>>,
|
|
328
330
|
}
|
|
329
331
|
|
|
330
332
|
impl Default for EngineState {
|
|
@@ -341,6 +343,7 @@ impl Default for EngineState {
|
|
|
341
343
|
node_auth_token: Arc::new(NodeAuthTokenHolder::new()),
|
|
342
344
|
node_auth_state: Arc::new(NodeAuthStateHolder::new()),
|
|
343
345
|
engine_process: None,
|
|
346
|
+
grant_verify_key: RwLock::new(None),
|
|
344
347
|
}
|
|
345
348
|
}
|
|
346
349
|
}
|
|
@@ -360,6 +363,7 @@ impl EngineState {
|
|
|
360
363
|
node_auth_token: Arc::new(NodeAuthTokenHolder::new()),
|
|
361
364
|
node_auth_state: Arc::new(NodeAuthStateHolder::new()),
|
|
362
365
|
engine_process: Some(engine),
|
|
366
|
+
grant_verify_key: RwLock::new(None),
|
|
363
367
|
}
|
|
364
368
|
}
|
|
365
369
|
|
|
@@ -431,6 +435,18 @@ impl EngineState {
|
|
|
431
435
|
pub fn clear_vault_cache(&self) {
|
|
432
436
|
self.vault_cache.clear();
|
|
433
437
|
}
|
|
438
|
+
|
|
439
|
+
/// Get cached grant verification key
|
|
440
|
+
pub fn get_grant_verify_key(&self) -> Option<String> {
|
|
441
|
+
self.grant_verify_key.read().ok()?.clone()
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/// Set grant verification key (fetched from well-known endpoint)
|
|
445
|
+
pub fn set_grant_verify_key(&self, key: String) {
|
|
446
|
+
if let Ok(mut guard) = self.grant_verify_key.write() {
|
|
447
|
+
*guard = Some(key);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
434
450
|
}
|
|
435
451
|
|
|
436
452
|
// =============================================================================
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//! Well-Known Configuration Fetcher
|
|
2
|
+
//!
|
|
3
|
+
//! Fetches public configuration from the EKKA Engine's /.well-known/ekka-configuration endpoint.
|
|
4
|
+
//! This includes the grant verification key needed for cryptographic grant validation.
|
|
5
|
+
|
|
6
|
+
use crate::config;
|
|
7
|
+
use serde::Deserialize;
|
|
8
|
+
|
|
9
|
+
/// Response from /.well-known/ekka-configuration
|
|
10
|
+
#[derive(Debug, Deserialize)]
|
|
11
|
+
pub struct WellKnownConfig {
|
|
12
|
+
pub grant_verify_key_b64: String,
|
|
13
|
+
pub grant_signing_algorithm: String,
|
|
14
|
+
#[serde(default)]
|
|
15
|
+
pub api_version: Option<String>,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Fetch the well-known configuration from the engine.
|
|
19
|
+
/// Returns the grant verification key (base64).
|
|
20
|
+
pub async fn fetch_grant_verify_key() -> Result<String, String> {
|
|
21
|
+
let engine_url = config::engine_url();
|
|
22
|
+
let url = format!("{}/engine/.well-known/ekka-configuration", engine_url.trim_end_matches('/'));
|
|
23
|
+
|
|
24
|
+
tracing::info!(
|
|
25
|
+
op = "well_known.fetch.start",
|
|
26
|
+
url = %url,
|
|
27
|
+
"Fetching grant verification key from engine"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
let client = reqwest::Client::builder()
|
|
31
|
+
.timeout(std::time::Duration::from_secs(10))
|
|
32
|
+
.build()
|
|
33
|
+
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
|
|
34
|
+
|
|
35
|
+
let response = client
|
|
36
|
+
.get(&url)
|
|
37
|
+
.header("X-EKKA-CLIENT", config::app_slug())
|
|
38
|
+
.send()
|
|
39
|
+
.await
|
|
40
|
+
.map_err(|e| format!("Failed to fetch well-known config: {}", e))?;
|
|
41
|
+
|
|
42
|
+
if !response.status().is_success() {
|
|
43
|
+
return Err(format!(
|
|
44
|
+
"Engine returned error: {} {}",
|
|
45
|
+
response.status().as_u16(),
|
|
46
|
+
response.status().canonical_reason().unwrap_or("Unknown")
|
|
47
|
+
));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let config: WellKnownConfig = response
|
|
51
|
+
.json()
|
|
52
|
+
.await
|
|
53
|
+
.map_err(|e| format!("Failed to parse well-known config: {}", e))?;
|
|
54
|
+
|
|
55
|
+
tracing::info!(
|
|
56
|
+
op = "well_known.fetch.success",
|
|
57
|
+
algorithm = %config.grant_signing_algorithm,
|
|
58
|
+
"Grant verification key fetched successfully"
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
Ok(config.grant_verify_key_b64)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Fetch and cache the grant verification key in state.
|
|
65
|
+
/// Also sets ENGINE_GRANT_VERIFY_KEY_B64 env var for SDK compatibility.
|
|
66
|
+
/// This is called on app startup.
|
|
67
|
+
pub async fn fetch_and_cache_verify_key(state: &crate::state::EngineState) -> Result<(), String> {
|
|
68
|
+
let key = fetch_grant_verify_key().await?;
|
|
69
|
+
|
|
70
|
+
// Cache in state
|
|
71
|
+
state.set_grant_verify_key(key.clone());
|
|
72
|
+
|
|
73
|
+
// Also set env var for SDK compatibility (ekka-ops, ekka-path-guard use it)
|
|
74
|
+
std::env::set_var("ENGINE_GRANT_VERIFY_KEY_B64", &key);
|
|
75
|
+
|
|
76
|
+
Ok(())
|
|
77
|
+
}
|