Remove the complete handler, make it go through the consent page
This commit is contained in:
@@ -405,13 +405,10 @@ where
|
||||
mas_router::OAuth2AuthorizationEndpoint::route(),
|
||||
get(self::oauth2::authorization::get),
|
||||
)
|
||||
.route(
|
||||
mas_router::ContinueAuthorizationGrant::route(),
|
||||
get(self::oauth2::authorization::complete::get),
|
||||
)
|
||||
.route(
|
||||
mas_router::Consent::route(),
|
||||
get(self::oauth2::consent::get).post(self::oauth2::consent::post),
|
||||
get(self::oauth2::authorization::consent::get)
|
||||
.post(self::oauth2::authorization::consent::post),
|
||||
)
|
||||
.route(
|
||||
mas_router::CompatLoginSsoComplete::route(),
|
||||
|
||||
@@ -101,7 +101,7 @@ impl CallbackDestination {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn go<T: Serialize + Send + Sync>(
|
||||
pub fn go<T: Serialize + Send + Sync>(
|
||||
self,
|
||||
templates: &Templates,
|
||||
locale: &DataLocale,
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{Html, IntoResponse, Response},
|
||||
};
|
||||
use axum_extra::TypedHeader;
|
||||
use hyper::StatusCode;
|
||||
use mas_axum_utils::{SessionInfoExt, cookies::CookieJar, csrf::CsrfExt, sentry::SentryEventID};
|
||||
use mas_data_model::{AuthorizationGrant, BrowserSession, Client};
|
||||
use mas_keystore::Keystore;
|
||||
use mas_policy::{EvaluationResult, Policy};
|
||||
use mas_router::{PostAuthAction, UrlBuilder};
|
||||
use mas_storage::{
|
||||
BoxClock, BoxRepository, BoxRng, Clock, RepositoryAccess,
|
||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository, OAuth2SessionRepository},
|
||||
user::BrowserSessionRepository,
|
||||
};
|
||||
use mas_templates::{PolicyViolationContext, TemplateContext, Templates};
|
||||
use oauth2_types::requests::AuthorizationResponse;
|
||||
use thiserror::Error;
|
||||
use tracing::warn;
|
||||
use ulid::Ulid;
|
||||
|
||||
use super::callback::CallbackDestination;
|
||||
use crate::{
|
||||
BoundActivityTracker, PreferredLanguage, impl_from_error_for_route, oauth2::generate_id_token,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RouteError {
|
||||
#[error(transparent)]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
#[error("authorization grant was not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("authorization grant is not in a pending state")]
|
||||
NotPending,
|
||||
|
||||
#[error("failed to load client")]
|
||||
NoSuchClient,
|
||||
}
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let event = sentry::capture_error(&self);
|
||||
// TODO: better error pages
|
||||
let response = match self {
|
||||
RouteError::NotFound => {
|
||||
(StatusCode::NOT_FOUND, "authorization grant was not found").into_response()
|
||||
}
|
||||
RouteError::NotPending => (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"authorization grant not in a pending state",
|
||||
)
|
||||
.into_response(),
|
||||
RouteError::Internal(_) | Self::NoSuchClient => {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
|
||||
}
|
||||
};
|
||||
|
||||
(SentryEventID::from(event), response).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(mas_storage::RepositoryError);
|
||||
impl_from_error_for_route!(mas_templates::TemplateError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
impl_from_error_for_route!(mas_policy::EvaluationError);
|
||||
impl_from_error_for_route!(super::callback::IntoCallbackDestinationError);
|
||||
impl_from_error_for_route!(super::callback::CallbackDestinationError);
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "handlers.oauth2.authorization_complete.get",
|
||||
fields(grant.id = %grant_id),
|
||||
skip_all,
|
||||
err,
|
||||
)]
|
||||
pub(crate) async fn get(
|
||||
mut rng: BoxRng,
|
||||
clock: BoxClock,
|
||||
PreferredLanguage(locale): PreferredLanguage,
|
||||
State(templates): State<Templates>,
|
||||
State(url_builder): State<UrlBuilder>,
|
||||
State(key_store): State<Keystore>,
|
||||
policy: Policy,
|
||||
activity_tracker: BoundActivityTracker,
|
||||
user_agent: Option<TypedHeader<headers::UserAgent>>,
|
||||
mut repo: BoxRepository,
|
||||
cookie_jar: CookieJar,
|
||||
Path(grant_id): Path<Ulid>,
|
||||
) -> Result<Response, RouteError> {
|
||||
let (session_info, cookie_jar) = cookie_jar.session_info();
|
||||
|
||||
let maybe_session = session_info.load_active_session(&mut repo).await?;
|
||||
|
||||
let user_agent = user_agent.map(|TypedHeader(ua)| ua.to_string());
|
||||
|
||||
let grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.lookup(grant_id)
|
||||
.await?
|
||||
.ok_or(RouteError::NotFound)?;
|
||||
|
||||
let callback_destination = CallbackDestination::try_from(&grant)?;
|
||||
let continue_grant = PostAuthAction::continue_grant(grant.id);
|
||||
|
||||
let Some(session) = maybe_session else {
|
||||
// If there is no session, redirect to the login screen, redirecting here after
|
||||
// logout
|
||||
return Ok((
|
||||
cookie_jar,
|
||||
url_builder.redirect(&mas_router::Login::and_then(continue_grant)),
|
||||
)
|
||||
.into_response());
|
||||
};
|
||||
|
||||
activity_tracker
|
||||
.record_browser_session(&clock, &session)
|
||||
.await;
|
||||
|
||||
let client = repo
|
||||
.oauth2_client()
|
||||
.lookup(grant.client_id)
|
||||
.await?
|
||||
.ok_or(RouteError::NoSuchClient)?;
|
||||
|
||||
match complete(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&activity_tracker,
|
||||
user_agent,
|
||||
repo,
|
||||
key_store,
|
||||
policy,
|
||||
&url_builder,
|
||||
grant,
|
||||
&client,
|
||||
&session,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(params) => {
|
||||
let res = callback_destination.go(&templates, &locale, params).await?;
|
||||
Ok((cookie_jar, res).into_response())
|
||||
}
|
||||
Err(GrantCompletionError::PolicyViolation(grant, res)) => {
|
||||
warn!(violation = ?res, "Authorization grant for client {} denied by policy", client.id);
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
let ctx = PolicyViolationContext::for_authorization_grant(grant, client)
|
||||
.with_session(session)
|
||||
.with_csrf(csrf_token.form_value())
|
||||
.with_language(locale);
|
||||
|
||||
let content = templates.render_policy_violation(&ctx)?;
|
||||
|
||||
Ok((cookie_jar, Html(content)).into_response())
|
||||
}
|
||||
Err(GrantCompletionError::NotPending) => Err(RouteError::NotPending),
|
||||
Err(GrantCompletionError::Internal(e)) => Err(RouteError::Internal(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GrantCompletionError {
|
||||
#[error(transparent)]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
|
||||
|
||||
#[error("authorization grant is not in a pending state")]
|
||||
NotPending,
|
||||
|
||||
#[error("denied by the policy")]
|
||||
PolicyViolation(AuthorizationGrant, EvaluationResult),
|
||||
}
|
||||
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_storage::RepositoryError);
|
||||
impl_from_error_for_route!(GrantCompletionError: super::callback::IntoCallbackDestinationError);
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_policy::LoadError);
|
||||
impl_from_error_for_route!(GrantCompletionError: mas_policy::EvaluationError);
|
||||
impl_from_error_for_route!(GrantCompletionError: super::super::IdTokenSignatureError);
|
||||
|
||||
pub(crate) async fn complete(
|
||||
rng: &mut (impl rand::RngCore + rand::CryptoRng + Send),
|
||||
clock: &impl Clock,
|
||||
activity_tracker: &BoundActivityTracker,
|
||||
user_agent: Option<String>,
|
||||
mut repo: BoxRepository,
|
||||
key_store: Keystore,
|
||||
mut policy: Policy,
|
||||
url_builder: &UrlBuilder,
|
||||
grant: AuthorizationGrant,
|
||||
client: &Client,
|
||||
browser_session: &BrowserSession,
|
||||
) -> Result<AuthorizationResponse, GrantCompletionError> {
|
||||
// Verify that the grant is in a pending stage
|
||||
if !grant.stage.is_pending() {
|
||||
return Err(GrantCompletionError::NotPending);
|
||||
}
|
||||
|
||||
// Run through the policy
|
||||
let res = policy
|
||||
.evaluate_authorization_grant(mas_policy::AuthorizationGrantInput {
|
||||
user: Some(&browser_session.user),
|
||||
client,
|
||||
scope: &grant.scope,
|
||||
grant_type: mas_policy::GrantType::AuthorizationCode,
|
||||
requester: mas_policy::Requester {
|
||||
ip_address: activity_tracker.ip(),
|
||||
user_agent,
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
|
||||
if !res.valid() {
|
||||
return Err(GrantCompletionError::PolicyViolation(grant, res));
|
||||
}
|
||||
|
||||
// All good, let's start the session
|
||||
let session = repo
|
||||
.oauth2_session()
|
||||
.add_from_browser_session(rng, clock, client, browser_session, grant.scope.clone())
|
||||
.await?;
|
||||
|
||||
let grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.fulfill(clock, &session, grant)
|
||||
.await?;
|
||||
|
||||
// Yep! Let's complete the auth now
|
||||
let mut params = AuthorizationResponse::default();
|
||||
|
||||
// Did they request an ID token?
|
||||
if grant.response_type_id_token {
|
||||
// Fetch the last authentication
|
||||
let last_authentication = repo
|
||||
.browser_session()
|
||||
.get_last_authentication(browser_session)
|
||||
.await?;
|
||||
|
||||
params.id_token = Some(generate_id_token(
|
||||
rng,
|
||||
clock,
|
||||
url_builder,
|
||||
&key_store,
|
||||
client,
|
||||
Some(&grant),
|
||||
browser_session,
|
||||
None,
|
||||
last_authentication.as_ref(),
|
||||
)?);
|
||||
}
|
||||
|
||||
// Did they request an auth code?
|
||||
if let Some(code) = grant.code {
|
||||
params.code = Some(code.code);
|
||||
}
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
activity_tracker
|
||||
.record_oauth2_session(clock, &session)
|
||||
.await;
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use mas_axum_utils::{
|
||||
sentry::SentryEventID,
|
||||
};
|
||||
use mas_data_model::AuthorizationGrantStage;
|
||||
use mas_keystore::Keystore;
|
||||
use mas_policy::Policy;
|
||||
use mas_router::{PostAuthAction, UrlBuilder};
|
||||
use mas_storage::{
|
||||
@@ -23,11 +24,14 @@ use mas_storage::{
|
||||
oauth2::{OAuth2AuthorizationGrantRepository, OAuth2ClientRepository},
|
||||
};
|
||||
use mas_templates::{ConsentContext, PolicyViolationContext, TemplateContext, Templates};
|
||||
use oauth2_types::requests::AuthorizationResponse;
|
||||
use thiserror::Error;
|
||||
use ulid::Ulid;
|
||||
|
||||
use super::callback::CallbackDestination;
|
||||
use crate::{
|
||||
BoundActivityTracker, PreferredLanguage, impl_from_error_for_route,
|
||||
oauth2::generate_id_token,
|
||||
session::{SessionOrFallback, load_session_or_fallback},
|
||||
};
|
||||
|
||||
@@ -45,9 +49,6 @@ pub enum RouteError {
|
||||
#[error("Authorization grant already used")]
|
||||
GrantNotPending,
|
||||
|
||||
#[error("Policy violation")]
|
||||
PolicyViolation,
|
||||
|
||||
#[error("Failed to load client")]
|
||||
NoSuchClient,
|
||||
}
|
||||
@@ -57,20 +58,24 @@ impl_from_error_for_route!(mas_storage::RepositoryError);
|
||||
impl_from_error_for_route!(mas_policy::LoadError);
|
||||
impl_from_error_for_route!(mas_policy::EvaluationError);
|
||||
impl_from_error_for_route!(crate::session::SessionLoadError);
|
||||
impl_from_error_for_route!(crate::oauth2::IdTokenSignatureError);
|
||||
impl_from_error_for_route!(super::callback::IntoCallbackDestinationError);
|
||||
impl_from_error_for_route!(super::callback::CallbackDestinationError);
|
||||
|
||||
impl IntoResponse for RouteError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let event_id = sentry::capture_error(&self);
|
||||
(
|
||||
SentryEventID::from(event_id),
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
SentryEventID::from(event_id),
|
||||
self.to_string(),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "handlers.oauth2.consent.get",
|
||||
name = "handlers.oauth2.authorization.consent.get",
|
||||
fields(grant.id = %grant_id),
|
||||
skip_all,
|
||||
err,
|
||||
@@ -142,17 +147,7 @@ pub(crate) async fn get(
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
|
||||
if res.valid() {
|
||||
let ctx = ConsentContext::new(grant, client)
|
||||
.with_session(session)
|
||||
.with_csrf(csrf_token.form_value())
|
||||
.with_language(locale);
|
||||
|
||||
let content = templates.render_consent(&ctx)?;
|
||||
|
||||
Ok((cookie_jar, Html(content)).into_response())
|
||||
} else {
|
||||
if !res.valid() {
|
||||
let ctx = PolicyViolationContext::for_authorization_grant(grant, client)
|
||||
.with_session(session)
|
||||
.with_csrf(csrf_token.form_value())
|
||||
@@ -160,12 +155,21 @@ pub(crate) async fn get(
|
||||
|
||||
let content = templates.render_policy_violation(&ctx)?;
|
||||
|
||||
Ok((cookie_jar, Html(content)).into_response())
|
||||
return Ok((cookie_jar, Html(content)).into_response());
|
||||
}
|
||||
|
||||
let ctx = ConsentContext::new(grant, client)
|
||||
.with_session(session)
|
||||
.with_csrf(csrf_token.form_value())
|
||||
.with_language(locale);
|
||||
|
||||
let content = templates.render_consent(&ctx)?;
|
||||
|
||||
Ok((cookie_jar, Html(content)).into_response())
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "handlers.oauth2.consent.post",
|
||||
name = "handlers.oauth2.authorization.consent.post",
|
||||
fields(grant.id = %grant_id),
|
||||
skip_all,
|
||||
err,
|
||||
@@ -175,6 +179,7 @@ pub(crate) async fn post(
|
||||
clock: BoxClock,
|
||||
PreferredLanguage(locale): PreferredLanguage,
|
||||
State(templates): State<Templates>,
|
||||
State(key_store): State<Keystore>,
|
||||
mut policy: Policy,
|
||||
mut repo: BoxRepository,
|
||||
activity_tracker: BoundActivityTracker,
|
||||
@@ -199,6 +204,8 @@ pub(crate) async fn post(
|
||||
SessionOrFallback::Fallback { response } => return Ok(response),
|
||||
};
|
||||
|
||||
let (csrf_token, cookie_jar) = cookie_jar.csrf_token(&clock, &mut rng);
|
||||
|
||||
let user_agent = user_agent.map(|ua| ua.to_string());
|
||||
|
||||
let grant = repo
|
||||
@@ -206,15 +213,16 @@ pub(crate) async fn post(
|
||||
.lookup(grant_id)
|
||||
.await?
|
||||
.ok_or(RouteError::GrantNotFound)?;
|
||||
let next = PostAuthAction::continue_grant(grant_id);
|
||||
let callback_destination = CallbackDestination::try_from(&grant)?;
|
||||
|
||||
let Some(session) = maybe_session else {
|
||||
let Some(browser_session) = maybe_session else {
|
||||
let next = PostAuthAction::continue_grant(grant_id);
|
||||
let login = mas_router::Login::and_then(next);
|
||||
return Ok((cookie_jar, url_builder.redirect(&login)).into_response());
|
||||
};
|
||||
|
||||
activity_tracker
|
||||
.record_browser_session(&clock, &session)
|
||||
.record_browser_session(&clock, &browser_session)
|
||||
.await;
|
||||
|
||||
let client = repo
|
||||
@@ -225,7 +233,7 @@ pub(crate) async fn post(
|
||||
|
||||
let res = policy
|
||||
.evaluate_authorization_grant(mas_policy::AuthorizationGrantInput {
|
||||
user: Some(&session.user),
|
||||
user: Some(&browser_session.user),
|
||||
client: &client,
|
||||
scope: &grant.scope,
|
||||
grant_type: mas_policy::GrantType::AuthorizationCode,
|
||||
@@ -237,10 +245,70 @@ pub(crate) async fn post(
|
||||
.await?;
|
||||
|
||||
if !res.valid() {
|
||||
return Err(RouteError::PolicyViolation);
|
||||
let ctx = PolicyViolationContext::for_authorization_grant(grant, client)
|
||||
.with_session(browser_session)
|
||||
.with_csrf(csrf_token.form_value())
|
||||
.with_language(locale);
|
||||
|
||||
let content = templates.render_policy_violation(&ctx)?;
|
||||
|
||||
return Ok((cookie_jar, Html(content)).into_response());
|
||||
}
|
||||
|
||||
// All good, let's start the session
|
||||
let session = repo
|
||||
.oauth2_session()
|
||||
.add_from_browser_session(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&client,
|
||||
&browser_session,
|
||||
grant.scope.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.fulfill(&clock, &session, grant)
|
||||
.await?;
|
||||
|
||||
let mut params = AuthorizationResponse::default();
|
||||
|
||||
// Did they request an ID token?
|
||||
if grant.response_type_id_token {
|
||||
// Fetch the last authentication
|
||||
let last_authentication = repo
|
||||
.browser_session()
|
||||
.get_last_authentication(&browser_session)
|
||||
.await?;
|
||||
|
||||
params.id_token = Some(generate_id_token(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&url_builder,
|
||||
&key_store,
|
||||
&client,
|
||||
Some(&grant),
|
||||
&browser_session,
|
||||
None,
|
||||
last_authentication.as_ref(),
|
||||
)?);
|
||||
}
|
||||
|
||||
// Did they request an auth code?
|
||||
if let Some(code) = grant.code {
|
||||
params.code = Some(code.code);
|
||||
}
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
Ok((cookie_jar, next.go_next(&url_builder)).into_response())
|
||||
activity_tracker
|
||||
.record_oauth2_session(&clock, &session)
|
||||
.await;
|
||||
|
||||
Ok((
|
||||
cookie_jar,
|
||||
callback_destination.go(&templates, &locale, params)?,
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
@@ -31,7 +31,7 @@ use self::callback::CallbackDestination;
|
||||
use crate::{BoundActivityTracker, PreferredLanguage, impl_from_error_for_route};
|
||||
|
||||
mod callback;
|
||||
pub mod complete;
|
||||
pub(crate) mod consent;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RouteError {
|
||||
@@ -172,80 +172,66 @@ pub(crate) async fn get(
|
||||
// Check if the request/request_uri/registration params are used. If so, reply
|
||||
// with the right error since we don't support them.
|
||||
if params.auth.request.is_some() {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RequestNotSupported),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RequestNotSupported),
|
||||
)?);
|
||||
}
|
||||
|
||||
if params.auth.request_uri.is_some() {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RequestUriNotSupported),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RequestUriNotSupported),
|
||||
)?);
|
||||
}
|
||||
|
||||
// Check if the client asked for a `token` response type, and bail out if it's
|
||||
// the case, since we don't support them
|
||||
if response_type.has_token() {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnsupportedResponseType),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnsupportedResponseType),
|
||||
)?);
|
||||
}
|
||||
|
||||
// If the client asked for a `id_token` response type, we must check if it can
|
||||
// use the `implicit` grant type
|
||||
if response_type.has_id_token() && !client.grant_types.contains(&GrantType::Implicit) {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnauthorizedClient),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnauthorizedClient),
|
||||
)?);
|
||||
}
|
||||
|
||||
if params.auth.registration.is_some() {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RegistrationNotSupported),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::RegistrationNotSupported),
|
||||
)?);
|
||||
}
|
||||
|
||||
// Fail early if prompt=none; we never let it go through
|
||||
if prompt.contains(&Prompt::None) {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::LoginRequired),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::LoginRequired),
|
||||
)?);
|
||||
}
|
||||
|
||||
let code: Option<AuthorizationCode> = if response_type.has_code() {
|
||||
// Check if it is allowed to use this grant type
|
||||
if !client.grant_types.contains(&GrantType::AuthorizationCode) {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnauthorizedClient),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::UnauthorizedClient),
|
||||
)?);
|
||||
}
|
||||
|
||||
// 32 random alphanumeric characters, about 190bit of entropy
|
||||
@@ -265,13 +251,11 @@ pub(crate) async fn get(
|
||||
// If the request had PKCE params but no code asked, it should get back with an
|
||||
// error
|
||||
if params.pkce.is_some() {
|
||||
return Ok(callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::InvalidRequest),
|
||||
)
|
||||
.await?);
|
||||
return Ok(callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::InvalidRequest),
|
||||
)?);
|
||||
}
|
||||
|
||||
None
|
||||
@@ -336,13 +320,11 @@ pub(crate) async fn get(
|
||||
Ok(r) => r,
|
||||
Err(err) => {
|
||||
tracing::error!(%err);
|
||||
callback_destination
|
||||
.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::ServerError),
|
||||
)
|
||||
.await?
|
||||
callback_destination.go(
|
||||
&templates,
|
||||
&locale,
|
||||
ClientError::from(ClientErrorCode::ServerError),
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ use mas_storage::{Clock, RepositoryAccess};
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod authorization;
|
||||
pub mod consent;
|
||||
pub mod device;
|
||||
pub mod discovery;
|
||||
pub mod introspection;
|
||||
|
||||
@@ -60,9 +60,7 @@ impl PostAuthAction {
|
||||
|
||||
pub fn go_next(&self, url_builder: &UrlBuilder) -> axum::response::Redirect {
|
||||
match self {
|
||||
Self::ContinueAuthorizationGrant { id } => {
|
||||
url_builder.redirect(&ContinueAuthorizationGrant(*id))
|
||||
}
|
||||
Self::ContinueAuthorizationGrant { id } => url_builder.redirect(&Consent(*id)),
|
||||
Self::ContinueDeviceCodeGrant { id } => {
|
||||
url_builder.redirect(&DeviceCodeConsent::new(*id))
|
||||
}
|
||||
@@ -521,21 +519,6 @@ impl SimpleRoute for AccountPasswordChange {
|
||||
const PATH: &'static str = "/account/password/change";
|
||||
}
|
||||
|
||||
/// `GET /authorize/{grant_id}`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContinueAuthorizationGrant(pub Ulid);
|
||||
|
||||
impl Route for ContinueAuthorizationGrant {
|
||||
type Query = ();
|
||||
fn route() -> &'static str {
|
||||
"/authorize/{grant_id}"
|
||||
}
|
||||
|
||||
fn path(&self) -> std::borrow::Cow<'static, str> {
|
||||
format!("/authorize/{}", self.0).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// `GET /consent/{grant_id}`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Consent(pub Ulid);
|
||||
|
||||
Reference in New Issue
Block a user