rocket_apitoken/lib.rs
1//! A very simple API Authorization module for Rocket web applications
2//!
3//! # Overview
4//! This module provides a simple token-based authorization system for Rocket web applications.
5//! It supports both enabled and disabled states, and validates Bearer tokens against a predefined set.
6//!
7//! # Usage Example
8//! ```no_run
9//! use rocket;
10//! use rocket_apitoken::{ApiToken, Authorized};
11//!
12//! #[post("/<method>?<json>", data = "<data>")]
13//! async fn protected_endpoint(_auth: Authorized, /* other params */) {
14//! // If this executes, the request was authorized
15//! // ...
16//! }
17//!
18//! #[launch]
19//! fn rocket() -> _ {
20//! let tokens = vec!["secret-token".to_string()];
21//! rocket::build()
22//! .manage(ApiToken::new(tokens, true))
23//! .mount("/api", routes![protected_endpoint])
24//! }
25//! ```
26//!
27//! # Configuration
28//! - Create an `ApiToken` instance with a list of valid tokens and enabled state
29//! - Add it to Rocket's state using `.manage()`
30//! - Use the `Authorized` guard in your route handlers
31//!
32//! When enabled, requests must include a valid token in the Authorization header.
33//! When disabled, all requests are authorized automatically.
34
35#![warn(missing_docs)]
36
37use rocket::http::Status;
38use rocket::request::{FromRequest, Outcome};
39use rocket::Request;
40use std::collections::HashSet;
41
42/// Configuration for API token authorization
43pub struct ApiToken {
44 tokens: HashSet<String>,
45 enabled: bool,
46}
47
48impl ApiToken {
49 /// Create a new `ApiToken` instance
50 pub fn new(tokens: Vec<String>, enabled: bool) -> Self {
51 Self {
52 tokens: tokens
53 .into_iter()
54 .map(|token| format!("Bearer {}", token))
55 .collect(),
56 enabled,
57 }
58 }
59
60 /// Add bearer tokens to the list of valid tokens
61 pub fn add_bearer(&mut self, token: &str) {
62 self.tokens.insert(format!("Bearer {}", token));
63 }
64}
65
66/// Request guard that ensures requests are authorized
67///
68/// This guard will succeed if either:
69/// - Authorization is disabled (`enabled = false` in ApiToken)
70/// - A valid bearer token is provided in the Authorization header
71///
72/// # Errors
73/// Returns 401 Unauthorized if:
74/// - Authorization is enabled and no Authorization header is present
75/// - The provided token is invalid
76#[derive(Debug)]
77pub struct Authorized;
78
79#[rocket::async_trait]
80impl<'r> FromRequest<'r> for Authorized {
81 type Error = &'static str;
82
83 async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
84 let token = request
85 .rocket()
86 .state::<ApiToken>()
87 .expect("Token state not available.");
88 if !token.enabled {
89 return Outcome::Success(Authorized);
90 }
91 match request.headers().get_one("Authorization") {
92 Some(value) => {
93 // Check the Bearer token
94 if token.tokens.contains(value) {
95 Outcome::Success(Authorized)
96 } else {
97 Outcome::Error((Status::Unauthorized, "invalid token"))
98 }
99 }
100 _ => Outcome::Error((Status::Unauthorized, "Authorization header not found")),
101 }
102 }
103}