-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from trchopan/apply-generic-token-type
Verifier take generic typing to be extended in case of Custom Claims
- Loading branch information
Showing
12 changed files
with
283 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "example-actix-custom-claims" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
firebase-auth = { path = "../../firebase-auth" } | ||
actix-web = { version = "4" } | ||
actix-web-httpauth = { version = "0.8.0" } | ||
futures = "0.3" | ||
serde = "1.0" | ||
serde_json = "1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use std::env; | ||
|
||
use actix_web::error::ErrorUnauthorized; | ||
use actix_web::{ | ||
dev, get, http::header::Header, middleware::Logger, web, web::Data, App, Error, FromRequest, | ||
HttpRequest, HttpServer, Responder, | ||
}; | ||
use actix_web_httpauth::headers::authorization::{Authorization, Bearer}; | ||
use firebase_auth::{FirebaseAuth, FirebaseProvider}; | ||
use futures::future::{err, ok, Ready}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Serialize, Deserialize, Clone)] | ||
pub struct FirebaseUser { | ||
pub iss: String, | ||
pub aud: String, | ||
pub sub: String, | ||
pub iat: u64, | ||
pub exp: u64, | ||
pub auth_time: u64, | ||
pub user_id: String, | ||
pub provider_id: Option<String>, | ||
pub name: Option<String>, | ||
pub picture: Option<String>, | ||
pub email: Option<String>, | ||
pub email_verified: Option<bool>, | ||
pub firebase: FirebaseProvider, | ||
|
||
#[serde(rename = "https://hasura.io/jwt/claims")] | ||
pub hasura: HasuraClaims, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Clone)] | ||
pub struct HasuraClaims { | ||
pub x_hasura_default_role: String, | ||
pub x_hasura_allowed_roles: Vec<String>, | ||
pub x_hasura_user_id: String, | ||
} | ||
|
||
fn get_bearer_token(header: &str) -> Option<String> { | ||
let prefix_len = "Bearer ".len(); | ||
|
||
match header.len() { | ||
l if l < prefix_len => None, | ||
_ => Some(header[prefix_len..].to_string()), | ||
} | ||
} | ||
|
||
impl FromRequest for FirebaseUser { | ||
type Error = Error; | ||
type Future = Ready<Result<Self, Self::Error>>; | ||
|
||
fn from_request(req: &HttpRequest, _: &mut dev::Payload) -> Self::Future { | ||
let firebase_auth = req | ||
.app_data::<web::Data<FirebaseAuth>>() | ||
.expect("must init FirebaseAuth in Application Data. see description in https://crates.io/crates/firebase-auth"); | ||
|
||
let bearer = match Authorization::<Bearer>::parse(req) { | ||
Err(e) => return err(e.into()), | ||
Ok(v) => get_bearer_token(&v.to_string()).unwrap_or_default(), | ||
}; | ||
|
||
match firebase_auth.verify(&bearer) { | ||
Err(e) => err(ErrorUnauthorized(format!("Failed to verify Token {}", e))), | ||
Ok(user) => ok(user), | ||
} | ||
} | ||
} | ||
|
||
#[get("/hello")] | ||
async fn greet(user: FirebaseUser) -> impl Responder { | ||
let hasura_user_id = user.hasura.x_hasura_user_id; | ||
format!("Hello user id {}!", hasura_user_id) | ||
} | ||
|
||
#[get("/public")] | ||
async fn public() -> impl Responder { | ||
"ok" | ||
} | ||
|
||
#[actix_web::main] | ||
async fn main() -> std::io::Result<()> { | ||
let project_id = env::var("PROJECT_ID").unwrap_or_else(|_| panic!("must set PROJECT_ID")); | ||
let firebase_auth = FirebaseAuth::new(&project_id).await; | ||
|
||
let app_data = Data::new(firebase_auth); | ||
|
||
HttpServer::new(move || { | ||
App::new() | ||
.wrap(Logger::default()) | ||
.app_data(app_data.clone()) | ||
.service(greet) | ||
.service(public) | ||
}) | ||
.bind(("127.0.0.1", 8080))? | ||
.run() | ||
.await | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "example-axum-custom-claims" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[dependencies] | ||
firebase-auth = { path = "../../firebase-auth" } | ||
axum = "0.7" | ||
tokio = { version = "1.0", features = ["full"] } | ||
tower-http = { version = "0.5.0", features = ["trace"] } | ||
tracing = "0.1" | ||
serde = "1.0" | ||
serde_json = "1.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use axum::{ | ||
async_trait, | ||
extract::{FromRef, FromRequestParts}, | ||
http::{self, request::Parts, StatusCode}, | ||
response::{IntoResponse, Response}, | ||
routing::get, | ||
Router, | ||
}; | ||
use firebase_auth::{FirebaseAuth, FirebaseAuthState, FirebaseProvider}; | ||
use serde::{Deserialize, Serialize}; | ||
use tracing::debug; | ||
|
||
#[derive(Serialize, Deserialize, Clone)] | ||
pub struct FirebaseUser { | ||
pub iss: String, | ||
pub aud: String, | ||
pub sub: String, | ||
pub iat: u64, | ||
pub exp: u64, | ||
pub auth_time: u64, | ||
pub user_id: String, | ||
pub provider_id: Option<String>, | ||
pub name: Option<String>, | ||
pub picture: Option<String>, | ||
pub email: Option<String>, | ||
pub email_verified: Option<bool>, | ||
pub firebase: FirebaseProvider, | ||
|
||
#[serde(rename = "https://hasura.io/jwt/claims")] | ||
pub hasura: HasuraClaims, | ||
} | ||
|
||
#[derive(Serialize, Deserialize, Clone)] | ||
pub struct HasuraClaims { | ||
pub x_hasura_default_role: String, | ||
pub x_hasura_allowed_roles: Vec<String>, | ||
pub x_hasura_user_id: String, | ||
} | ||
|
||
fn get_bearer_token(header: &str) -> Option<String> { | ||
let prefix_len = "Bearer ".len(); | ||
|
||
match header.len() { | ||
l if l < prefix_len => None, | ||
_ => Some(header[prefix_len..].to_string()), | ||
} | ||
} | ||
|
||
#[async_trait] | ||
impl<S> FromRequestParts<S> for FirebaseUser | ||
where | ||
FirebaseAuthState: FromRef<S>, | ||
S: Send + Sync, | ||
{ | ||
type Rejection = UnauthorizedResponse; | ||
|
||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> { | ||
let store = FirebaseAuthState::from_ref(state); | ||
|
||
let auth_header = parts | ||
.headers | ||
.get(http::header::AUTHORIZATION) | ||
.and_then(|value| value.to_str().ok()) | ||
.unwrap_or(""); | ||
|
||
let bearer = get_bearer_token(auth_header).map_or( | ||
Err(UnauthorizedResponse { | ||
msg: "Missing Bearer Token".to_string(), | ||
}), | ||
Ok, | ||
)?; | ||
|
||
debug!("Got bearer token {}", bearer); | ||
|
||
match store.firebase_auth.verify(&bearer) { | ||
Err(e) => Err(UnauthorizedResponse { | ||
msg: format!("Failed to verify Token: {}", e), | ||
}), | ||
Ok(current_user) => Ok(current_user), | ||
} | ||
} | ||
} | ||
|
||
pub struct UnauthorizedResponse { | ||
msg: String, | ||
} | ||
|
||
impl IntoResponse for UnauthorizedResponse { | ||
fn into_response(self) -> Response { | ||
(StatusCode::UNAUTHORIZED, self.msg).into_response() | ||
} | ||
} | ||
|
||
async fn greet(user: FirebaseUser) -> String { | ||
let email = user.email.unwrap_or("empty email".to_string()); | ||
format!("hello {}", email) | ||
} | ||
|
||
async fn public() -> &'static str { | ||
"ok" | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
let firebase_auth = FirebaseAuth::new("my-project-id").await; | ||
|
||
let app = Router::new() | ||
.route("/hello", get(greet)) | ||
.route("/", get(public)) | ||
.with_state(FirebaseAuthState { firebase_auth }); | ||
|
||
let addr = "127.0.0.1:8080"; | ||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); | ||
|
||
axum::serve(listener, app).await.unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.