(server: &str, identity: &Identity)
| 120 | // --------------------------------------------------------------------------- |
| 121 | |
| 122 | fn mint_token(server: &str, identity: &Identity) -> CliResult<String> { |
| 123 | // The token is keyed by the signer's own public key — no server-assigned |
| 124 | // identifier to look up. For an agent the signer is the agent itself. |
| 125 | let public_key_b32 = identity.public_key_base32(); |
| 126 | |
| 127 | // Load the keypair (needs the secret key to sign). |
| 128 | let store = IdentityStore::open_default() |
| 129 | .map_err(|e| CliError::Internal(anyhow::anyhow!("Failed to open identity store: {e}")))?; |
| 130 | let keypair = store.load_keypair(&identity.id, None).map_err(|e| { |
| 131 | CliError::Internal(anyhow::anyhow!( |
| 132 | "Failed to load keypair for '{}': {e}", |
| 133 | identity.name |
| 134 | )) |
| 135 | })?; |
| 136 | |
| 137 | // An agent acts on behalf of the human who delegated to it, so the subject |
| 138 | // becomes the human and the agent moves into `act`. Resolving the |
| 139 | // certificate here means an unusable delegation is reported before any |
| 140 | // request is made, with the command that fixes it. |
| 141 | let (sub, act, dlg) = if identity.identity_type.is_delegated() { |
| 142 | let resolved = crate::commands::delegation::active_for(&store, identity, Some(server))?; |
| 143 | let delegator_key = atomic_canonical::delegation::delegator_public_key( |
| 144 | &resolved.delegation, |
| 145 | ) |
| 146 | .map_err(|e| CliError::DelegationError { |
| 147 | message: format!("Delegation for '{}' is malformed: {e}", identity.name), |
| 148 | })?; |
| 149 | ( |
| 150 | delegator_key.to_base32(), |
| 151 | Some(Actor { |
| 152 | sub: public_key_b32.clone(), |
| 153 | }), |
| 154 | Some(resolved.delegation.id.to_urn()), |
| 155 | ) |
| 156 | } else { |
| 157 | (public_key_b32.clone(), None, None) |
| 158 | }; |
| 159 | |
| 160 | let now = Utc::now(); |
| 161 | let claims = Claims { |
| 162 | sub, |
| 163 | iat: now.timestamp(), |
| 164 | exp: (now + TOKEN_TTL).timestamp(), |
| 165 | jti: Uuid::new_v4().to_string(), |
| 166 | act, |
| 167 | dlg, |
| 168 | }; |
| 169 | |
| 170 | let header = JwtHeader { |
| 171 | alg: "EdDSA", |
| 172 | typ: "JWT", |
| 173 | kid: public_key_b32, |
| 174 | }; |
| 175 | |
| 176 | let header_json = serde_json::to_vec(&header) |
| 177 | .map_err(|e| CliError::Internal(anyhow::anyhow!("Failed to encode JWT header: {e}")))?; |
| 178 | let claims_json = serde_json::to_vec(&claims) |
| 179 | .map_err(|e| CliError::Internal(anyhow::anyhow!("Failed to encode JWT claims: {e}")))?; |
no test coverage detected