1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

Lookup and save upstream links

This commit is contained in:
Quentin Gliech
2022-11-23 16:31:47 +01:00
parent e8c8d0bf8a
commit cde9187adc
6 changed files with 293 additions and 12 deletions

View File

@ -21,13 +21,17 @@ use axum_extra::extract::PrivateCookieJar;
use hyper::StatusCode;
use mas_axum_utils::http_client_factory::HttpClientFactory;
use mas_http::ClientInitError;
use mas_jose::claims::ClaimError;
use mas_keystore::{Encrypter, Keystore};
use mas_oidc_client::{
error::{DiscoveryError, JwksError, TokenAuthorizationCodeError},
requests::{authorization_code::AuthorizationValidationData, jose::JwtVerificationData},
};
use mas_router::UrlBuilder;
use mas_storage::{upstream_oauth2::lookup_session, LookupResultExt};
use mas_storage::{
upstream_oauth2::{add_link, complete_session, lookup_link_by_subject, lookup_session},
GenericLookupError, LookupResultExt,
};
use oauth2_types::errors::ClientErrorCode;
use serde::Deserialize;
use sqlx::PgPool;
@ -66,9 +70,21 @@ pub(crate) enum RouteError {
#[error("Provider mismatch")]
ProviderMismatch,
#[error("Session already completed")]
AlreadyCompleted,
#[error("State parameter mismatch")]
StateMismatch,
#[error("Missing ID token")]
MissingIDToken,
#[error("Invalid ID token")]
InvalidIdToken(#[from] ClaimError),
#[error("User already linked")]
UserAlreadyLinked,
#[error("Error from the provider: {error}")]
ClientError {
error: ClientErrorCode,
@ -88,6 +104,12 @@ pub(crate) enum RouteError {
Anyhow(#[from] anyhow::Error),
}
impl From<GenericLookupError> for RouteError {
fn from(e: GenericLookupError) -> Self {
Self::InternalError(Box::new(e))
}
}
impl From<sqlx::Error> for RouteError {
fn from(e: sqlx::Error) -> Self {
Self::InternalError(Box::new(e))
@ -182,6 +204,11 @@ pub(crate) async fn get(
return Err(RouteError::StateMismatch);
}
if session.completed() {
// The session was already completed
return Err(RouteError::AlreadyCompleted);
}
// Let's extract the code from the params, and return if there was an error
let code = match params.code_or_error {
CodeOrError::Error {
@ -224,10 +251,11 @@ pub(crate) async fn get(
let redirect_uri = url_builder.upstream_oauth_callback(provider.id);
// TODO: all that should be borrowed
let validation_data = AuthorizationValidationData {
state: session.state,
nonce: session.nonce,
code_challenge_verifier: session.code_challenge_verifier,
state: session.state.clone(),
nonce: session.nonce.clone(),
code_challenge_verifier: session.code_challenge_verifier.clone(),
redirect_uri,
};
@ -243,7 +271,7 @@ pub(crate) async fn get(
.http_service("upstream-exchange-code")
.await?;
let (response, _id_token) =
let (response, id_token) =
mas_oidc_client::requests::authorization_code::access_token_with_authorization_code(
&http_service,
client_credentials,
@ -256,5 +284,30 @@ pub(crate) async fn get(
)
.await?;
let (_header, mut id_token) = id_token.ok_or(RouteError::MissingIDToken)?.into_parts();
// Extract the subject from the id_token
let subject = mas_jose::claims::SUB.extract_required(&mut id_token)?;
// Look for an existing link
let maybe_link = lookup_link_by_subject(&mut txn, &provider, &subject)
.await
.to_option()?;
let link = if let Some((link, maybe_user_id)) = maybe_link {
if let Some(_user_id) = maybe_user_id {
// TODO: Here we should login if the user is linked
return Err(RouteError::UserAlreadyLinked);
}
link
} else {
add_link(&mut txn, &mut rng, &clock, &provider, subject).await?
};
let _session = complete_session(&mut txn, &clock, session, &link).await?;
txn.commit().await?;
Ok(Json(response))
}