diff options
| author | mrw1593 <botahamec@outlook.com> | 2023-05-29 17:06:44 -0400 |
|---|---|---|
| committer | mrw1593 <botahamec@outlook.com> | 2023-05-29 17:06:44 -0400 |
| commit | 4782cd0c9f1b930f05fe24118001a4de45893b79 (patch) | |
| tree | f348ae4ca022e609e286c6260c24f636f5a5cb1d /src | |
| parent | 8ee2802e8e1b3c443485dce002115389f2ba8f75 (diff) | |
Add basic authorization to the token endpoint
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/oauth.rs | 9 | ||||
| -rw-r--r-- | src/services/authorization.rs | 108 |
2 files changed, 77 insertions, 40 deletions
diff --git a/src/api/oauth.rs b/src/api/oauth.rs index 7941735..a563ac8 100644 --- a/src/api/oauth.rs +++ b/src/api/oauth.rs @@ -9,7 +9,7 @@ use url::Url; use uuid::Uuid; use crate::resources::{languages, templates}; -use crate::services::db; +use crate::services::{authorization, db}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -35,6 +35,7 @@ struct AuthorizeCredentials { #[post("/authorize")] async fn authorize( + db: web::Data<MySqlPool>, query: web::Query<AuthorizationParameters>, credentials: web::Form<AuthorizeCredentials>, ) -> HttpResponse { @@ -69,7 +70,11 @@ struct TokenRequest { } #[post("/token")] -async fn token(db: web::Data<MySqlPool>, req: web::Form<TokenRequest>) -> HttpResponse { +async fn token( + db: web::Data<MySqlPool>, + req: web::Form<TokenRequest>, + authorization: web::Header<authorization::BasicAuthorization>, // TODO make this optional +) -> HttpResponse { todo!() } diff --git a/src/services/authorization.rs b/src/services/authorization.rs index b9d57ae..bfbbb5a 100644 --- a/src/services/authorization.rs +++ b/src/services/authorization.rs @@ -1,50 +1,82 @@ +use actix_web::{ + error::ParseError, + http::header::{self, Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue}, +}; use base64::Engine; use raise::yeet; -use thiserror::Error; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)] -pub enum ParseBasicError { - #[error("Basic Authorization is required")] - NotBasic, - #[error("No credentials were provided for authorization")] - NoCredentials, - #[error("The credentials provided were not base64")] - InvalidBase64, - #[error("The decoded base64 credentials were not UTF-8")] - NotUtf8, - #[error("A colon (:) must be used to delimit the username and password")] - NoColon, + +#[derive(Clone)] +pub struct BasicAuthorization { + username: Box<str>, + password: Box<str>, } -/// Returns a username and a password from a Basic authorization header -pub fn parse_basic(value: &str) -> Result<(Box<str>, Box<str>), ParseBasicError> { - if !value.starts_with("Basic") { - yeet!(ParseBasicError::NotBasic); - } +impl TryIntoHeaderValue for BasicAuthorization { + type Error = InvalidHeaderValue; - let value: String = value - .chars() - .skip(5) - .skip_while(|ch| ch.is_whitespace()) - .collect(); + fn try_into_value(self) -> Result<HeaderValue, Self::Error> { + let username = self.username; + let password = self.password; + let utf8 = format!("{username}:{password}"); + let b64 = base64::engine::general_purpose::STANDARD.encode(utf8); + let value = format!("Basic {b64}"); + HeaderValue::from_str(&value) + } +} - if value.is_empty() { - yeet!(ParseBasicError::NoCredentials); +impl Header for BasicAuthorization { + fn name() -> HeaderName { + header::AUTHORIZATION } - let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else { - yeet!(ParseBasicError::InvalidBase64) - }; + fn parse<M: actix_web::HttpMessage>(msg: &M) -> Result<Self, actix_web::error::ParseError> { + let Some(value) = msg.headers().get(Self::name()) else { + yeet!(ParseError::Header) + }; + + let Ok(value) = value.to_str() else { + yeet!(ParseError::Header) + }; + + if !value.starts_with("Basic") { + yeet!(ParseError::Header); + } + + let value: String = value + .chars() + .skip(5) + .skip_while(|ch| ch.is_whitespace()) + .collect(); + + if value.is_empty() { + yeet!(ParseError::Header); + } - let Ok(value) = String::from_utf8(bytes) else { - yeet!(ParseBasicError::NotUtf8) - }; + let Ok(bytes) = base64::engine::general_purpose::STANDARD.decode(value) else { + yeet!(ParseError::Header) + }; - let mut parts = value.split(':'); - let username = parts.next().unwrap(); - let Some(password) = parts.next() else { - yeet!(ParseBasicError::NoColon) - }; + let Ok(value) = String::from_utf8(bytes) else { + yeet!(ParseError::Header) + }; - Ok((Box::from(username), Box::from(password))) + let mut parts = value.split(':'); + let username = Box::from(parts.next().unwrap()); + let Some(password) = parts.next() else { + yeet!(ParseError::Header) + }; + let password = Box::from(password); + + Ok(Self { username, password }) + } +} + +impl BasicAuthorization { + pub fn username(&self) -> &str { + &self.username + } + + pub fn password(&self) -> &str { + &self.password + } } |
