Skip to main content

auth_service/
lib.rs

1//! Auth service: campaigns, the devices admitted to them, and short-lived
2//! tokens for the other services. See `edda-auth` for the cryptography.
3//! Several instances can run against the same database.
4
5/// Errors, as responses.
6/// What whoever runs the service asks of it over HTTP.
7pub mod admin;
8pub mod error;
9pub mod pairing;
10/// Setting up campaigns, logging in, and devices.
11pub mod routes;
12pub mod store;
13
14use std::sync::Arc;
15
16use axum::{Extension, Router};
17use edda_auth::{
18    DeviceKey,
19    server::{TokenSigner, TokenVerifier},
20};
21use edda_server::{Limits, openapi::Conventions};
22use sqlx::PgPool;
23
24pub use store::Store;
25
26/// The service's `OpenAPI` description.
27#[derive(utoipa::OpenApi)]
28#[openapi(
29    paths(
30        edda_server::health,
31        edda_server::ready,
32        routes::public_key,
33        routes::lookup_setup_code,
34        routes::setup,
35        routes::challenge,
36        routes::login,
37        routes::campaign,
38        routes::devices,
39        routes::add_device,
40        routes::revoke,
41        pairing::open,
42        pairing::cancel,
43        pairing::send_as_host,
44        pairing::described::receive_from_host,
45        pairing::send_as_joiner,
46        pairing::described::receive_from_joiner,
47    ),
48    modifiers(&Conventions),
49    tags(
50        (name = "auth", description = "Setting up a campaign's first DM device, and logging in."),
51        (name = "devices", description = "A campaign's devices: DM devices add and revoke them."),
52        (name = "pairing", description = "Relaying the messages that pair a new device with a DM's, by a code read out between them."),
53    )
54)]
55pub struct ApiDoc;
56
57/// Connects to the database, creating it if needed, and applies migrations.
58pub async fn connect(database_url: &str) -> Result<PgPool, sqlx::Error> {
59    edda_server::open_database(database_url, &sqlx::migrate!()).await
60}
61
62/// The service, within `limits` (its campaigns' devices, and how often
63/// clients may try to sign in), signing tokens with `token_key`, or with
64/// the key kept in its database if `None`.
65pub async fn app(
66    pool: PgPool,
67    token_key: Option<DeviceKey>,
68    limits: Limits,
69) -> Result<Router, error::ApiError> {
70    let ready = edda_server::readiness(pool.clone());
71    let store = Store::new(pool);
72    let token_key = match token_key {
73        Some(key) => key,
74        None => store.token_key().await?,
75    };
76    let signer = TokenSigner::new(&token_key);
77    let verifier = Arc::new(TokenVerifier::new(signer.public_key()));
78    Ok(Router::new()
79        .merge(routes::router(&limits))
80        .merge(pairing::router(&limits))
81        .with_state(Arc::new(routes::AppState {
82            store,
83            signer,
84            limits,
85        }))
86        .layer(Extension(verifier))
87        .merge(ready))
88}