Closed rgarlik closed 1 year ago
I can see you implemented the BearerToken as a guard. Why not go all the way and check if the token is valid with Firebase inside the guard? That's the whole point of "guards" that they guard the endpoint and reject any traffic that's invalid.
It's possible to reference State from within a guard so the guard can have access to the Firebase auth state.
Here's my very "glued together" implementation of a guard that could be used for verification that I've implemented using modules from rocket-firebase-auth
:
use rocket::{
http::Status,
request::{self, FromRequest, Request},
};
use rocket_firebase_auth::{errors::Error, DecodedToken, FirebaseAuth};
// Firebase is a request guard that verifies the Firebase JWT's validity
pub struct FirebaseGuard {
// The JWT token with all its claims
pub token: DecodedToken,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for FirebaseGuard {
type Error = FirebaseError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
// Is header present?
match req.headers().get_one("Authorization") {
Some(header_raw) => {
let (header_name, header_content) = header_raw.split_at(7);
// Is header in the correct format?
if header_name != "Bearer " {
request::Outcome::Failure((
Status::Unauthorized,
FirebaseError::IncorrectHeaderFormat,
))
} else {
// Find the FirebaseAuth state
match req.rocket().state::<FirebaseAuth>() {
// Verify if the token is valid
Some(auth) => match auth.verify_token(header_content).await {
Ok(t) => {
// Token is valid
let firebase_guard = FirebaseGuard { token: t };
request::Outcome::Success(firebase_guard)
}
Err(e) => request::Outcome::Failure((
// Token is invalid
Status::Unauthorized,
FirebaseError::JwtFailedValidation(e),
)),
},
// FirebaseAuth state not found
None => request::Outcome::Failure((
Status::InternalServerError,
FirebaseError::MissingFirebaseState,
)),
}
}
}
None => request::Outcome::Failure((Status::Unauthorized, FirebaseError::MissingHeader)),
}
// Check if Firebase JWT token is valid
}
}
// This custom error would of course be replaced with the rocket-firebase-auth internal Error type
#[derive(Debug)]
pub enum FirebaseError {
MissingHeader,
IncorrectHeaderFormat,
JwtFailedValidation(Error),
MissingFirebaseState,
}
Usage is very identical to my original comment's usage example up above.
Sorry for the late response, and thanks for opening an issue! This sounds like a great idea! Honestly, when you show the before and after of using RequestGuards, it's pretty obvious that this is something we should add to the library. You can open a PR if you're comfortable, or I can work on it, but it seems you are already halfway there. What do you think?
Hey @DrPoppyseed, I can implement it, no problem. I'll be adding a PR within a few days.
@rgarlik Sounds awesome!
Closing issue as guard has been implemented. Let me know if anyone wants to reopen this issue.
Hey all,
I've come across this crate and I see that it's implemented the firebase authentication as a State. This works, however wouldn't it make more sense to implement the mechanism as a request guard?
This would lift the logic from the controller function body from this:
..to something like this:
I'm implementing a firebase guard myself for a web project of mine, so I can contribute some of my code if you'd like. However I'm not sure if it's gonna fit well into your library. I'm new to Rust however so that might not be a good idea.