1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-21 23:00:50 +03:00

Move clients to the database

This commit is contained in:
Quentin Gliech
2022-03-08 17:33:25 +01:00
parent 19a81afe51
commit 62f633a716
33 changed files with 1926 additions and 867 deletions

View File

@@ -20,7 +20,7 @@ use hyper::{
http::uri::{Parts, PathAndQuery, Uri},
StatusCode,
};
use mas_config::{ClientsConfig, Encrypter};
use mas_config::Encrypter;
use mas_data_model::{
Authentication, AuthorizationCode, AuthorizationGrant, AuthorizationGrantStage, BrowserSession,
Pkce, StorageBackend, TokenType,
@@ -32,6 +32,7 @@ use mas_storage::{
authorization_grant::{
derive_session, fulfill_grant, get_grant_by_id, new_authorization_grant,
},
client::lookup_client_by_client_id,
refresh_token::add_refresh_token,
},
PostgresqlBackend,
@@ -41,7 +42,7 @@ use mas_warp_utils::{
errors::WrapError,
filters::{
self,
database::transaction,
database::{connection, transaction},
session::{optional_session, session},
with_templates,
},
@@ -49,19 +50,20 @@ use mas_warp_utils::{
use oauth2_types::{
errors::{
ErrorResponse, InvalidGrant, InvalidRequest, LoginRequired, OAuth2Error,
RegistrationNotSupported, RequestNotSupported, RequestUriNotSupported,
RegistrationNotSupported, RequestNotSupported, RequestUriNotSupported, UnauthorizedClient,
},
pkce,
prelude::*,
requests::{
AccessTokenResponse, AuthorizationRequest, AuthorizationResponse, Prompt, ResponseMode,
AccessTokenResponse, AuthorizationRequest, AuthorizationResponse, GrantType, Prompt,
ResponseMode,
},
scope::ScopeToken,
};
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sqlx::{PgExecutor, PgPool, Postgres, Transaction};
use sqlx::{pool::PoolConnection, PgConnection, PgPool, Postgres, Transaction};
use url::Url;
use warp::{
filters::BoxedFilter,
@@ -217,15 +219,10 @@ pub fn filter(
pool: &PgPool,
templates: &Templates,
encrypter: &Encrypter,
clients_config: &ClientsConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> {
let clients_config = clients_config.clone();
let clients_config_2 = clients_config.clone();
let authorize = warp::path!("oauth2" / "authorize")
.and(filters::trace::name("GET /oauth2/authorize"))
.and(warp::get())
.map(move || clients_config.clone())
.and(warp::query())
.and(optional_session(pool, encrypter))
.and(transaction(pool))
@@ -245,8 +242,8 @@ pub fn filter(
.recover(recover)
.unify()
.and(warp::query())
.and(warp::any().map(move || clients_config_2.clone()))
.and(with_templates(templates))
.and(connection(pool))
.and_then(actually_reply)
.boxed()
}
@@ -262,8 +259,8 @@ async fn recover(rejection: Rejection) -> Result<ReplyOrBackToClient, Rejection>
async fn actually_reply(
rep: ReplyOrBackToClient,
q: PartialParams,
clients: ClientsConfig,
templates: Templates,
mut conn: PoolConnection<Postgres>,
) -> Result<Box<dyn Reply>, Rejection> {
let (redirect_uri, response_mode, state, params) = match rep {
ReplyOrBackToClient::Reply(r) => return Ok(r),
@@ -281,15 +278,14 @@ async fn actually_reply(
..
} = q;
// First, disover the client
let client = client_id
.and_then(|client_id| clients.iter().find(|client| client.client_id == client_id));
let client = match client {
Some(client) => client,
None => return Ok(Box::new(html(templates.render_error(&error.into()).await?))),
let client_id = if let Some(client_id) = client_id {
client_id
} else {
return Ok(Box::new(html(templates.render_error(&error.into()).await?)));
};
let client = lookup_client_by_client_id(&mut conn, &client_id).await?;
let redirect_uri: Result<Option<Url>, _> = redirect_uri.map(|r| r.parse()).transpose();
let redirect_uri = match redirect_uri {
Ok(r) => r,
@@ -315,7 +311,6 @@ async fn actually_reply(
}
async fn get(
clients: ClientsConfig,
params: Params,
maybe_session: Option<BrowserSession<PostgresqlBackend>>,
mut txn: Transaction<'_, Postgres>,
@@ -337,15 +332,17 @@ async fn get(
}
// First, find out what client it is
let client = clients
.iter()
.find(|client| client.client_id == params.auth.client_id)
.ok_or_else(|| anyhow::anyhow!("could not find client"))
.wrap_error()?;
let client = lookup_client_by_client_id(&mut txn, &params.auth.client_id).await?;
// Check if it is allowed to use this grant type
if !client.grant_types.contains(&GrantType::AuthorizationCode) {
return Ok(ReplyOrBackToClient::Error(Box::new(UnauthorizedClient)));
}
let redirect_uri = client
.resolve_redirect_uri(&params.auth.redirect_uri)
.wrap_error()?;
.wrap_error()?
.clone();
let response_type = params.auth.response_type;
let response_mode =
resolve_response_mode(response_type, params.auth.response_mode).wrap_error()?;
@@ -392,8 +389,8 @@ async fn get(
let grant = new_authorization_grant(
&mut txn,
client.client_id.clone(),
redirect_uri.clone(),
client,
redirect_uri,
scope,
code,
params.auth.state,
@@ -471,10 +468,10 @@ impl ContinueAuthorizationGrant {
pub async fn fetch_authorization_grant(
&self,
executor: impl PgExecutor<'_>,
conn: &mut PgConnection,
) -> anyhow::Result<AuthorizationGrant<PostgresqlBackend>> {
let data = self.data.parse()?;
get_grant_by_id(executor, data).await
get_grant_by_id(conn, data).await
}
}

View File

@@ -12,11 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use mas_config::{ClientConfig, ClientsConfig, HttpConfig};
use mas_data_model::TokenType;
use mas_config::{Encrypter, HttpConfig};
use mas_data_model::{Client, TokenType};
use mas_iana::oauth::{OAuthClientAuthenticationMethod, OAuthTokenTypeHint};
use mas_storage::oauth2::{
access_token::lookup_active_access_token, refresh_token::lookup_active_refresh_token,
use mas_storage::{
oauth2::{
access_token::lookup_active_access_token, refresh_token::lookup_active_refresh_token,
},
PostgresqlBackend,
};
use mas_warp_utils::{
errors::WrapError,
@@ -29,7 +32,7 @@ use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
pub fn filter(
pool: &PgPool,
clients_config: &ClientsConfig,
encrypter: &Encrypter,
http_config: &HttpConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> {
let audience = UrlBuilder::from(http_config)
@@ -41,7 +44,7 @@ pub fn filter(
.and(
warp::post()
.and(connection(pool))
.and(client_authentication(clients_config, audience))
.and(client_authentication(pool, encrypter, audience))
.and_then(introspect)
.recover(recover)
.unify(),
@@ -67,7 +70,7 @@ const INACTIVE: IntrospectionResponse = IntrospectionResponse {
async fn introspect(
mut conn: PoolConnection<Postgres>,
auth: OAuthClientAuthenticationMethod,
client: ClientConfig,
client: Client<PostgresqlBackend>,
params: IntrospectionRequest,
) -> Result<Box<dyn Reply>, Rejection> {
// Token introspection is only allowed by confidential clients

View File

@@ -15,7 +15,7 @@
use std::sync::Arc;
use hyper::Method;
use mas_config::{ClientsConfig, Encrypter, HttpConfig};
use mas_config::{Encrypter, HttpConfig};
use mas_jose::StaticKeystore;
use mas_templates::Templates;
use mas_warp_utils::filters::cors::cors;
@@ -41,15 +41,14 @@ pub fn filter(
templates: &Templates,
key_store: &Arc<StaticKeystore>,
encrypter: &Encrypter,
clients_config: &ClientsConfig,
http_config: &HttpConfig,
) -> BoxedFilter<(impl Reply,)> {
let discovery = discovery(key_store.as_ref(), http_config);
let keys = keys(key_store);
let authorization = authorization(pool, templates, encrypter, clients_config);
let authorization = authorization(pool, templates, encrypter);
let userinfo = userinfo(pool);
let introspection = introspection(pool, clients_config, http_config);
let token = token(pool, key_store, clients_config, http_config);
let introspection = introspection(pool, encrypter, http_config);
let token = token(pool, encrypter, key_store, http_config);
let filter = discovery
.or(keys)

View File

@@ -19,8 +19,8 @@ use chrono::{DateTime, Duration, Utc};
use data_encoding::BASE64URL_NOPAD;
use headers::{CacheControl, Pragma};
use hyper::StatusCode;
use mas_config::{ClientConfig, ClientsConfig, HttpConfig};
use mas_data_model::{AuthorizationGrantStage, TokenType};
use mas_config::{Encrypter, HttpConfig};
use mas_data_model::{AuthorizationGrantStage, Client, TokenType};
use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod};
use mas_jose::{claims, DecodedJsonWebToken, SigningKeystore, StaticKeystore};
use mas_storage::{
@@ -33,7 +33,7 @@ use mas_storage::{
RefreshTokenLookupError,
},
},
DatabaseInconsistencyError,
DatabaseInconsistencyError, PostgresqlBackend,
};
use mas_warp_utils::{
errors::WrapError,
@@ -99,8 +99,8 @@ where
pub fn filter(
pool: &PgPool,
encrypter: &Encrypter,
key_store: &Arc<StaticKeystore>,
clients_config: &ClientsConfig,
http_config: &HttpConfig,
) -> BoxedFilter<(Box<dyn Reply>,)> {
let key_store = key_store.clone();
@@ -113,7 +113,7 @@ pub fn filter(
.and(filters::trace::name("POST /oauth2/token"))
.and(
warp::post()
.and(client_authentication(clients_config, audience))
.and(client_authentication(pool, encrypter, audience))
.and(warp::any().map(move || key_store.clone()))
.and(warp::any().map(move || issuer.clone()))
.and(connection(pool))
@@ -145,7 +145,7 @@ async fn recover(rejection: Rejection) -> Result<Box<dyn Reply>, Infallible> {
async fn token(
_auth: OAuthClientAuthenticationMethod,
client: ClientConfig,
client: Client<PostgresqlBackend>,
req: AccessTokenRequest,
key_store: Arc<StaticKeystore>,
issuer: Url,
@@ -185,7 +185,7 @@ fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> {
#[allow(clippy::too_many_lines)]
async fn authorization_code_grant(
grant: &AuthorizationCodeGrant,
client: &ClientConfig,
client: &Client<PostgresqlBackend>,
key_store: &StaticKeystore,
issuer: Url,
conn: &mut PoolConnection<Postgres>,
@@ -349,7 +349,7 @@ async fn authorization_code_grant(
async fn refresh_token_grant(
grant: &RefreshTokenGrant,
client: &ClientConfig,
client: &Client<PostgresqlBackend>,
conn: &mut PoolConnection<Postgres>,
) -> Result<AccessTokenResponse, Rejection> {
let mut txn = conn.begin().await.wrap_error()?;