Skip to content

Commit

Permalink
replicate: use bearer token instead of did
Browse files Browse the repository at this point in the history
  • Loading branch information
TheButlah committed Jun 16, 2024
1 parent a818e1c commit 8fe1b9a
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 202 deletions.
17 changes: 5 additions & 12 deletions crates/replicate/client/examples/example-client.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use clap::Parser;
use color_eyre::{eyre::WrapErr, Result};
use replicate_client::{instance::Instance, manager::Manager};
use replicate_common::{
data_model::State,
did::{AuthenticationAttestation, Did, DidPrivateKey},
};
use replicate_common::data_model::State;
use tracing::info;
use tracing_subscriber::{filter::LevelFilter, EnvFilter};
use url::Url;
Expand All @@ -14,8 +11,9 @@ use url::Url;
pub struct Args {
#[clap(long)]
url: Url,
/// Optional bearer token to use for authenticating.
#[clap(long)]
username: String,
token: Option<String>,
}

#[tokio::main]
Expand All @@ -34,12 +32,7 @@ async fn main() -> Result<()> {

let args = Args::parse();

let did = Did(args.username);
let did_private_key = DidPrivateKey;

let auth_attest = AuthenticationAttestation::new(did, &did_private_key);

let mut manager = Manager::connect(args.url, &auth_attest)
let mut manager = Manager::connect(args.url, args.token.as_deref())
.await
.wrap_err("failed to connect to manager")?;
info!("Connected to manager!");
Expand All @@ -55,7 +48,7 @@ async fn main() -> Result<()> {
.wrap_err("failed to get instance url")?;
info!("Got instance {instance_id} at: {instance_url}");

let mut instance = Instance::connect(instance_url, auth_attest)
let mut instance = Instance::connect(instance_url, args.token.as_deref())
.await
.wrap_err("failed to connect to instance")?;
info!("Connected to instance!");
Expand Down
69 changes: 11 additions & 58 deletions crates/replicate/client/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@
//! disconnects and reconnects. The client must prove ownership of this unique
//! identifier.
//!
//! The most logical implementation strategy to accomplish this is to solve it the same
//! way that the nexus protocol does - every user is identified by a
//! [Decentralized Identifier][DID][^1], and they sign a message with the DID's associated
//! private key, proving that they are who they say they are. This is done via the
//! [`AuthenticationAttestation`] argument when connecting to the instance.
//!
//! # Entities
//! Each entity has state, and the instance has many entities. An entity is identified
//! with an id, which the server assigns in response to a request to spawn an entity
Expand Down Expand Up @@ -79,18 +73,14 @@
//! [did:web]: https://w3c-ccg.github.io/did-method-web/
//! [ABA]: https://en.wikipedia.org/wiki/ABA_problem

use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use eyre::{bail, ensure, Result, WrapErr};
use futures::{SinkExt, StreamExt};
use replicate_common::{
data_model::{DataModel, Entity, LocalChanges, RemoteChanges, State},
did::AuthenticationAttestation,
use replicate_common::data_model::{
DataModel, Entity, LocalChanges, RemoteChanges, State,
};
use tracing::warn;
use url::Url;
use wtransport::{endpoint::ConnectOptions, ClientConfig, Endpoint};

use crate::CertHashDecodeErr;
use crate::{connect_to_url, Ascii};

use replicate_common::messages::instance::{Clientbound as Cb, Serverbound as Sb};
type RpcFramed = replicate_common::Framed<wtransport::stream::BiStream, Cb, Sb>;
Expand Down Expand Up @@ -118,13 +108,14 @@ impl Instance {
/// # Arguments
/// - `url`: Url of the manager api. For example, `https://foobar.com/my/manager`
/// or `192.168.1.1:1337/uwu/some_manager`.
/// - `auth_attest`: Used to provide to the server proof of our identity, based on
/// our DID.
pub async fn connect(
url: Url,
auth_attest: AuthenticationAttestation,
) -> Result<Self> {
let conn = connect_to_url(&url, auth_attest)
/// - `bearer_token`: optional, must be ascii otherwise we will panic.
pub async fn connect(url: Url, bearer_token: Option<&str>) -> Result<Self> {
let bearer_token = bearer_token.map(|s| {
// Technically, bearer tokens are actually only permit a *subset* of ascii
// characters, but I didn't care enough to be that precise.
Ascii::try_from(s).expect("to be in-spec, bearer tokens must be ascii")
});
let conn = connect_to_url(&url, bearer_token)
.await
.wrap_err("failed to connect to server")?;

Expand Down Expand Up @@ -196,41 +187,3 @@ pub enum RecvState<'a> {
/// Sequence number for state messages
#[derive(Debug, Default)]
pub struct StateSeq;

async fn connect_to_url(
url: &Url,
auth_attest: AuthenticationAttestation,
) -> Result<wtransport::Connection> {
let cert_hash = if let Some(frag) = url.fragment() {
let cert_hash = BASE64_URL_SAFE_NO_PAD
.decode(frag)
.map_err(CertHashDecodeErr::from)?;
let len = cert_hash.len();
let cert_hash: [u8; 32] = cert_hash
.try_into()
.map_err(|_| CertHashDecodeErr::InvalidLen(len))?;
Some(cert_hash)
} else {
None
};

let cfg = ClientConfig::builder().with_bind_default();
let cfg = if let Some(_cert_hash) = cert_hash {
// TODO: Implement self signed certs properly:
// https://github.com/BiagioFesta/wtransport/issues/128
warn!(
"`serverCertificateHashes` is not yet supported, turning off \
cert validation."
);
cfg.with_no_cert_validation()
} else {
cfg.with_native_certs()
}
.build();

let client = Endpoint::client(cfg)?;
let opts = ConnectOptions::builder(url)
.add_header("Authorization", format!("Bearer {}", auth_attest))
.build();
Ok(client.connect(opts).await?)
}
63 changes: 63 additions & 0 deletions crates/replicate/client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine as _};
use eyre::Result;
use tracing::warn;
use url::Url;

pub use replicate_common as common;
use wtransport::{endpoint::ConnectOptions, ClientConfig, Endpoint};

pub mod instance;
pub mod manager;
Expand All @@ -11,3 +17,60 @@ pub enum CertHashDecodeErr {
#[error("expected length of 32, got length of {0}")]
InvalidLen(usize),
}

/// A string that has been validated to be ascii.
struct Ascii<'a>(&'a str);

impl<'a> TryFrom<&'a str> for Ascii<'a> {
type Error = ();

fn try_from(value: &'a str) -> std::prelude::v1::Result<Self, Self::Error> {
if value.is_ascii() {
Ok(Self(value))
} else {
Err(())
}
}
}

/// If there is a url fragment, it will be treated as a server certificate hash.
async fn connect_to_url(
url: &Url,
bearer_token: Option<Ascii<'_>>,
) -> Result<wtransport::Connection> {
let cert_hash = if let Some(frag) = url.fragment() {
let cert_hash = BASE64_URL_SAFE_NO_PAD
.decode(frag)
.map_err(CertHashDecodeErr::from)?;
let len = cert_hash.len();
let cert_hash: [u8; 32] = cert_hash
.try_into()
.map_err(|_| CertHashDecodeErr::InvalidLen(len))?;
Some(cert_hash)
} else {
None
};

let cfg = ClientConfig::builder().with_bind_default();
let cfg = if let Some(_cert_hash) = cert_hash {
// TODO: Implement self signed certs properly:
// https://github.com/BiagioFesta/wtransport/issues/128
warn!(
"`serverCertificateHashes` is not yet supported, turning off \
cert validation."
);
cfg.with_no_cert_validation()
} else {
cfg.with_native_certs()
}
.build();

let client = Endpoint::client(cfg)?;
let opts = ConnectOptions::builder(url);
let opts = if let Some(b) = bearer_token {
opts.add_header("Authorization", format!("Bearer {}", b.0))
} else {
opts
};
Ok(client.connect(opts.build()).await?)
}
53 changes: 10 additions & 43 deletions crates/replicate/client/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@

use std::fmt::Debug;

use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use eyre::{bail, ensure, eyre, Context};
use futures::sink::SinkExt;
use futures::stream::StreamExt;
use replicate_common::{
did::AuthenticationAttestation,
messages::manager::{Clientbound as Cb, Serverbound as Sb},
InstanceId,
};
use tracing::warn;
use url::Url;
use wtransport::{endpoint::ConnectOptions, ClientConfig, Endpoint};

use crate::CertHashDecodeErr;
use crate::connect_to_url;
use crate::Ascii;

type Result<T> = eyre::Result<T>;
type Framed = replicate_common::Framed<wtransport::stream::BiStream, Cb, Sb>;
Expand All @@ -37,44 +34,14 @@ impl Manager {
/// # Arguments
/// - `url`: Url of the manager api. For example, `https://foobar.com/my/manager`
/// or `192.168.1.1:1337/uwu/some_manager`.
/// - `auth_attest`: Used to provide to the server proof of our identity, based on
/// our DID.
pub async fn connect(
url: Url,
auth_attest: &AuthenticationAttestation,
) -> Result<Self> {
let cert_hash = if let Some(frag) = url.fragment() {
let cert_hash = BASE64_URL_SAFE_NO_PAD
.decode(frag)
.map_err(CertHashDecodeErr::from)?;
let len = cert_hash.len();
let cert_hash: [u8; 32] = cert_hash
.try_into()
.map_err(|_| CertHashDecodeErr::InvalidLen(len))?;
Some(cert_hash)
} else {
None
};

let cfg = ClientConfig::builder().with_bind_default();
let cfg = if let Some(_cert_hash) = cert_hash {
warn!(
"`serverCertificateHashes` is not yet supported, turning off \
cert validation."
);
// TODO: Use the cert hash as the root cert instead of no validation
cfg.with_no_cert_validation()
} else {
cfg.with_native_certs()
}
.build();

let client = Endpoint::client(cfg)?;
let opts = ConnectOptions::builder(&url)
.add_header("Authorization", format!("Bearer {}", auth_attest))
.build();
let conn = client
.connect(opts)
/// - `bearer_token`: optional, must be ascii otherwise we will panic.
pub async fn connect(url: Url, bearer_token: Option<&str>) -> Result<Self> {
let bearer_token = bearer_token.map(|s| {
// Technically, bearer tokens are actually only permit a *subset* of ascii
// characters, but I didn't care enough to be that precise.
Ascii::try_from(s).expect("to be in-spec, bearer tokens must be ascii")
});
let conn = connect_to_url(&url, bearer_token)
.await
.wrap_err("failed to connect to server")?;
let bi = wtransport::stream::BiStream::join(
Expand Down
88 changes: 0 additions & 88 deletions crates/replicate/common/src/did.rs

This file was deleted.

1 change: 0 additions & 1 deletion crates/replicate/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub mod data_model;
pub mod did;
mod framed;
pub mod messages;

Expand Down

0 comments on commit 8fe1b9a

Please sign in to comment.