mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
feat(openapi): document bearer auth on the 28 token-gated routes
The spec carried no security scheme, so generated clients/docs could not tell which endpoints require a session token — e.g. /wallet/utxo/balance read as public while its handler enforces auth via extract_user_id_from_token. Add a `bearer_auth` HTTP/JWT scheme through a Modify addon and annotate every route that resolves a session; auth-bootstrap, public username/key lookups, and system routes stay open. Regenerated openapi.json (drift clean).
This commit is contained in:
@@ -857,6 +857,7 @@ pub async fn logout(
|
||||
|
||||
/// The authenticated user's own info.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
get,
|
||||
path = "/auth/me",
|
||||
responses(
|
||||
@@ -1009,6 +1010,7 @@ pub struct NostrLinkResponse {
|
||||
/// shape but refuses every link, because it cannot yet prove the nonce was issued
|
||||
/// by this server and is unused.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/auth/nostr/link",
|
||||
request_body = NostrLinkRequest,
|
||||
|
||||
@@ -14,6 +14,7 @@ use utoipa::OpenApi;
|
||||
version = "0.3.0",
|
||||
description = "Open, self-hostable backend for the Smirk non-custodial multi-chain wallet."
|
||||
),
|
||||
modifiers(&SecurityAddon),
|
||||
paths(
|
||||
crate::api::health::health,
|
||||
crate::api::capabilities::capabilities,
|
||||
@@ -144,3 +145,32 @@ use utoipa::OpenApi;
|
||||
)
|
||||
)]
|
||||
pub struct ApiDoc;
|
||||
|
||||
/// Registers the `bearer_auth` security scheme (HTTP Bearer / JWT). Routes that
|
||||
/// resolve a session via `extract_user_id_from_token` annotate themselves with
|
||||
/// `security(("bearer_auth" = []))`; public routes (auth bootstrap, lookups,
|
||||
/// system) carry no `security` and are documented as open. Without this the
|
||||
/// generated spec/client could not tell which endpoints require a token.
|
||||
struct SecurityAddon;
|
||||
|
||||
impl utoipa::Modify for SecurityAddon {
|
||||
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
||||
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
|
||||
let components = openapi
|
||||
.components
|
||||
.as_mut()
|
||||
.expect("components are registered by the derive");
|
||||
components.add_security_scheme(
|
||||
"bearer_auth",
|
||||
SecurityScheme::Http(
|
||||
HttpBuilder::new()
|
||||
.scheme(HttpAuthScheme::Bearer)
|
||||
.bearer_format("JWT")
|
||||
.description(Some(
|
||||
"Session access token issued by the /auth/* endpoints.",
|
||||
))
|
||||
.build(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@ pub struct UserKeysResponse {
|
||||
/// reserved list before the DB write. The UNIQUE constraint is the atomic claim:
|
||||
/// a collision (taken or reserved) is a 409, never a 500.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/users/me/username",
|
||||
request_body = SetUsernameRequest,
|
||||
@@ -244,6 +245,7 @@ pub async fn set_username(
|
||||
|
||||
/// The authenticated user's own username (or `null`).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
get,
|
||||
path = "/users/me/username",
|
||||
responses(
|
||||
@@ -327,6 +329,7 @@ pub async fn lookup_username(
|
||||
/// Idempotent: the DB upsert keys on `(user_id, asset, key_type)`, so re-posting
|
||||
/// the same asset replaces the stored key material in place.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/keys",
|
||||
request_body = RegisterKeyRequest,
|
||||
|
||||
@@ -166,6 +166,7 @@ pub struct BroadcastResponse {
|
||||
|
||||
/// Confirmed/unconfirmed balance for a BTC/LTC address.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/balance",
|
||||
request_body = AddressRequest,
|
||||
@@ -198,6 +199,7 @@ pub async fn balance(
|
||||
|
||||
/// Unspent outputs for a BTC/LTC address.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/utxos",
|
||||
request_body = AddressRequest,
|
||||
@@ -237,6 +239,7 @@ pub async fn utxos(
|
||||
|
||||
/// Confirmed + mempool transaction history for a BTC/LTC address.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/history",
|
||||
request_body = AddressRequest,
|
||||
@@ -275,6 +278,7 @@ pub async fn history(
|
||||
|
||||
/// Best-chain tip height (for client-side confirmation counting).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/tip",
|
||||
request_body = AssetRequest,
|
||||
@@ -300,6 +304,7 @@ pub async fn tip(
|
||||
|
||||
/// Fee-rate estimate (sat/vB) for confirmation within `blocks` blocks.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/fee",
|
||||
request_body = FeeRequest,
|
||||
@@ -327,6 +332,7 @@ pub async fn fee(
|
||||
|
||||
/// Broadcast a finalized, signed BTC/LTC transaction.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/utxo/broadcast",
|
||||
request_body = BroadcastRequest,
|
||||
|
||||
@@ -103,6 +103,7 @@ fn output_dto(o: ViewWalletOutputResult) -> GrinOutput {
|
||||
|
||||
/// View-only scan for the outputs a `rewind_hash` recognizes.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/scan",
|
||||
request_body = GrinScanRequest,
|
||||
@@ -135,6 +136,7 @@ pub async fn scan(
|
||||
|
||||
/// Current Grin chain tip height (for confirmation counting).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
get,
|
||||
path = "/wallet/grin/height",
|
||||
responses(
|
||||
@@ -157,6 +159,7 @@ pub async fn height(
|
||||
|
||||
/// Broadcast a finalized, signed Grin transaction.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/broadcast",
|
||||
request_body = GrinBroadcastRequest,
|
||||
|
||||
@@ -201,6 +201,7 @@ fn validate_slatepack(value: &str, field: &str) -> Result<(), AppError> {
|
||||
|
||||
/// Post a slatepack addressed to a registered recipient (sender = caller).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/relay/create",
|
||||
request_body = CreateRelayRequest,
|
||||
@@ -252,6 +253,7 @@ pub async fn create(
|
||||
|
||||
/// The caller's inbox: relays awaiting their response.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
get,
|
||||
path = "/wallet/grin/relay/pending",
|
||||
responses(
|
||||
@@ -277,6 +279,7 @@ pub async fn pending(
|
||||
/// Poll a relay's current state (sender or recipient). The sender uses this to
|
||||
/// fetch the recipient's response once status is `pending_sender`.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/relay/get",
|
||||
request_body = SlateIdRequest,
|
||||
@@ -301,6 +304,7 @@ pub async fn get_relay(
|
||||
|
||||
/// Attach the recipient's response, advancing to `pending_sender`.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/relay/respond",
|
||||
request_body = RespondRelayRequest,
|
||||
@@ -339,6 +343,7 @@ pub async fn respond(
|
||||
/// The backend does NOT finalize or broadcast — the wallet does that locally and
|
||||
/// reports the resulting tx hash here.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/relay/finalize",
|
||||
request_body = FinalizeRelayRequest,
|
||||
@@ -377,6 +382,7 @@ pub async fn finalize(
|
||||
|
||||
/// Cancel a relay (sender or recipient), unless already finalized.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/grin/relay/cancel",
|
||||
request_body = SlateIdRequest,
|
||||
|
||||
@@ -274,6 +274,7 @@ fn random_dto(r: RandomOutput) -> RandomOutputDto {
|
||||
/// Register an account (its view key) with the LWS so it begins scanning.
|
||||
/// Idempotent at the LWS; pass `start_height` to scan from a wallet birthday.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/register",
|
||||
request_body = RegisterRequest,
|
||||
@@ -310,6 +311,7 @@ pub async fn register(
|
||||
|
||||
/// Balance + scan state for an account.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/balance",
|
||||
request_body = ViewRequest,
|
||||
@@ -347,6 +349,7 @@ pub async fn balance(
|
||||
|
||||
/// Transaction history (confirmed + mempool) for an account.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/history",
|
||||
request_body = ViewRequest,
|
||||
@@ -379,6 +382,7 @@ pub async fn history(
|
||||
|
||||
/// Unspent outputs for an account (for client-side spend construction).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/unspent_outs",
|
||||
request_body = ViewRequest,
|
||||
@@ -414,6 +418,7 @@ pub async fn unspent_outs(
|
||||
|
||||
/// Random decoy outputs for ring construction.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/random_outs",
|
||||
request_body = RandomOutsRequest,
|
||||
@@ -452,6 +457,7 @@ pub async fn random_outs(
|
||||
|
||||
/// Broadcast a finalized, signed Monero/Wownero transaction.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/submit_tx",
|
||||
request_body = SubmitRequest,
|
||||
@@ -480,6 +486,7 @@ pub async fn submit_tx(
|
||||
|
||||
/// Current chain height (daemon) for confirmation counting.
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/height",
|
||||
request_body = LwsAssetRequest,
|
||||
@@ -505,6 +512,7 @@ pub async fn height(
|
||||
|
||||
/// Confirmation count for a transaction (daemon).
|
||||
#[utoipa::path(
|
||||
security(("bearer_auth" = [])),
|
||||
post,
|
||||
path = "/wallet/lws/confirmations",
|
||||
request_body = ConfirmationsRequest,
|
||||
|
||||
Reference in New Issue
Block a user