1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-20 12:02:22 +03:00

Axum migration: /oauth2/token

This commit is contained in:
Quentin Gliech
2022-04-05 12:08:56 +02:00
parent 0f7484beee
commit 35310849c7
5 changed files with 188 additions and 182 deletions

View File

@@ -94,6 +94,7 @@ where
"/oauth2/introspect", "/oauth2/introspect",
post(self::oauth2::introspection::post), post(self::oauth2::introspection::post),
) )
.route("/oauth2/token", post(self::oauth2::token::post))
.fallback(mas_static_files::Assets) .fallback(mas_static_files::Assets)
.layer(Extension(pool.clone())) .layer(Extension(pool.clone()))
.layer(Extension(templates.clone())) .layer(Extension(templates.clone()))

View File

@@ -1,4 +1,4 @@
// Copyright 2021 The Matrix.org Foundation C.I.C. // Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.

View File

@@ -16,7 +16,7 @@
pub mod discovery; pub mod discovery;
pub mod introspection; pub mod introspection;
pub mod keys; pub mod keys;
// pub mod token; pub mod token;
pub mod userinfo; pub mod userinfo;
use hyper::{ use hyper::{

View File

@@ -1,4 +1,4 @@
// Copyright 2021 The Matrix.org Foundation C.I.C. // Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@@ -12,21 +12,30 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::{collections::HashMap, convert::Infallible, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use anyhow::Context; use anyhow::Context;
use axum::{extract::Extension, response::IntoResponse, Json};
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Duration, Utc};
use data_encoding::BASE64URL_NOPAD; use data_encoding::BASE64URL_NOPAD;
use headers::{CacheControl, Pragma}; use headers::{CacheControl, HeaderMap, HeaderMapExt, Pragma};
use hyper::StatusCode; use hyper::StatusCode;
use mas_config::{Encrypter, HttpConfig}; use mas_axum_utils::{
client_authorization::{ClientAuthorization, CredentialsVerificationError},
UrlBuilder,
};
use mas_config::Encrypter;
use mas_data_model::{AuthorizationGrantStage, Client, TokenType}; use mas_data_model::{AuthorizationGrantStage, Client, TokenType};
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_iana::jose::JsonWebSignatureAlg;
use mas_jose::{claims, DecodedJsonWebToken, SigningKeystore, StaticKeystore}; use mas_jose::{
claims::{self, ClaimError},
DecodedJsonWebToken, SigningKeystore, StaticKeystore,
};
use mas_storage::{ use mas_storage::{
oauth2::{ oauth2::{
access_token::{add_access_token, revoke_access_token}, access_token::{add_access_token, revoke_access_token},
authorization_grant::{exchange_grant, lookup_grant_by_code}, authorization_grant::{exchange_grant, lookup_grant_by_code},
client::ClientFetchError,
end_oauth_session, end_oauth_session,
refresh_token::{ refresh_token::{
add_refresh_token, lookup_active_refresh_token, replace_refresh_token, add_refresh_token, lookup_active_refresh_token, replace_refresh_token,
@@ -35,15 +44,7 @@ use mas_storage::{
}, },
DatabaseInconsistencyError, PostgresqlBackend, DatabaseInconsistencyError, PostgresqlBackend,
}; };
use mas_warp_utils::{
errors::WrapError,
filters::{self, client::client_authentication, database::connection, url_builder::UrlBuilder},
reply::with_typed_header,
};
use oauth2_types::{ use oauth2_types::{
errors::{
InvalidGrant, InvalidRequest, OAuth2Error, OAuth2ErrorCode, ServerError, UnauthorizedClient,
},
requests::{ requests::{
AccessTokenRequest, AccessTokenResponse, AuthorizationCodeGrant, RefreshTokenGrant, AccessTokenRequest, AccessTokenResponse, AuthorizationCodeGrant, RefreshTokenGrant,
}, },
@@ -53,15 +54,9 @@ use rand::thread_rng;
use serde::Serialize; use serde::Serialize;
use serde_with::{serde_as, skip_serializing_none}; use serde_with::{serde_as, skip_serializing_none};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use sqlx::{pool::PoolConnection, Acquire, PgPool, Postgres}; use sqlx::{PgPool, Postgres, Transaction};
use tracing::debug; use tracing::debug;
use url::Url; use url::Url;
use warp::{
filters::BoxedFilter,
reject::Reject,
reply::{json, with_status},
Filter, Rejection, Reply,
};
#[serde_as] #[serde_as]
#[skip_serializing_none] #[skip_serializing_none]
@@ -80,96 +75,107 @@ struct CustomClaims {
c_hash: String, c_hash: String,
} }
#[derive(Debug)] pub(crate) enum RouteError {
struct Error { Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
json: serde_json::Value, Anyhow(anyhow::Error),
status: StatusCode, BadRequest,
ClientNotFound,
ClientNotAllowed,
ClientCredentialsVerification(CredentialsVerificationError),
InvalidGrant,
UnauthorizedClient,
} }
impl Reject for Error {} impl From<ClientFetchError> for RouteError {
fn from(e: ClientFetchError) -> Self {
fn error<T, E>(e: E) -> Result<T, Rejection>
where
E: OAuth2ErrorCode + 'static,
{
let status = e.status();
let json = serde_json::to_value(e.into_response()).wrap_error()?;
Err(Error { json, status }.into())
}
pub fn filter(
pool: &PgPool,
encrypter: &Encrypter,
key_store: &Arc<StaticKeystore>,
http_config: &HttpConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> {
let key_store = key_store.clone();
let builder = UrlBuilder::from(http_config);
let audience = builder.oauth_token_endpoint().to_string();
let issuer = builder.oidc_issuer();
warp::path!("oauth2" / "token")
.and(filters::trace::name("POST /oauth2/token"))
.and(
warp::post()
.and(client_authentication(pool, encrypter, audience))
.and(warp::any().map(move || key_store.clone()))
.and(warp::any().map(move || issuer.clone()))
.and(connection(pool))
.and_then(token)
.recover(recover)
.unify(),
)
.boxed()
}
async fn recover(rejection: Rejection) -> Result<Box<dyn Reply>, Infallible> {
fn reply<E: OAuth2ErrorCode>(err: E) -> Box<dyn Reply> {
let status = err.status();
Box::new(with_status(warp::reply::json(&err.into_response()), status))
}
if let Some(Error { json, status }) = rejection.find::<Error>() {
return Ok(Box::new(with_status(warp::reply::json(json), *status)));
}
if let Some(e) = rejection.find::<RefreshTokenLookupError>() {
if e.not_found() { if e.not_found() {
return Ok(reply(InvalidGrant)); Self::ClientNotFound
} else {
Self::Internal(Box::new(e))
}
} }
};
Ok(reply(ServerError))
} }
async fn token( impl From<RefreshTokenLookupError> for RouteError {
_auth: OAuthClientAuthenticationMethod, fn from(e: RefreshTokenLookupError) -> Self {
client: Client<PostgresqlBackend>, if e.not_found() {
req: AccessTokenRequest, Self::InvalidGrant
key_store: Arc<StaticKeystore>, } else {
issuer: Url, Self::Internal(Box::new(e))
mut conn: PoolConnection<Postgres>, }
) -> Result<Box<dyn Reply>, Rejection> { }
let reply = match req { }
impl IntoResponse for RouteError {
fn into_response(self) -> axum::response::Response {
// TODO
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
impl From<sqlx::Error> for RouteError {
fn from(e: sqlx::Error) -> Self {
Self::Internal(Box::new(e))
}
}
impl From<ClaimError> for RouteError {
fn from(e: ClaimError) -> Self {
Self::Internal(Box::new(e))
}
}
impl From<anyhow::Error> for RouteError {
fn from(e: anyhow::Error) -> Self {
Self::Anyhow(e)
}
}
impl From<CredentialsVerificationError> for RouteError {
fn from(e: CredentialsVerificationError) -> Self {
Self::ClientCredentialsVerification(e)
}
}
pub(crate) async fn post(
client_authorization: ClientAuthorization<AccessTokenRequest>,
Extension(key_store): Extension<Arc<StaticKeystore>>,
Extension(url_builder): Extension<UrlBuilder>,
Extension(pool): Extension<PgPool>,
Extension(encrypter): Extension<Encrypter>,
) -> Result<impl IntoResponse, RouteError> {
let mut txn = pool.begin().await?;
let client = client_authorization.credentials.fetch(&mut txn).await?;
let method = client
.token_endpoint_auth_method
.ok_or(RouteError::ClientNotAllowed)?;
client_authorization
.credentials
.verify(&encrypter, method, &client)
.await?;
let form = client_authorization.form.ok_or(RouteError::BadRequest)?;
let reply = match form {
AccessTokenRequest::AuthorizationCode(grant) => { AccessTokenRequest::AuthorizationCode(grant) => {
let reply = authorization_code_grant(&grant, &client, &key_store, &url_builder, txn).await?
authorization_code_grant(&grant, &client, &key_store, issuer, &mut conn).await?;
json(&reply)
} }
AccessTokenRequest::RefreshToken(grant) => { AccessTokenRequest::RefreshToken(grant) => {
let reply = refresh_token_grant(&grant, &client, &mut conn).await?; refresh_token_grant(&grant, &client, txn).await?
json(&reply)
} }
_ => { _ => {
let reply = InvalidGrant.into_response(); return Err(RouteError::InvalidGrant);
json(&reply)
} }
}; };
let reply = with_typed_header(CacheControl::new().with_no_store(), reply); let mut headers = HeaderMap::new();
let reply = with_typed_header(Pragma::no_cache(), reply); headers.typed_insert(CacheControl::new().with_no_store());
Ok(Box::new(reply)) headers.typed_insert(Pragma::no_cache());
Ok((StatusCode::OK, headers, Json(reply)))
} }
fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> { fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> {
@@ -187,16 +193,12 @@ async fn authorization_code_grant(
grant: &AuthorizationCodeGrant, grant: &AuthorizationCodeGrant,
client: &Client<PostgresqlBackend>, client: &Client<PostgresqlBackend>,
key_store: &StaticKeystore, key_store: &StaticKeystore,
issuer: Url, url_builder: &UrlBuilder,
conn: &mut PoolConnection<Postgres>, mut txn: Transaction<'_, Postgres>,
) -> Result<AccessTokenResponse, Rejection> { ) -> Result<AccessTokenResponse, RouteError> {
// TODO: there is a bunch of unnecessary cloning here // TODO: there is a bunch of unnecessary cloning here
let mut txn = conn.begin().await.wrap_error()?;
// TODO: handle "not found" cases // TODO: handle "not found" cases
let authz_grant = lookup_grant_by_code(&mut txn, &grant.code) let authz_grant = lookup_grant_by_code(&mut txn, &grant.code).await?;
.await
.wrap_error()?;
// TODO: that's not a timestamp from the DB. Let's assume they are in sync // TODO: that's not a timestamp from the DB. Let's assume they are in sync
let now = Utc::now(); let now = Utc::now();
@@ -204,7 +206,7 @@ async fn authorization_code_grant(
let session = match authz_grant.stage { let session = match authz_grant.stage {
AuthorizationGrantStage::Cancelled { cancelled_at } => { AuthorizationGrantStage::Cancelled { cancelled_at } => {
debug!(%cancelled_at, "Authorization grant was cancelled"); debug!(%cancelled_at, "Authorization grant was cancelled");
return error(InvalidGrant); return Err(RouteError::InvalidGrant);
} }
AuthorizationGrantStage::Exchanged { AuthorizationGrantStage::Exchanged {
exchanged_at, exchanged_at,
@@ -216,15 +218,15 @@ async fn authorization_code_grant(
// Ending the session if the token was already exchanged more than 20s ago // Ending the session if the token was already exchanged more than 20s ago
if now - exchanged_at > Duration::seconds(20) { if now - exchanged_at > Duration::seconds(20) {
debug!("Ending potentially compromised session"); debug!("Ending potentially compromised session");
end_oauth_session(&mut txn, session).await.wrap_error()?; end_oauth_session(&mut txn, session).await?;
txn.commit().await.wrap_error()?; txn.commit().await?;
} }
return error(InvalidGrant); return Err(RouteError::InvalidGrant);
} }
AuthorizationGrantStage::Pending => { AuthorizationGrantStage::Pending => {
debug!("Authorization grant has not been fulfilled yet"); debug!("Authorization grant has not been fulfilled yet");
return error(InvalidGrant); return Err(RouteError::InvalidGrant);
} }
AuthorizationGrantStage::Fulfilled { AuthorizationGrantStage::Fulfilled {
ref session, ref session,
@@ -232,7 +234,7 @@ async fn authorization_code_grant(
} => { } => {
if now - fulfilled_at > Duration::minutes(10) { if now - fulfilled_at > Duration::minutes(10) {
debug!("Code exchange took more than 10 minutes"); debug!("Code exchange took more than 10 minutes");
return error(InvalidGrant); return Err(RouteError::InvalidGrant);
} }
session session
@@ -243,21 +245,20 @@ async fn authorization_code_grant(
let code = authz_grant let code = authz_grant
.code .code
.as_ref() .as_ref()
.ok_or(DatabaseInconsistencyError) .ok_or_else(|| anyhow::anyhow!(DatabaseInconsistencyError))?;
.wrap_error()?;
if client.client_id != session.client.client_id { if client.client_id != session.client.client_id {
return error(UnauthorizedClient); return Err(RouteError::UnauthorizedClient);
} }
match (code.pkce.as_ref(), grant.code_verifier.as_ref()) { match (code.pkce.as_ref(), grant.code_verifier.as_ref()) {
(None, None) => {} (None, None) => {}
// We have a challenge but no verifier (or vice-versa)? Bad request. // We have a challenge but no verifier (or vice-versa)? Bad request.
(Some(_), None) | (None, Some(_)) => return error(InvalidRequest), (Some(_), None) | (None, Some(_)) => return Err(RouteError::BadRequest),
// If we have both, we need to check the code validity // If we have both, we need to check the code validity
(Some(pkce), Some(verifier)) => { (Some(pkce), Some(verifier)) => {
if !pkce.verify(verifier) { if !pkce.verify(verifier) {
return error(InvalidRequest); return Err(RouteError::BadRequest);
} }
} }
}; };
@@ -273,58 +274,33 @@ async fn authorization_code_grant(
) )
}; };
let access_token = add_access_token(&mut txn, session, &access_token_str, ttl) let access_token = add_access_token(&mut txn, session, &access_token_str, ttl).await?;
.await
.wrap_error()?;
let _refresh_token = add_refresh_token(&mut txn, session, access_token, &refresh_token_str) let _refresh_token =
.await add_refresh_token(&mut txn, session, access_token, &refresh_token_str).await?;
.wrap_error()?;
let id_token = if session.scope.contains(&scope::OPENID) { let id_token = if session.scope.contains(&scope::OPENID) {
let mut claims = HashMap::new(); let mut claims = HashMap::new();
let now = Utc::now(); let now = Utc::now();
claims::ISS claims::ISS.insert(&mut claims, url_builder.oidc_issuer().to_string())?;
.insert(&mut claims, issuer.to_string()) claims::SUB.insert(&mut claims, &browser_session.user.sub)?;
.wrap_error()?; claims::AUD.insert(&mut claims, client.client_id.clone())?;
claims::SUB claims::IAT.insert(&mut claims, now)?;
.insert(&mut claims, &browser_session.user.sub) claims::EXP.insert(&mut claims, now + Duration::hours(1))?;
.wrap_error()?;
claims::AUD
.insert(&mut claims, client.client_id.clone())
.wrap_error()?;
claims::IAT.insert(&mut claims, now).wrap_error()?;
claims::EXP
.insert(&mut claims, now + Duration::hours(1))
.wrap_error()?;
if let Some(ref nonce) = authz_grant.nonce { if let Some(ref nonce) = authz_grant.nonce {
claims::NONCE claims::NONCE.insert(&mut claims, nonce.clone())?;
.insert(&mut claims, nonce.clone())
.wrap_error()?;
} }
if let Some(ref last_authentication) = browser_session.last_authentication { if let Some(ref last_authentication) = browser_session.last_authentication {
claims::AUTH_TIME claims::AUTH_TIME.insert(&mut claims, last_authentication.created_at)?;
.insert(&mut claims, last_authentication.created_at)
.wrap_error()?;
} }
claims::AT_HASH claims::AT_HASH.insert(&mut claims, hash(Sha256::new(), &access_token_str)?)?;
.insert( claims::C_HASH.insert(&mut claims, hash(Sha256::new(), &grant.code)?)?;
&mut claims,
hash(Sha256::new(), &access_token_str).wrap_error()?,
)
.wrap_error()?;
claims::C_HASH
.insert(&mut claims, hash(Sha256::new(), &grant.code).wrap_error()?)
.wrap_error()?;
let header = key_store let header = key_store.prepare_header(JsonWebSignatureAlg::Rs256).await?;
.prepare_header(JsonWebSignatureAlg::Rs256)
.await
.wrap_error()?;
let id_token = DecodedJsonWebToken::new(header, claims); let id_token = DecodedJsonWebToken::new(header, claims);
let id_token = id_token.sign(key_store).await.wrap_error()?; let id_token = id_token.sign(key_store).await?;
Some(id_token.serialize()) Some(id_token.serialize())
} else { } else {
@@ -340,9 +316,9 @@ async fn authorization_code_grant(
params = params.with_id_token(id_token); params = params.with_id_token(id_token);
} }
exchange_grant(&mut txn, authz_grant).await.wrap_error()?; exchange_grant(&mut txn, authz_grant).await?;
txn.commit().await.wrap_error()?; txn.commit().await?;
Ok(params) Ok(params)
} }
@@ -350,15 +326,14 @@ async fn authorization_code_grant(
async fn refresh_token_grant( async fn refresh_token_grant(
grant: &RefreshTokenGrant, grant: &RefreshTokenGrant,
client: &Client<PostgresqlBackend>, client: &Client<PostgresqlBackend>,
conn: &mut PoolConnection<Postgres>, mut txn: Transaction<'_, Postgres>,
) -> Result<AccessTokenResponse, Rejection> { ) -> Result<AccessTokenResponse, RouteError> {
let mut txn = conn.begin().await.wrap_error()?;
let (refresh_token, session) = let (refresh_token, session) =
lookup_active_refresh_token(&mut txn, &grant.refresh_token).await?; lookup_active_refresh_token(&mut txn, &grant.refresh_token).await?;
if client.client_id != session.client.client_id { if client.client_id != session.client.client_id {
// As per https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 // As per https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
return error(InvalidGrant); return Err(RouteError::InvalidGrant);
} }
let ttl = Duration::minutes(5); let ttl = Duration::minutes(5);
@@ -370,23 +345,15 @@ async fn refresh_token_grant(
) )
}; };
let new_access_token = add_access_token(&mut txn, &session, &access_token_str, ttl) let new_access_token = add_access_token(&mut txn, &session, &access_token_str, ttl).await?;
.await
.wrap_error()?;
let new_refresh_token = let new_refresh_token =
add_refresh_token(&mut txn, &session, new_access_token, &refresh_token_str) add_refresh_token(&mut txn, &session, new_access_token, &refresh_token_str).await?;
.await
.wrap_error()?;
replace_refresh_token(&mut txn, &refresh_token, &new_refresh_token) replace_refresh_token(&mut txn, &refresh_token, &new_refresh_token).await?;
.await
.wrap_error()?;
if let Some(access_token) = refresh_token.access_token { if let Some(access_token) = refresh_token.access_token {
revoke_access_token(&mut txn, &access_token) revoke_access_token(&mut txn, &access_token).await?;
.await
.wrap_error()?;
} }
let params = AccessTokenResponse::new(access_token_str) let params = AccessTokenResponse::new(access_token_str)
@@ -394,7 +361,7 @@ async fn refresh_token_grant(
.with_refresh_token(refresh_token_str) .with_refresh_token(refresh_token_str)
.with_scope(session.scope); .with_scope(session.scope);
txn.commit().await.wrap_error()?; txn.commit().await?;
Ok(params) Ok(params)
} }

View File

@@ -16,6 +16,11 @@ use http::status::StatusCode;
use serde::ser::{Serialize, SerializeMap}; use serde::ser::{Serialize, SerializeMap};
use url::Url; use url::Url;
pub struct ClientError {
pub error: &'static str,
pub error_description: &'static str,
}
pub trait OAuth2Error: std::fmt::Debug + Send + Sync { pub trait OAuth2Error: std::fmt::Debug + Send + Sync {
/// A single ASCII error code. /// A single ASCII error code.
/// ///
@@ -148,6 +153,15 @@ macro_rules! oauth2_error_error {
}; };
} }
macro_rules! oauth2_error_const {
($const:ident, $err:literal, $description:expr) => {
pub const $const: ClientError = ClientError {
error: $err,
error_description: $description,
};
};
}
macro_rules! oauth2_error_description { macro_rules! oauth2_error_description {
($description:expr) => { ($description:expr) => {
fn description(&self) -> Option<String> { fn description(&self) -> Option<String> {
@@ -157,32 +171,36 @@ macro_rules! oauth2_error_description {
} }
macro_rules! oauth2_error { macro_rules! oauth2_error {
($name:ident, $err:literal => $description:expr) => { ($name:ident, $const:ident, $err:literal => $description:expr) => {
oauth2_error_const!($const, $err, $description);
oauth2_error_def!($name); oauth2_error_def!($name);
impl $crate::errors::OAuth2Error for $name { impl $crate::errors::OAuth2Error for $name {
oauth2_error_error!($err); oauth2_error_error!($err);
oauth2_error_description!(indoc::indoc! {$description}); oauth2_error_description!(indoc::indoc! {$description});
} }
}; };
($name:ident, $err:literal) => { ($name:ident, $const:ident, $err:literal) => {
oauth2_error_def!($name); oauth2_error_def!($name);
impl $crate::errors::OAuth2Error for $name { impl $crate::errors::OAuth2Error for $name {
oauth2_error_error!($err); oauth2_error_error!($err);
} }
}; };
($name:ident, code: $code:ident, $err:literal => $description:expr) => { ($name:ident, $const:ident, code: $code:ident, $err:literal => $description:expr) => {
oauth2_error!($name, $err => $description); oauth2_error!($name, $const, $err => $description);
oauth2_error_status!($name, $code); oauth2_error_status!($name, $code);
}; };
($name:ident, code: $code:ident, $err:literal) => { ($name:ident, $const:ident, code: $code:ident, $err:literal) => {
oauth2_error!($name, $err); oauth2_error!($name, $const, $err);
oauth2_error_status!($name, $code); oauth2_error_status!($name, $code);
}; };
} }
pub mod rfc6749 { pub mod rfc6749 {
use super::ClientError;
oauth2_error! { oauth2_error! {
InvalidRequest, InvalidRequest,
INVALID_REQUEST,
code: BAD_REQUEST, code: BAD_REQUEST,
"invalid_request" => "invalid_request" =>
"The request is missing a required parameter, includes an invalid parameter value, \ "The request is missing a required parameter, includes an invalid parameter value, \
@@ -191,6 +209,7 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
InvalidClient, InvalidClient,
INVALID_CLIENT,
code: BAD_REQUEST, code: BAD_REQUEST,
"invalid_client" => "invalid_client" =>
"Client authentication failed." "Client authentication failed."
@@ -198,12 +217,14 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
InvalidGrant, InvalidGrant,
INVALID_GRANT,
code: BAD_REQUEST, code: BAD_REQUEST,
"invalid_grant" "invalid_grant"
} }
oauth2_error! { oauth2_error! {
UnauthorizedClient, UnauthorizedClient,
UNAUTHORIZED_CLIENT,
code: BAD_REQUEST, code: BAD_REQUEST,
"unauthorized_client" => "unauthorized_client" =>
"The client is not authorized to request an access token using this method." "The client is not authorized to request an access token using this method."
@@ -211,6 +232,7 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
UnsupportedGrantType, UnsupportedGrantType,
UNSUPPORTED_GRANT_TYPE,
code: BAD_REQUEST, code: BAD_REQUEST,
"unsupported_grant_type" => "unsupported_grant_type" =>
"The authorization grant type is not supported by the authorization server." "The authorization grant type is not supported by the authorization server."
@@ -218,18 +240,21 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
AccessDenied, AccessDenied,
ACCESS_DENIED,
"access_denied" => "access_denied" =>
"The resource owner or authorization server denied the request." "The resource owner or authorization server denied the request."
} }
oauth2_error! { oauth2_error! {
UnsupportedResponseType, UnsupportedResponseType,
UNSUPPORTED_RESPONSE_TYPE,
"unsupported_response_type" => "unsupported_response_type" =>
"The authorization server does not support obtaining an access token using this method." "The authorization server does not support obtaining an access token using this method."
} }
oauth2_error! { oauth2_error! {
InvalidScope, InvalidScope,
INVALID_SCOPE,
code: BAD_REQUEST, code: BAD_REQUEST,
"invalid_scope" => "invalid_scope" =>
"The requested scope is invalid, unknown, or malformed." "The requested scope is invalid, unknown, or malformed."
@@ -237,6 +262,7 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
ServerError, ServerError,
SERVER_ERROR,
code: INTERNAL_SERVER_ERROR, code: INTERNAL_SERVER_ERROR,
"server_error" => "server_error" =>
"The authorization server encountered an unexpected \ "The authorization server encountered an unexpected \
@@ -245,6 +271,7 @@ pub mod rfc6749 {
oauth2_error! { oauth2_error! {
TemporarilyUnavailable, TemporarilyUnavailable,
TEMPORARILY_UNAVAILABLE,
"temporarily_unavailable" => "temporarily_unavailable" =>
"The authorization server is currently unable to handle \ "The authorization server is currently unable to handle \
the request due to a temporary overloading or maintenance \ the request due to a temporary overloading or maintenance \
@@ -253,54 +280,65 @@ pub mod rfc6749 {
} }
pub mod oidc_core { pub mod oidc_core {
use super::ClientError;
oauth2_error! { oauth2_error! {
InteractionRequired, InteractionRequired,
INTERACTION_REQUIRED,
"interaction_required" => "interaction_required" =>
"The Authorization Server requires End-User interaction of some form to proceed." "The Authorization Server requires End-User interaction of some form to proceed."
} }
oauth2_error! { oauth2_error! {
LoginRequired, LoginRequired,
LOGIN_REQUIRED,
"login_required" => "login_required" =>
"The Authorization Server requires End-User authentication." "The Authorization Server requires End-User authentication."
} }
oauth2_error! { oauth2_error! {
AccountSelectionRequired, AccountSelectionRequired,
ACCOUNT_SELECTION_REQUIRED,
"account_selection_required" "account_selection_required"
} }
oauth2_error! { oauth2_error! {
ConsentRequired, ConsentRequired,
CONSENT_REQUIRED,
"consent_required" "consent_required"
} }
oauth2_error! { oauth2_error! {
InvalidRequestUri, InvalidRequestUri,
INVALID_REQUEST_URI,
"invalid_request_uri" => "invalid_request_uri" =>
"The request_uri in the Authorization Request returns an error or contains invalid data. " "The request_uri in the Authorization Request returns an error or contains invalid data. "
} }
oauth2_error! { oauth2_error! {
InvalidRequestObject, InvalidRequestObject,
INVALID_REQUEST_OBJECT,
"invalid_request_object" => "invalid_request_object" =>
"The request parameter contains an invalid Request Object." "The request parameter contains an invalid Request Object."
} }
oauth2_error! { oauth2_error! {
RequestNotSupported, RequestNotSupported,
REQUEST_NOT_SUPPORTED,
"request_not_supported" => "request_not_supported" =>
"The provider does not support use of the request parameter." "The provider does not support use of the request parameter."
} }
oauth2_error! { oauth2_error! {
RequestUriNotSupported, RequestUriNotSupported,
REQUEST_URI_NOT_SUPPORTED,
"request_uri_not_supported" => "request_uri_not_supported" =>
"The provider does not support use of the request_uri parameter." "The provider does not support use of the request_uri parameter."
} }
oauth2_error! { oauth2_error! {
RegistrationNotSupported, RegistrationNotSupported,
REGISTRATION_NOT_SUPPORTED,
"registration_not_supported" => "registration_not_supported" =>
"The provider does not support use of the registration parameter." "The provider does not support use of the registration parameter."
} }