@auths-dev/sdk 0.0.1 → 0.1.0
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/Cargo.toml +45 -0
- package/README.md +163 -4
- package/__test__/client.spec.ts +78 -0
- package/__test__/exports.spec.ts +57 -0
- package/__test__/integration.spec.ts +407 -0
- package/__test__/policy.spec.ts +202 -0
- package/__test__/verify.spec.ts +88 -0
- package/build.rs +5 -0
- package/index.d.ts +259 -0
- package/index.js +622 -1
- package/lib/artifacts.ts +124 -0
- package/lib/attestations.ts +126 -0
- package/lib/audit.ts +189 -0
- package/lib/client.ts +293 -0
- package/lib/commits.ts +70 -0
- package/lib/devices.ts +178 -0
- package/lib/errors.ts +306 -0
- package/lib/identity.ts +280 -0
- package/lib/index.ts +125 -0
- package/lib/native.ts +255 -0
- package/lib/org.ts +235 -0
- package/lib/pairing.ts +271 -0
- package/lib/policy.ts +669 -0
- package/lib/signing.ts +204 -0
- package/lib/trust.ts +152 -0
- package/lib/types.ts +179 -0
- package/lib/verify.ts +241 -0
- package/lib/witness.ts +91 -0
- package/npm/darwin-arm64/README.md +3 -0
- package/npm/darwin-arm64/package.json +23 -0
- package/npm/linux-arm64-gnu/README.md +3 -0
- package/npm/linux-arm64-gnu/package.json +26 -0
- package/npm/linux-x64-gnu/README.md +3 -0
- package/npm/linux-x64-gnu/package.json +26 -0
- package/npm/win32-arm64-msvc/README.md +3 -0
- package/npm/win32-arm64-msvc/package.json +23 -0
- package/npm/win32-x64-msvc/README.md +3 -0
- package/npm/win32-x64-msvc/package.json +23 -0
- package/package.json +51 -16
- package/src/artifact.rs +217 -0
- package/src/attestation_query.rs +104 -0
- package/src/audit.rs +128 -0
- package/src/commit_sign.rs +63 -0
- package/src/device.rs +212 -0
- package/src/diagnostics.rs +106 -0
- package/src/error.rs +5 -0
- package/src/helpers.rs +60 -0
- package/src/identity.rs +467 -0
- package/src/lib.rs +26 -0
- package/src/org.rs +430 -0
- package/src/pairing.rs +454 -0
- package/src/policy.rs +147 -0
- package/src/sign.rs +215 -0
- package/src/trust.rs +189 -0
- package/src/types.rs +205 -0
- package/src/verify.rs +447 -0
- package/src/witness.rs +138 -0
- package/tsconfig.json +19 -0
- package/typedoc.json +18 -0
- package/vitest.config.ts +12 -0
package/src/device.rs
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
use std::path::PathBuf;
|
|
2
|
+
use std::sync::Arc;
|
|
3
|
+
|
|
4
|
+
use auths_core::signing::PrefilledPassphraseProvider;
|
|
5
|
+
use auths_core::storage::keychain::get_platform_keychain_with_config;
|
|
6
|
+
use auths_sdk::context::AuthsContext;
|
|
7
|
+
use auths_sdk::device::extend_device;
|
|
8
|
+
use auths_sdk::device::{link_device, revoke_device};
|
|
9
|
+
use auths_sdk::types::{DeviceExtensionConfig, DeviceLinkConfig};
|
|
10
|
+
use auths_storage::git::{
|
|
11
|
+
GitRegistryBackend, RegistryAttestationStorage, RegistryConfig, RegistryIdentityStorage,
|
|
12
|
+
};
|
|
13
|
+
use auths_verifier::clock::SystemClock;
|
|
14
|
+
use auths_verifier::core::Capability;
|
|
15
|
+
use auths_verifier::types::DeviceDID;
|
|
16
|
+
use napi_derive::napi;
|
|
17
|
+
|
|
18
|
+
use crate::error::format_error;
|
|
19
|
+
use crate::helpers::{make_env_config, resolve_key_alias, resolve_passphrase};
|
|
20
|
+
use crate::types::{NapiExtensionResult, NapiLinkResult};
|
|
21
|
+
|
|
22
|
+
fn open_backend(repo: &PathBuf) -> napi::Result<Arc<GitRegistryBackend>> {
|
|
23
|
+
let config = RegistryConfig::single_tenant(repo);
|
|
24
|
+
let backend = GitRegistryBackend::open_existing(config).map_err(|e| {
|
|
25
|
+
format_error(
|
|
26
|
+
"AUTHS_REGISTRY_ERROR",
|
|
27
|
+
format!("Failed to open registry: {e}"),
|
|
28
|
+
)
|
|
29
|
+
})?;
|
|
30
|
+
Ok(Arc::new(backend))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[napi]
|
|
34
|
+
pub fn link_device_to_identity(
|
|
35
|
+
identity_key_alias: String,
|
|
36
|
+
capabilities: Vec<String>,
|
|
37
|
+
repo_path: String,
|
|
38
|
+
passphrase: Option<String>,
|
|
39
|
+
expires_in: Option<i64>,
|
|
40
|
+
) -> napi::Result<NapiLinkResult> {
|
|
41
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
42
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
43
|
+
let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
|
|
44
|
+
let clock = Arc::new(SystemClock);
|
|
45
|
+
|
|
46
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
47
|
+
let backend = open_backend(&repo)?;
|
|
48
|
+
|
|
49
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
50
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
51
|
+
|
|
52
|
+
let alias = resolve_key_alias(&identity_key_alias, keychain.as_ref())?;
|
|
53
|
+
|
|
54
|
+
let parsed_caps: Vec<Capability> = capabilities
|
|
55
|
+
.iter()
|
|
56
|
+
.map(|c| {
|
|
57
|
+
Capability::parse(c).map_err(|e| {
|
|
58
|
+
format_error(
|
|
59
|
+
"AUTHS_INVALID_INPUT",
|
|
60
|
+
format!("Invalid capability '{c}': {e}"),
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
.collect::<napi::Result<Vec<_>>>()?;
|
|
65
|
+
|
|
66
|
+
let link_config = DeviceLinkConfig {
|
|
67
|
+
identity_key_alias: alias,
|
|
68
|
+
device_key_alias: None,
|
|
69
|
+
device_did: None,
|
|
70
|
+
capabilities: parsed_caps,
|
|
71
|
+
expires_in: expires_in.map(|s| s as u64),
|
|
72
|
+
note: None,
|
|
73
|
+
payload: None,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
77
|
+
Arc::from(keychain);
|
|
78
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
79
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
80
|
+
|
|
81
|
+
let ctx = AuthsContext::builder()
|
|
82
|
+
.registry(backend)
|
|
83
|
+
.key_storage(keychain)
|
|
84
|
+
.clock(clock.clone())
|
|
85
|
+
.identity_storage(identity_storage)
|
|
86
|
+
.attestation_sink(attestation_storage.clone())
|
|
87
|
+
.attestation_source(attestation_storage)
|
|
88
|
+
.passphrase_provider(provider)
|
|
89
|
+
.build();
|
|
90
|
+
|
|
91
|
+
let result = link_device(link_config, &ctx, clock.as_ref())
|
|
92
|
+
.map_err(|e| format_error("AUTHS_DEVICE_ERROR", format!("Device linking failed: {e}")))?;
|
|
93
|
+
|
|
94
|
+
Ok(NapiLinkResult {
|
|
95
|
+
device_did: result.device_did.to_string(),
|
|
96
|
+
attestation_id: result.attestation_id.to_string(),
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[napi]
|
|
101
|
+
pub fn revoke_device_from_identity(
|
|
102
|
+
device_did: String,
|
|
103
|
+
identity_key_alias: String,
|
|
104
|
+
repo_path: String,
|
|
105
|
+
passphrase: Option<String>,
|
|
106
|
+
note: Option<String>,
|
|
107
|
+
) -> napi::Result<()> {
|
|
108
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
109
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
110
|
+
let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
|
|
111
|
+
let clock = Arc::new(SystemClock);
|
|
112
|
+
|
|
113
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
114
|
+
let backend = open_backend(&repo)?;
|
|
115
|
+
|
|
116
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
117
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
118
|
+
|
|
119
|
+
let alias = resolve_key_alias(&identity_key_alias, keychain.as_ref())?;
|
|
120
|
+
|
|
121
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
122
|
+
Arc::from(keychain);
|
|
123
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
124
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
125
|
+
|
|
126
|
+
let ctx = AuthsContext::builder()
|
|
127
|
+
.registry(backend)
|
|
128
|
+
.key_storage(keychain)
|
|
129
|
+
.clock(clock.clone())
|
|
130
|
+
.identity_storage(identity_storage)
|
|
131
|
+
.attestation_sink(attestation_storage.clone())
|
|
132
|
+
.attestation_source(attestation_storage)
|
|
133
|
+
.passphrase_provider(provider)
|
|
134
|
+
.build();
|
|
135
|
+
|
|
136
|
+
revoke_device(&device_did, &alias, &ctx, note, clock.as_ref()).map_err(|e| {
|
|
137
|
+
format_error(
|
|
138
|
+
"AUTHS_DEVICE_ERROR",
|
|
139
|
+
format!("Device revocation failed: {e}"),
|
|
140
|
+
)
|
|
141
|
+
})?;
|
|
142
|
+
|
|
143
|
+
Ok(())
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#[napi]
|
|
147
|
+
pub fn extend_device_authorization(
|
|
148
|
+
device_did: String,
|
|
149
|
+
identity_key_alias: String,
|
|
150
|
+
expires_in: i64,
|
|
151
|
+
repo_path: String,
|
|
152
|
+
passphrase: Option<String>,
|
|
153
|
+
) -> napi::Result<NapiExtensionResult> {
|
|
154
|
+
if expires_in <= 0 {
|
|
155
|
+
return Err(format_error(
|
|
156
|
+
"AUTHS_INVALID_INPUT",
|
|
157
|
+
"expires_in must be positive (> 0)",
|
|
158
|
+
));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let passphrase_str = resolve_passphrase(passphrase);
|
|
162
|
+
let env_config = make_env_config(&passphrase_str, &repo_path);
|
|
163
|
+
let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
|
|
164
|
+
let clock = Arc::new(SystemClock);
|
|
165
|
+
|
|
166
|
+
let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
|
|
167
|
+
let backend = open_backend(&repo)?;
|
|
168
|
+
|
|
169
|
+
let keychain = get_platform_keychain_with_config(&env_config)
|
|
170
|
+
.map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
|
|
171
|
+
let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
|
|
172
|
+
Arc::from(keychain);
|
|
173
|
+
|
|
174
|
+
let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
|
|
175
|
+
let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
|
|
176
|
+
|
|
177
|
+
let alias = resolve_key_alias(&identity_key_alias, keychain.as_ref())?;
|
|
178
|
+
|
|
179
|
+
let ext_config = DeviceExtensionConfig {
|
|
180
|
+
repo_path: repo,
|
|
181
|
+
device_did: DeviceDID::parse(&device_did)
|
|
182
|
+
.map_err(|e| format_error("AUTHS_INVALID_INPUT", e))?,
|
|
183
|
+
expires_in: expires_in as u64,
|
|
184
|
+
identity_key_alias: alias,
|
|
185
|
+
device_key_alias: None,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
let ctx = AuthsContext::builder()
|
|
189
|
+
.registry(backend)
|
|
190
|
+
.key_storage(keychain)
|
|
191
|
+
.clock(clock.clone())
|
|
192
|
+
.identity_storage(identity_storage)
|
|
193
|
+
.attestation_sink(attestation_storage.clone())
|
|
194
|
+
.attestation_source(attestation_storage)
|
|
195
|
+
.passphrase_provider(provider)
|
|
196
|
+
.build();
|
|
197
|
+
|
|
198
|
+
let result = extend_device(ext_config, &ctx, clock.as_ref()).map_err(|e| {
|
|
199
|
+
format_error(
|
|
200
|
+
"AUTHS_DEVICE_ERROR",
|
|
201
|
+
format!("Device extension failed: {e}"),
|
|
202
|
+
)
|
|
203
|
+
})?;
|
|
204
|
+
|
|
205
|
+
Ok(NapiExtensionResult {
|
|
206
|
+
device_did: result.device_did.to_string(),
|
|
207
|
+
new_expires_at: result.new_expires_at.to_rfc3339(),
|
|
208
|
+
previous_expires_at: result
|
|
209
|
+
.previous_expires_at
|
|
210
|
+
.map(|t: chrono::DateTime<chrono::Utc>| t.to_rfc3339()),
|
|
211
|
+
})
|
|
212
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
use std::process::Command;
|
|
2
|
+
|
|
3
|
+
use auths_sdk::ports::diagnostics::{
|
|
4
|
+
CheckCategory, CheckResult, CryptoDiagnosticProvider, DiagnosticError, GitDiagnosticProvider,
|
|
5
|
+
};
|
|
6
|
+
use auths_sdk::workflows::diagnostics::DiagnosticsWorkflow;
|
|
7
|
+
use napi_derive::napi;
|
|
8
|
+
|
|
9
|
+
use crate::error::format_error;
|
|
10
|
+
|
|
11
|
+
struct FfiDiagnosticAdapter;
|
|
12
|
+
|
|
13
|
+
impl GitDiagnosticProvider for FfiDiagnosticAdapter {
|
|
14
|
+
fn check_git_version(&self) -> Result<CheckResult, DiagnosticError> {
|
|
15
|
+
let output = Command::new("git").arg("--version").output();
|
|
16
|
+
let (passed, message) = match output {
|
|
17
|
+
Ok(out) if out.status.success() => {
|
|
18
|
+
let version = String::from_utf8_lossy(&out.stdout).trim().to_string();
|
|
19
|
+
(true, Some(version))
|
|
20
|
+
}
|
|
21
|
+
_ => (false, Some("git command not found on PATH".to_string())),
|
|
22
|
+
};
|
|
23
|
+
Ok(CheckResult {
|
|
24
|
+
name: "Git installed".to_string(),
|
|
25
|
+
passed,
|
|
26
|
+
message,
|
|
27
|
+
config_issues: vec![],
|
|
28
|
+
category: CheckCategory::Advisory,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fn get_git_config(&self, key: &str) -> Result<Option<String>, DiagnosticError> {
|
|
33
|
+
let output = Command::new("git")
|
|
34
|
+
.args(["config", "--global", "--get", key])
|
|
35
|
+
.output()
|
|
36
|
+
.map_err(|e| DiagnosticError::ExecutionFailed(e.to_string()))?;
|
|
37
|
+
|
|
38
|
+
if output.status.success() {
|
|
39
|
+
Ok(String::from_utf8(output.stdout)
|
|
40
|
+
.ok()
|
|
41
|
+
.map(|s| s.trim().to_string()))
|
|
42
|
+
} else {
|
|
43
|
+
Ok(None)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
impl CryptoDiagnosticProvider for FfiDiagnosticAdapter {
|
|
49
|
+
fn check_ssh_keygen_available(&self) -> Result<CheckResult, DiagnosticError> {
|
|
50
|
+
let output = Command::new("ssh-keygen").arg("-V").output();
|
|
51
|
+
let (passed, message) = match output {
|
|
52
|
+
Ok(out) if out.status.success() => (true, Some("ssh-keygen found on PATH".to_string())),
|
|
53
|
+
_ => (
|
|
54
|
+
false,
|
|
55
|
+
Some("ssh-keygen command not found on PATH".to_string()),
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
Ok(CheckResult {
|
|
59
|
+
name: "ssh-keygen installed".to_string(),
|
|
60
|
+
passed,
|
|
61
|
+
message,
|
|
62
|
+
config_issues: vec![],
|
|
63
|
+
category: CheckCategory::Advisory,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#[napi]
|
|
69
|
+
pub fn run_diagnostics(repo_path: String, passphrase: Option<String>) -> napi::Result<String> {
|
|
70
|
+
let _repo = repo_path;
|
|
71
|
+
let _passphrase = passphrase;
|
|
72
|
+
|
|
73
|
+
let adapter = FfiDiagnosticAdapter;
|
|
74
|
+
let workflow = DiagnosticsWorkflow::new(&adapter, &adapter);
|
|
75
|
+
let report = workflow
|
|
76
|
+
.run()
|
|
77
|
+
.map_err(|e| format_error("AUTHS_DIAGNOSTIC_ERROR", e))?;
|
|
78
|
+
|
|
79
|
+
let all_passed = report.checks.iter().all(|c| c.passed);
|
|
80
|
+
|
|
81
|
+
let checks: Vec<serde_json::Value> = report
|
|
82
|
+
.checks
|
|
83
|
+
.iter()
|
|
84
|
+
.map(|c| {
|
|
85
|
+
let fix_hint = if !c.passed {
|
|
86
|
+
Some("Run: auths init --profile developer")
|
|
87
|
+
} else {
|
|
88
|
+
None
|
|
89
|
+
};
|
|
90
|
+
serde_json::json!({
|
|
91
|
+
"name": c.name,
|
|
92
|
+
"passed": c.passed,
|
|
93
|
+
"message": c.message,
|
|
94
|
+
"fix_hint": fix_hint,
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
.collect();
|
|
98
|
+
|
|
99
|
+
let result = serde_json::json!({
|
|
100
|
+
"checks": checks,
|
|
101
|
+
"all_passed": all_passed,
|
|
102
|
+
"version": env!("CARGO_PKG_VERSION"),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
serde_json::to_string(&result).map_err(|e| format_error("AUTHS_DIAGNOSTIC_ERROR", e))
|
|
106
|
+
}
|
package/src/error.rs
ADDED
package/src/helpers.rs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
use std::path::PathBuf;
|
|
2
|
+
|
|
3
|
+
use auths_core::config::{EnvironmentConfig, KeychainConfig};
|
|
4
|
+
use auths_core::storage::keychain::{
|
|
5
|
+
IdentityDID, KeyAlias, KeyRole, KeyStorage, get_platform_keychain_with_config,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
use crate::error::format_error;
|
|
9
|
+
|
|
10
|
+
#[allow(clippy::disallowed_methods)] // Presentation boundary: env var read is intentional
|
|
11
|
+
pub fn resolve_passphrase(passphrase: Option<String>) -> String {
|
|
12
|
+
passphrase.unwrap_or_else(|| std::env::var("AUTHS_PASSPHRASE").unwrap_or_default())
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#[allow(clippy::disallowed_methods)] // Presentation boundary: env var read is intentional
|
|
16
|
+
pub fn resolve_repo_path(path: Option<String>) -> PathBuf {
|
|
17
|
+
let raw = path
|
|
18
|
+
.unwrap_or_else(|| std::env::var("AUTHS_HOME").unwrap_or_else(|_| "~/.auths".to_string()));
|
|
19
|
+
let expanded = shellexpand::tilde(&raw);
|
|
20
|
+
PathBuf::from(expanded.as_ref())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub fn make_env_config(passphrase: &str, repo_path: &str) -> EnvironmentConfig {
|
|
24
|
+
let mut keychain = KeychainConfig::from_env();
|
|
25
|
+
if keychain.backend.is_none() {
|
|
26
|
+
keychain.backend = Some("file".to_string());
|
|
27
|
+
}
|
|
28
|
+
keychain.passphrase = Some(passphrase.to_string());
|
|
29
|
+
EnvironmentConfig {
|
|
30
|
+
auths_home: Some(repo_path.into()),
|
|
31
|
+
keychain,
|
|
32
|
+
ssh_agent_socket: None,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn get_keychain(config: &EnvironmentConfig) -> napi::Result<Box<dyn KeyStorage + Send + Sync>> {
|
|
37
|
+
get_platform_keychain_with_config(config).map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", e))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn resolve_key_alias(
|
|
41
|
+
identity_ref: &str,
|
|
42
|
+
keychain: &(dyn KeyStorage + Send + Sync),
|
|
43
|
+
) -> napi::Result<KeyAlias> {
|
|
44
|
+
if identity_ref.starts_with("did:") {
|
|
45
|
+
let did =
|
|
46
|
+
IdentityDID::parse(identity_ref).map_err(|e| format_error("AUTHS_INVALID_INPUT", e))?;
|
|
47
|
+
let aliases = keychain
|
|
48
|
+
.list_aliases_for_identity_with_role(&did, KeyRole::Primary)
|
|
49
|
+
.map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Key lookup failed: {e}")))?;
|
|
50
|
+
aliases.into_iter().next().ok_or_else(|| {
|
|
51
|
+
format_error(
|
|
52
|
+
"AUTHS_KEY_NOT_FOUND",
|
|
53
|
+
format!("No primary key found for identity '{identity_ref}'"),
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
} else {
|
|
57
|
+
KeyAlias::new(identity_ref)
|
|
58
|
+
.map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Invalid key alias: {e}")))
|
|
59
|
+
}
|
|
60
|
+
}
|