feat(client): add client metadata and wallet visibility support
This commit is contained in:
@@ -110,9 +110,8 @@ async fn dispatch_conn_message(
|
||||
pub async fn start(conn: ClientConnection, mut bi: GrpcBi<ClientRequest, ClientResponse>) {
|
||||
let mut conn = conn;
|
||||
let mut request_tracker = RequestTracker::default();
|
||||
let mut response_id = None;
|
||||
|
||||
match auth::start(&mut conn, &mut bi, &mut request_tracker, &mut response_id).await {
|
||||
match auth::start(&mut conn, &mut bi, &mut request_tracker).await {
|
||||
Ok(_) => {
|
||||
let actor =
|
||||
client::session::ClientSession::spawn(client::session::ClientSession::new(conn));
|
||||
@@ -125,11 +124,7 @@ pub async fn start(conn: ClientConnection, mut bi: GrpcBi<ClientRequest, ClientR
|
||||
dispatch_loop(bi, actor, request_tracker).await;
|
||||
}
|
||||
Err(e) => {
|
||||
let mut transport = auth::AuthTransportAdapter::new(
|
||||
&mut bi,
|
||||
&mut request_tracker,
|
||||
&mut response_id,
|
||||
);
|
||||
let mut transport = auth::AuthTransportAdapter::new(&mut bi, &mut request_tracker);
|
||||
let _ = transport.send(Err(e.clone())).await;
|
||||
warn!(error = ?e, "Authentication failed");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ use arbiter_proto::{
|
||||
proto::client::{
|
||||
AuthChallenge as ProtoAuthChallenge, AuthChallengeRequest as ProtoAuthChallengeRequest,
|
||||
AuthChallengeSolution as ProtoAuthChallengeSolution, AuthResult as ProtoAuthResult,
|
||||
ClientRequest, ClientResponse, client_request::Payload as ClientRequestPayload,
|
||||
ClientInfo as ProtoClientInfo, ClientRequest, ClientResponse,
|
||||
client_request::Payload as ClientRequestPayload,
|
||||
client_response::Payload as ClientResponsePayload,
|
||||
},
|
||||
transport::{Bi, Error as TransportError, Receiver, Sender, grpc::GrpcBi},
|
||||
@@ -19,19 +20,16 @@ use crate::{
|
||||
pub struct AuthTransportAdapter<'a> {
|
||||
bi: &'a mut GrpcBi<ClientRequest, ClientResponse>,
|
||||
request_tracker: &'a mut RequestTracker,
|
||||
response_id: &'a mut Option<i32>,
|
||||
}
|
||||
|
||||
impl<'a> AuthTransportAdapter<'a> {
|
||||
pub fn new(
|
||||
bi: &'a mut GrpcBi<ClientRequest, ClientResponse>,
|
||||
request_tracker: &'a mut RequestTracker,
|
||||
response_id: &'a mut Option<i32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bi,
|
||||
request_tracker,
|
||||
response_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +70,9 @@ impl<'a> AuthTransportAdapter<'a> {
|
||||
&mut self,
|
||||
payload: ClientResponsePayload,
|
||||
) -> Result<(), TransportError> {
|
||||
let request_id = self.response_id.take();
|
||||
|
||||
self.bi
|
||||
.send(Ok(ClientResponse {
|
||||
request_id,
|
||||
request_id: Some(self.request_tracker.current_request_id()),
|
||||
payload: Some(payload),
|
||||
}))
|
||||
.await
|
||||
@@ -114,19 +110,27 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
let request_id = match self.request_tracker.request(request.request_id) {
|
||||
match self.request_tracker.request(request.request_id) {
|
||||
Ok(request_id) => request_id,
|
||||
Err(error) => {
|
||||
let _ = self.bi.send(Err(error)).await;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
*self.response_id = Some(request_id);
|
||||
|
||||
let payload = request.payload?;
|
||||
|
||||
match payload {
|
||||
ClientRequestPayload::AuthChallengeRequest(ProtoAuthChallengeRequest { pubkey }) => {
|
||||
ClientRequestPayload::AuthChallengeRequest(ProtoAuthChallengeRequest {
|
||||
pubkey,
|
||||
client_info,
|
||||
}) => {
|
||||
let Some(client_info) = client_info else {
|
||||
let _ = self
|
||||
.bi
|
||||
.send(Err(Status::invalid_argument("Missing client info")))
|
||||
.await;
|
||||
return None;
|
||||
};
|
||||
let Ok(pubkey) = <[u8; 32]>::try_from(pubkey) else {
|
||||
let _ = self.send_auth_result(ProtoAuthResult::InvalidKey).await;
|
||||
return None;
|
||||
@@ -135,7 +139,10 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
||||
let _ = self.send_auth_result(ProtoAuthResult::InvalidKey).await;
|
||||
return None;
|
||||
};
|
||||
Some(auth::Inbound::AuthChallengeRequest { pubkey })
|
||||
Some(auth::Inbound::AuthChallengeRequest {
|
||||
pubkey,
|
||||
metadata: client_metadata_from_proto(client_info),
|
||||
})
|
||||
}
|
||||
ClientRequestPayload::AuthChallengeSolution(ProtoAuthChallengeSolution {
|
||||
signature,
|
||||
@@ -151,7 +158,9 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
||||
_ => {
|
||||
let _ = self
|
||||
.bi
|
||||
.send(Err(Status::invalid_argument("Unsupported client auth request")))
|
||||
.send(Err(Status::invalid_argument(
|
||||
"Unsupported client auth request",
|
||||
)))
|
||||
.await;
|
||||
None
|
||||
}
|
||||
@@ -161,13 +170,20 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
||||
|
||||
impl Bi<auth::Inbound, Result<auth::Outbound, auth::Error>> for AuthTransportAdapter<'_> {}
|
||||
|
||||
fn client_metadata_from_proto(metadata: ProtoClientInfo) -> auth::ClientMetadata {
|
||||
auth::ClientMetadata {
|
||||
name: metadata.name,
|
||||
description: (!metadata.description.is_empty()).then_some(metadata.description),
|
||||
version: (!metadata.version.is_empty()).then_some(metadata.version),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(
|
||||
conn: &mut ClientConnection,
|
||||
bi: &mut GrpcBi<ClientRequest, ClientResponse>,
|
||||
request_tracker: &mut RequestTracker,
|
||||
response_id: &mut Option<i32>,
|
||||
) -> Result<(), auth::Error> {
|
||||
let mut transport = AuthTransportAdapter::new(bi, request_tracker, response_id);
|
||||
let mut transport = AuthTransportAdapter::new(bi, request_tracker);
|
||||
client::auth::authenticate(conn, &mut transport).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,4 +17,10 @@ impl RequestTracker {
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
// This is used to set the response id for auth responses, which need to match the request id of the auth challenge request.
|
||||
// -1 offset is needed because request() increments the next_request_id after returning the current request id.
|
||||
pub fn current_request_id(&self) -> i32 {
|
||||
self.next_request_id - 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,11 +241,7 @@ async fn dispatch_conn_message(
|
||||
UserAgentRequestPayload::EvmGrantList(_) => UserAgentResponsePayload::EvmGrantList(
|
||||
EvmGrantOrWallet::grant_list_response(actor.ask(HandleGrantList {}).await),
|
||||
),
|
||||
UserAgentRequestPayload::EvmGrantCreate(EvmGrantCreateRequest {
|
||||
client_id,
|
||||
shared,
|
||||
specific,
|
||||
}) => {
|
||||
UserAgentRequestPayload::EvmGrantCreate(EvmGrantCreateRequest { shared, specific }) => {
|
||||
let (basic, grant) = match parse_grant_request(shared, specific) {
|
||||
Ok(values) => values,
|
||||
Err(status) => {
|
||||
@@ -255,13 +251,7 @@ async fn dispatch_conn_message(
|
||||
};
|
||||
|
||||
UserAgentResponsePayload::EvmGrantCreate(EvmGrantOrWallet::grant_create_response(
|
||||
actor
|
||||
.ask(HandleGrantCreate {
|
||||
client_id,
|
||||
basic,
|
||||
grant,
|
||||
})
|
||||
.await,
|
||||
actor.ask(HandleGrantCreate { basic, grant }).await,
|
||||
))
|
||||
}
|
||||
UserAgentRequestPayload::EvmGrantDelete(EvmGrantDeleteRequest { grant_id }) => {
|
||||
@@ -296,6 +286,7 @@ async fn send_out_of_band(
|
||||
OutOfBand::ClientConnectionRequest { pubkey } => {
|
||||
UserAgentResponsePayload::ClientConnectionRequest(ClientConnectionRequest {
|
||||
pubkey: pubkey.to_bytes().to_vec(),
|
||||
info: None,
|
||||
})
|
||||
}
|
||||
OutOfBand::ClientConnectionCancel => {
|
||||
@@ -327,8 +318,7 @@ fn parse_grant_request(
|
||||
|
||||
fn shared_settings_from_proto(shared: ProtoSharedSettings) -> Result<SharedGrantSettings, Status> {
|
||||
Ok(SharedGrantSettings {
|
||||
wallet_id: shared.wallet_id,
|
||||
client_id: 0,
|
||||
visibility_id: shared.visibility_id,
|
||||
chain: shared.chain_id,
|
||||
valid_from: shared.valid_from.map(proto_timestamp_to_utc).transpose()?,
|
||||
valid_until: shared.valid_until.map(proto_timestamp_to_utc).transpose()?,
|
||||
@@ -412,7 +402,7 @@ fn proto_timestamp_to_utc(
|
||||
|
||||
fn shared_settings_to_proto(shared: SharedGrantSettings) -> ProtoSharedSettings {
|
||||
ProtoSharedSettings {
|
||||
wallet_id: shared.wallet_id,
|
||||
visibility_id: shared.visibility_id,
|
||||
chain_id: shared.chain,
|
||||
valid_from: shared.valid_from.map(|time| prost_types::Timestamp {
|
||||
seconds: time.timestamp(),
|
||||
@@ -552,7 +542,7 @@ impl EvmGrantOrWallet {
|
||||
.into_iter()
|
||||
.map(|grant| GrantEntry {
|
||||
id: grant.id,
|
||||
client_id: grant.shared.client_id,
|
||||
visibility_id: grant.shared.visibility_id,
|
||||
shared: Some(shared_settings_to_proto(grant.shared)),
|
||||
specific: Some(specific_grant_to_proto(grant.settings)),
|
||||
})
|
||||
@@ -575,15 +565,8 @@ pub async fn start(
|
||||
mut bi: GrpcBi<UserAgentRequest, UserAgentResponse>,
|
||||
) {
|
||||
let mut request_tracker = RequestTracker::default();
|
||||
let mut response_id = None;
|
||||
|
||||
let pubkey = match auth::start(
|
||||
&mut conn,
|
||||
&mut bi,
|
||||
&mut request_tracker,
|
||||
&mut response_id,
|
||||
)
|
||||
.await
|
||||
let pubkey = match auth::start(&mut conn, &mut bi, &mut request_tracker).await
|
||||
{
|
||||
Ok(pubkey) => pubkey,
|
||||
Err(e) => {
|
||||
|
||||
@@ -21,19 +21,16 @@ use crate::{
|
||||
pub struct AuthTransportAdapter<'a> {
|
||||
bi: &'a mut GrpcBi<UserAgentRequest, UserAgentResponse>,
|
||||
request_tracker: &'a mut RequestTracker,
|
||||
response_id: &'a mut Option<i32>,
|
||||
}
|
||||
|
||||
impl<'a> AuthTransportAdapter<'a> {
|
||||
pub fn new(
|
||||
bi: &'a mut GrpcBi<UserAgentRequest, UserAgentResponse>,
|
||||
request_tracker: &'a mut RequestTracker,
|
||||
response_id: &'a mut Option<i32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bi,
|
||||
request_tracker,
|
||||
response_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +38,9 @@ impl<'a> AuthTransportAdapter<'a> {
|
||||
&mut self,
|
||||
payload: UserAgentResponsePayload,
|
||||
) -> Result<(), TransportError> {
|
||||
let id = self.response_id.take();
|
||||
|
||||
self.bi
|
||||
.send(Ok(UserAgentResponse {
|
||||
id,
|
||||
id: Some(self.request_tracker.current_request_id()),
|
||||
payload: Some(payload),
|
||||
}))
|
||||
.await
|
||||
@@ -75,9 +70,14 @@ impl Sender<Result<auth::Outbound, auth::Error>> for AuthTransportAdapter<'_> {
|
||||
Err(Error::InvalidBootstrapToken) => {
|
||||
UserAgentResponsePayload::AuthResult(ProtoAuthResult::TokenInvalid.into())
|
||||
}
|
||||
Err(Error::Internal { details }) => return self.bi.send(Err(Status::internal(details))).await,
|
||||
Err(Error::Internal { details }) => {
|
||||
return self.bi.send(Err(Status::internal(details))).await;
|
||||
}
|
||||
Err(Error::Transport) => {
|
||||
return self.bi.send(Err(Status::unavailable("transport error"))).await;
|
||||
return self
|
||||
.bi
|
||||
.send(Err(Status::unavailable("transport error")))
|
||||
.await;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,14 +96,13 @@ impl Receiver<auth::Inbound> for AuthTransportAdapter<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
let request_id = match self.request_tracker.request(request.id) {
|
||||
match self.request_tracker.request(request.id) {
|
||||
Ok(request_id) => request_id,
|
||||
Err(error) => {
|
||||
let _ = self.bi.send(Err(error)).await;
|
||||
return None;
|
||||
}
|
||||
};
|
||||
*self.response_id = Some(request_id);
|
||||
|
||||
let Some(payload) = request.payload else {
|
||||
warn!(
|
||||
@@ -173,8 +172,7 @@ pub async fn start(
|
||||
conn: &mut UserAgentConnection,
|
||||
bi: &mut GrpcBi<UserAgentRequest, UserAgentResponse>,
|
||||
request_tracker: &mut RequestTracker,
|
||||
response_id: &mut Option<i32>,
|
||||
) -> Result<AuthPublicKey, auth::Error> {
|
||||
let transport = AuthTransportAdapter::new(bi, request_tracker, response_id);
|
||||
let transport = AuthTransportAdapter::new(bi, request_tracker);
|
||||
auth::authenticate(conn, transport).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user