-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
identity: created initial did:web server (#123)
* 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
Showing
7 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} | ||
} |
Oops, something went wrong.