firestore_db_and_auth/rocket/
mod.rs

1//! # Rocket Authentication Guard
2//!
3//! Because the `sessions` module of this crate is already able to verify access tokens,
4//! it was not much more work to turn this into a Rocket 0.4+ Guard.
5//!
6//! The implemented Guard (enabled by the feature "rocket_support") allows access to http paths
7//! if the provided http "Authorization" header contains a valid "Bearer" token.
8//! The above mentioned validations on the token are performed.
9//!
10//! Example:
11//!
12//! ```
13//! use firestore_db_and_auth::{Credentials, rocket::FirestoreAuthSessionGuard};
14//! use rocket::get;
15//!
16//! fn main() {
17//!     use rocket::routes;
18//! let credentials = Credentials::from_file("firebase-service-account.json").unwrap();
19//!     rocket::build().ignite().manage(credentials).mount("/", routes![hello, hello_not_logged_in]).launch();
20//! }
21//!
22//! /// And an example route could be:
23//! #[get("/hello")]
24//! fn hello<'r>(auth: FirestoreAuthSessionGuard) -> String {
25//!     // ApiKey is a single value tuple with a sessions::user::Session object inside
26//! format!("you are logged in. user_id: {}", auth.0.user_id)
27//! }
28//!
29//! #[get("/hello")]
30//! fn hello_not_logged_in<'r>() -> &'r str {
31//!     "you are not logged in"
32//! }
33//! ```
34use super::credentials::Credentials;
35use super::errors::FirebaseError;
36use super::sessions;
37use rocket::request::Outcome;
38use rocket::{http::Status, request, State};
39
40/// Use this Rocket guard to secure a route for authenticated users only.
41/// Will return the associated session, that contains the used access token for further use
42/// and access to the Firestore database.
43pub struct FirestoreAuthSessionGuard(pub sessions::user::Session);
44
45#[rocket::async_trait]
46impl<'a> request::FromRequest<'a> for FirestoreAuthSessionGuard {
47    type Error = FirebaseError;
48
49    async fn from_request(request: &'a request::Request<'_>) -> request::Outcome<Self, Self::Error> {
50        let r = request
51            .headers()
52            .get_one("Authorization")
53            .map(|f| f.to_owned())
54            .or(request.query_value("auth").and_then(|r| r.ok()));
55        if r.is_none() {
56            return Outcome::Forward(Status::BadRequest);
57        }
58        let bearer = r.unwrap();
59        if !bearer.starts_with("Bearer ") {
60            return Outcome::Forward(Status::BadRequest);
61        }
62        let bearer = &bearer[7..];
63
64        // You MUST make the credentials object available as managed state to rocket!
65        let db = match request.guard::<&State<Credentials>>().await {
66            Outcome::Success(db) => db,
67            _ => {
68                return Outcome::Error((
69                    Status::InternalServerError,
70                    FirebaseError::Generic("Firestore credentials not set!"),
71                ))
72            }
73        };
74
75        let session = sessions::user::Session::by_access_token(&db, bearer).await;
76        if session.is_err() {
77            return Outcome::Forward(Status::BadRequest);
78        }
79        Outcome::Success(FirestoreAuthSessionGuard(session.unwrap()))
80    }
81}