diff --git a/crates/ezidam/src/guards.rs b/crates/ezidam/src/guards.rs index e81a988..bcecbfe 100644 --- a/crates/ezidam/src/guards.rs +++ b/crates/ezidam/src/guards.rs @@ -1,3 +1,4 @@ +mod access_token; mod basic_auth; mod completed_setup; mod jwt; @@ -5,7 +6,8 @@ mod need_setup; mod refresh_token; pub use self::jwt::*; +pub use access_token::AccessToken; +pub use basic_auth::BasicAuth; pub use completed_setup::CompletedSetup; pub use need_setup::NeedSetup; pub use refresh_token::RefreshToken; -pub use basic_auth::BasicAuth; diff --git a/crates/ezidam/src/guards/access_token.rs b/crates/ezidam/src/guards/access_token.rs new file mode 100644 index 0000000..9d3b86c --- /dev/null +++ b/crates/ezidam/src/guards/access_token.rs @@ -0,0 +1,64 @@ +use crate::guards::validate_jwt; +use jwt::JwtClaims; +use rocket::http::Status; +use rocket::request::{FromRequest, Outcome}; +use rocket::Request; + +#[derive(Debug)] +pub enum BearerAuthError { + BadCount, + Invalid, + Empty, + Jwt(super::jwt::Error), +} + +pub struct AccessToken(pub JwtClaims); + +impl AccessToken { + async fn from_bearer( + raw: &str, + request: &Request<'_>, + ) -> Result, BearerAuthError> { + // Make sure format is `Bearer access_token` + if !raw.starts_with("Bearer ") { + return Err(BearerAuthError::Invalid); + } + + // Extract access token + let (_, access_token) = raw.split_once(' ').ok_or(BearerAuthError::Empty)?; + + // Validate + match validate_jwt::(access_token.to_string(), request, None).await { + Ok(jwt_claims) => match jwt_claims { + Some(jwt_claims) => Ok(Some(Self(jwt_claims))), + None => Ok(None), + }, + Err(e) => match e { + Outcome::Success(s) => Ok(Some(Self(s))), + Outcome::Failure(e) => Err(BearerAuthError::Jwt(e.1)), + Outcome::Forward(_) => Ok(None), + }, + } + } +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for AccessToken { + type Error = BearerAuthError; + + async fn from_request(request: &'r Request<'_>) -> Outcome { + let keys: Vec<_> = request.headers().get("Authorization").collect(); + + match keys.len() { + 0 => Outcome::Forward(()), + 1 => match AccessToken::from_bearer(keys[0], request).await { + Ok(access_token) => match access_token { + Some(access_token) => Outcome::Success(access_token), + None => Outcome::Forward(()), + }, + Err(e) => Outcome::Failure((Status::Unauthorized, e)), + }, + _ => Outcome::Failure((Status::BadRequest, BearerAuthError::BadCount)), + } + } +} diff --git a/crates/ezidam/src/guards/jwt.rs b/crates/ezidam/src/guards/jwt.rs index 17b4031..63d663c 100644 --- a/crates/ezidam/src/guards/jwt.rs +++ b/crates/ezidam/src/guards/jwt.rs @@ -1,22 +1,21 @@ use crate::database::Database; -use jwt::database::Key; -use jwt::{JwtClaims, PrivateKey}; -use rocket::http::Status; -use rocket::request::Outcome; -use rocket::tokio::task; -use rocket::Request; - -mod admin; -mod user; - use crate::guards::refresh_token::get_refresh_token_from_cookie; use crate::tokens::{ JWT_COOKIE_NAME, JWT_DURATION_MINUTES, REFRESH_TOKEN_COOKIE_NAME, REFRESH_TOKEN_DURATION_DAYS, }; pub use admin::JwtAdmin; use id::KeyID; +use jwt::database::Key; +use jwt::{JwtClaims, PrivateKey}; +use rocket::http::Status; +use rocket::request::Outcome; +use rocket::tokio::task; +use rocket::Request; pub use user::JwtUser; +mod admin; +mod user; + #[derive(Debug)] pub enum Error { GetDatabase, diff --git a/crates/ezidam/src/routes/oauth.rs b/crates/ezidam/src/routes/oauth.rs index fee1374..b6a2622 100644 --- a/crates/ezidam/src/routes/oauth.rs +++ b/crates/ezidam/src/routes/oauth.rs @@ -2,10 +2,12 @@ use authorize::*; use redirect::*; use rocket::{routes, Route}; use token::*; +use userinfo::*; pub mod authorize; pub mod redirect; pub mod token; +pub mod userinfo; pub fn routes() -> Vec { routes![ @@ -14,6 +16,7 @@ pub fn routes() -> Vec { authorize_ezidam, redirect_page, request_token, + get_userinfo, ] } diff --git a/crates/ezidam/src/routes/oauth/userinfo.rs b/crates/ezidam/src/routes/oauth/userinfo.rs new file mode 100644 index 0000000..6add6ea --- /dev/null +++ b/crates/ezidam/src/routes/oauth/userinfo.rs @@ -0,0 +1,9 @@ +use crate::routes::prelude::*; +use jwt::JwtClaims; +use rocket::get; +use rocket::serde::json::Json; + +#[get("/oauth/userinfo")] +pub async fn get_userinfo(access_token: AccessToken) -> Json { + Json(access_token.0) +}