Skip to content

Commit

Permalink
identity: created initial did:web server (#123)
Browse files Browse the repository at this point in the history
* Adds create POST method (accepts pub key, redirects to new URL)
* Adds read GET method (retrieves JWK from user's url)
* Handles serialization of JWK
* Testable/mockable UUID generation.

Known issues:
* Not actually compliant with did:web yet. We are supposed to return a
did:document, but right now we return a Json Web Key
* We don't do any auth on the endpoints yet
* We don't use any persistent storage (no db)
* We totally fake the returned pub key. I.e. we actually don't use the
pub key from the POST at all yet.
  • Loading branch information
TheButlah authored Aug 8, 2024
1 parent 79d54b0 commit e5aba14
Show file tree
Hide file tree
Showing 7 changed files with 366 additions and 0 deletions.
57 changes: 57 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
"apps/identity_server",
"apps/legacy_web/backend",
"apps/legacy_web/frontend",
"apps/networked_physics_demo/client",
Expand Down Expand Up @@ -29,6 +30,7 @@ rust-version = "1.78.0"

[workspace.dependencies]
async-compat = "0.2.4"
axum = "0.7.5"
base64 = "0.21.7"
bevy = { version = "0.13", features = ["serialize"] }
bevy-inspector-egui = "0.23.4"
Expand All @@ -47,15 +49,19 @@ bevy_web_asset = { git = "https://github.com/Schmarni-Dev/bevy_web_asset", rev =
bytes = "1.5.0"
clap = { version = "4.4.11", features = ["derive"] }
color-eyre = "0.6"
did-simple.path = "crates/did-simple"
egui = "0.26"
egui-picking = { path = "crates/egui-picking" }
eyre = "0.6"
futures = "0.3.30"
hex-literal = "0.4.1"
jose-jwk = { version = "0.1.2", default-features = false }
lightyear = "0.12"
openxr = "0.18"
picking-xr = { path = "crates/picking-xr" }
pin-project = "1"
rand = "0.8.5"
rand_chacha = "0.3.1"
rand_xoshiro = "0.6.0"
random-number = "0.1.8"
replicate-client.path = "crates/replicate/client"
Expand All @@ -69,6 +75,7 @@ thiserror = "1.0.56"
tokio = { version = "1.35.1", default-features = false }
tokio-serde = "0.9"
tokio-util = { version = "0.7.10", default-features = true }
tower-http = "0.5.2"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = "2.5.0"
Expand Down
28 changes: 28 additions & 0 deletions apps/identity_server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "identity_server"
version.workspace = true
license.workspace = true
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "Self-custodial identity using did:web"
publish = false

[dependencies]
axum.workspace = true
clap.workspace = true
color-eyre.workspace = true
did-simple.workspace = true
jose-jwk = { workspace = true, default-features = false }
rand.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["full"] }
tower-http = { workspace = true, features = ["trace"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing.workspace = true
uuid = { workspace = true, features = ["std", "v4", "serde"] }

[dev-dependencies]
base64.workspace = true
hex-literal.workspace = true
21 changes: 21 additions & 0 deletions apps/identity_server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
mod uuid;
pub mod v1;

use axum::routing::get;
use tower_http::trace::TraceLayer;

/// Main router of API
pub fn router() -> axum::Router<()> {
let v1_router = crate::v1::RouterConfig {
..Default::default()
}
.build();
axum::Router::new()
.route("/", get(root))
.nest("/api/v1", v1_router)
.layer(TraceLayer::new_for_http())
}

async fn root() -> &'static str {
"uwu hewwo this api is under constwuction"
}
33 changes: 33 additions & 0 deletions apps/identity_server/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::net::{Ipv6Addr, SocketAddr};

use clap::Parser as _;
use tracing::info;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};

#[derive(clap::Parser, Debug)]
struct Cli {
#[clap(default_value = "0")]
port: u16,
}

#[tokio::main]
async fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env().unwrap_or("info".into()))
.with(tracing_subscriber::fmt::layer())
.init();

let cli = Cli::parse();

let listener = tokio::net::TcpListener::bind(SocketAddr::new(
Ipv6Addr::UNSPECIFIED.into(),
cli.port,
))
.await
.unwrap();
info!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, identity_server::router())
.await
.map_err(|e| e.into())
}
104 changes: 104 additions & 0 deletions apps/identity_server/src/uuid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//! Mockable UUID generation.
use ::uuid::Uuid;
use std::sync::atomic::{AtomicUsize, Ordering};

/// Handles generation of UUIDs. This is used instead of the uuid crate directly,
/// to better support deterministic UUID creation in tests.
#[derive(Debug)]
pub struct UuidProvider {
#[cfg(not(test))]
provider: ThreadLocalRng,
#[cfg(test)]
provider: Box<dyn UuidProviderT>,
}

impl UuidProvider {
#[allow(dead_code)]
pub fn new_thread_local() -> Self {
Self {
#[cfg(test)]
provider: Box::new(ThreadLocalRng),
#[cfg(not(test))]
provider: ThreadLocalRng,
}
}

/// Allows controlling the sequence of generated UUIDs. Only available in
/// `cfg(test)`.
#[allow(dead_code)]
#[cfg(test)]
pub fn new_from_sequence(uuids: Vec<Uuid>) -> Self {
Self {
provider: Box::new(TestSequence::new(uuids)),
}
}

#[inline]
pub fn next_v4(&self) -> Uuid {
self.provider.next_v4()
}
}

impl Default for UuidProvider {
fn default() -> Self {
Self::new_thread_local()
}
}

trait UuidProviderT: std::fmt::Debug + Send + Sync + 'static {
fn next_v4(&self) -> Uuid;
}

#[derive(Debug)]
struct ThreadLocalRng;
impl UuidProviderT for ThreadLocalRng {
fn next_v4(&self) -> Uuid {
Uuid::new_v4()
}
}

/// Provides UUIDs from a known sequence. Useful for tests.
#[derive(Debug)]
struct TestSequence {
uuids: Vec<Uuid>,
pos: AtomicUsize,
}
impl TestSequence {
/// # Panics
/// Panics if len of vec is 0
#[allow(dead_code)]
fn new(uuids: Vec<Uuid>) -> Self {
assert!(!uuids.is_empty());
Self {
uuids,
pos: AtomicUsize::new(0),
}
}
}

impl UuidProviderT for TestSequence {
fn next_v4(&self) -> Uuid {
let curr_pos = self.pos.fetch_add(1, Ordering::SeqCst) % self.uuids.len();
self.uuids[curr_pos]
}
}

fn _assert_bounds(p: UuidProvider) {
fn helper(_p: impl std::fmt::Debug + Send + Sync + 'static) {}
helper(p)
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_sequence_order() {
let uuids: Vec<Uuid> = (0..4).map(|_| Uuid::new_v4()).collect();
let sequence = TestSequence::new(uuids.clone());
for uuid in uuids {
assert_eq!(uuid, sequence.next_v4());
}
}
}
Loading

0 comments on commit e5aba14

Please sign in to comment.