oauth: token route: client_id is not required if basic auth is present
This commit is contained in:
parent
3d79fc1817
commit
2e5a1b0c42
2 changed files with 51 additions and 24 deletions
|
|
@ -38,7 +38,7 @@ pub struct TokenRequest<'r> {
|
||||||
pub grant_type: GrantType,
|
pub grant_type: GrantType,
|
||||||
pub code: Option<&'r str>,
|
pub code: Option<&'r str>,
|
||||||
pub redirect_uri: Option<&'r str>,
|
pub redirect_uri: Option<&'r str>,
|
||||||
pub client_id: &'r str,
|
pub client_id: Option<&'r str>,
|
||||||
pub client_secret: Option<&'r str>,
|
pub client_secret: Option<&'r str>,
|
||||||
pub scope: Option<&'r str>,
|
pub scope: Option<&'r str>,
|
||||||
pub refresh_token: Option<&'r str>,
|
pub refresh_token: Option<&'r str>,
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,14 @@ pub enum TokenError {
|
||||||
RefreshTokenExpired,
|
RefreshTokenExpired,
|
||||||
AuthorizationCodeUsed,
|
AuthorizationCodeUsed,
|
||||||
AuthorizationCodeExpired,
|
AuthorizationCodeExpired,
|
||||||
HttpAuthDifferentClientId,
|
|
||||||
AppError(apps::Error),
|
AppError(apps::Error),
|
||||||
AppNotFound(String),
|
AppNotFoundFromAuthorizationCode(String),
|
||||||
|
AppNotFoundFromRefreshToken(String),
|
||||||
|
AppIdNotProvided,
|
||||||
AppSecretNotProvided,
|
AppSecretNotProvided,
|
||||||
Blocking(task::JoinError),
|
Blocking(task::JoinError),
|
||||||
SecretCompare(hash::Error),
|
SecretCompare(hash::Error),
|
||||||
|
AppIdWrong,
|
||||||
AppSecretWrong,
|
AppSecretWrong,
|
||||||
UserError(users::Error),
|
UserError(users::Error),
|
||||||
UserNotFound,
|
UserNotFound,
|
||||||
|
|
@ -111,14 +113,19 @@ impl<'r> Responder<'r, 'static> for TokenError {
|
||||||
Status::BadRequest,
|
Status::BadRequest,
|
||||||
"Authorization code has expired".to_string(),
|
"Authorization code has expired".to_string(),
|
||||||
),
|
),
|
||||||
TokenError::HttpAuthDifferentClientId => (
|
|
||||||
Status::BadRequest,
|
|
||||||
"HTTP Auth differs from provided client_id".to_string(),
|
|
||||||
),
|
|
||||||
TokenError::AppError(e) => (Status::InternalServerError, e.to_string()),
|
TokenError::AppError(e) => (Status::InternalServerError, e.to_string()),
|
||||||
TokenError::AppNotFound(e) => {
|
TokenError::AppNotFoundFromAuthorizationCode(e) => (
|
||||||
(Status::NotFound, format!("Could not find application {e}"))
|
Status::NotFound,
|
||||||
}
|
format!("Could not find application from authorization code {e}"),
|
||||||
|
),
|
||||||
|
TokenError::AppNotFoundFromRefreshToken(e) => (
|
||||||
|
Status::NotFound,
|
||||||
|
format!("Could not find application from refresh token {e}"),
|
||||||
|
),
|
||||||
|
TokenError::AppIdNotProvided => (
|
||||||
|
Status::BadRequest,
|
||||||
|
"Could not get client_id: not provided in any way".to_string(),
|
||||||
|
),
|
||||||
TokenError::AppSecretNotProvided => {
|
TokenError::AppSecretNotProvided => {
|
||||||
(Status::BadRequest, "Secret was not provided".to_string())
|
(Status::BadRequest, "Secret was not provided".to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +134,7 @@ impl<'r> Responder<'r, 'static> for TokenError {
|
||||||
Status::InternalServerError,
|
Status::InternalServerError,
|
||||||
format!("Failed to check app secret: {e}"),
|
format!("Failed to check app secret: {e}"),
|
||||||
),
|
),
|
||||||
|
TokenError::AppIdWrong => (Status::Forbidden, "Invalid client_id provided".to_string()),
|
||||||
TokenError::AppSecretWrong => {
|
TokenError::AppSecretWrong => {
|
||||||
(Status::Forbidden, "Invalid secret provided".to_string())
|
(Status::Forbidden, "Invalid secret provided".to_string())
|
||||||
}
|
}
|
||||||
|
|
@ -171,8 +179,8 @@ pub async fn request_token(
|
||||||
) -> std::result::Result<TokenResponse, TokenError> {
|
) -> std::result::Result<TokenResponse, TokenError> {
|
||||||
let mut transaction = db.begin().await.map_err(TokenError::TransactionStart)?;
|
let mut transaction = db.begin().await.map_err(TokenError::TransactionStart)?;
|
||||||
|
|
||||||
// Get user depending on grant type
|
// Get user and app depending on grant type
|
||||||
let user = match token_request.grant_type {
|
let (user, app) = match token_request.grant_type {
|
||||||
GrantType::AuthorizationCode => {
|
GrantType::AuthorizationCode => {
|
||||||
let authorization_code = token_request
|
let authorization_code = token_request
|
||||||
.code
|
.code
|
||||||
|
|
@ -220,12 +228,20 @@ pub async fn request_token(
|
||||||
return Err(TokenError::UserArchived(user.id().to_string()));
|
return Err(TokenError::UserArchived(user.id().to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get app from code
|
||||||
|
let app = App::get_one_from_authorization_code(&mut transaction, authorization_code)
|
||||||
|
.await
|
||||||
|
.map_err(TokenError::AppError)?
|
||||||
|
.ok_or(TokenError::AppNotFoundFromAuthorizationCode(
|
||||||
|
authorization_code.into(),
|
||||||
|
))?;
|
||||||
|
|
||||||
// Mark code as used
|
// Mark code as used
|
||||||
code.use_code(&mut transaction)
|
code.use_code(&mut transaction)
|
||||||
.await
|
.await
|
||||||
.map_err(TokenError::AuthorizationError)?;
|
.map_err(TokenError::AuthorizationError)?;
|
||||||
|
|
||||||
user
|
(user, app)
|
||||||
}
|
}
|
||||||
GrantType::RefreshToken => {
|
GrantType::RefreshToken => {
|
||||||
let refresh_token = token_request
|
let refresh_token = token_request
|
||||||
|
|
@ -264,27 +280,38 @@ pub async fn request_token(
|
||||||
return Err(TokenError::RefreshTokenExpired);
|
return Err(TokenError::RefreshTokenExpired);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get app
|
||||||
|
let app = App::get_one_by_id(&mut transaction, refresh_token.app().as_ref())
|
||||||
|
.await
|
||||||
|
.map_err(TokenError::AppError)?
|
||||||
|
.ok_or(TokenError::AppNotFoundFromRefreshToken(format!(
|
||||||
|
"Refresh token for user {}",
|
||||||
|
refresh_token.user()
|
||||||
|
)))?;
|
||||||
|
|
||||||
refresh_token
|
refresh_token
|
||||||
.use_token(&mut transaction)
|
.use_token(&mut transaction)
|
||||||
.await
|
.await
|
||||||
.map_err(TokenError::RefreshTokenError)?;
|
.map_err(TokenError::RefreshTokenError)?;
|
||||||
|
|
||||||
user
|
(user, app)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If HTTP Basic Auth is provided, verify provided client id in form
|
// Get client id
|
||||||
if let Some(app_auth) = &app_auth {
|
// https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
|
||||||
if app_auth.id != token_request.client_id {
|
let provided_client_id = match (&app_auth, token_request.client_id) {
|
||||||
return Err(TokenError::HttpAuthDifferentClientId);
|
(Some(http_auth), _) => http_auth.id.to_string(),
|
||||||
|
(None, Some(form)) => form.into(),
|
||||||
|
(None, None) => {
|
||||||
|
return Err(TokenError::AppIdNotProvided);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Get app
|
// Verify client id
|
||||||
let app = App::get_one_by_id(&mut transaction, token_request.client_id)
|
if app.id().as_ref() != provided_client_id {
|
||||||
.await
|
return Err(TokenError::AppIdWrong);
|
||||||
.map_err(TokenError::AppError)?
|
}
|
||||||
.ok_or(TokenError::AppNotFound(token_request.client_id.into()))?;
|
|
||||||
|
|
||||||
if app.is_confidential() {
|
if app.is_confidential() {
|
||||||
let provided_secret = match (app_auth, token_request.client_secret) {
|
let provided_secret = match (app_auth, token_request.client_secret) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue