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

Support signed userinfo responses

This commit is contained in:
Quentin Gliech
2022-04-21 11:49:49 +02:00
parent 0c8656f464
commit 25193ebaa5
9 changed files with 127 additions and 12 deletions

View File

@@ -59,14 +59,20 @@ use oauth2_types::{
use rand::{distributions::Alphanumeric, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use sqlx::{PgConnection, PgPool, Postgres, Transaction};
use thiserror::Error;
use url::Url;
use crate::views::{LoginRequest, PostAuthAction, ReauthRequest, RegisterRequest};
#[derive(Debug, Error)]
pub enum RouteError {
#[error(transparent)]
Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
#[error(transparent)]
Anyhow(anyhow::Error),
#[error("could not find client")]
ClientNotFound,
#[error("invalid redirect uri")]
InvalidRedirectUri,
}
@@ -218,6 +224,7 @@ fn resolve_response_mode(
}
#[allow(clippy::too_many_lines)]
#[tracing::instrument(skip_all, err)]
pub(crate) async fn get(
Extension(templates): Extension<Templates>,
Extension(pool): Extension<PgPool>,
@@ -413,7 +420,10 @@ pub(crate) async fn get(
let response = match res {
Ok(r) => r,
Err(_e) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
Err(err) => {
tracing::error!(%err);
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
};
Ok((cookie_jar, response).into_response())

View File

@@ -105,7 +105,8 @@ pub(crate) async fn get(
let subject_types_supported = Some(vec![SubjectType::Public]);
let id_token_signing_alg_values_supported = jwt_signing_alg_values_supported;
let id_token_signing_alg_values_supported = jwt_signing_alg_values_supported.clone();
let userinfo_signing_alg_values_supported = jwt_signing_alg_values_supported;
let display_values_supported = Some(vec![Display::Page]);
@@ -148,6 +149,7 @@ pub(crate) async fn get(
userinfo_endpoint,
subject_types_supported,
id_token_signing_alg_values_supported,
userinfo_signing_alg_values_supported,
display_values_supported,
claim_types_supported,
claims_supported,

View File

@@ -75,6 +75,7 @@ pub(crate) async fn post(
body.jwks_uri.as_ref(),
body.jwks.as_ref(),
body.id_token_signed_response_alg,
body.userinfo_signed_response_alg,
body.token_endpoint_auth_method,
body.token_endpoint_auth_signing_alg,
body.initiate_login_uri.as_ref(),

View File

@@ -317,7 +317,13 @@ async fn authorization_code_grant(
claims::AT_HASH.insert(&mut claims, hash(Sha256::new(), &access_token_str)?)?;
claims::C_HASH.insert(&mut claims, hash(Sha256::new(), &grant.code)?)?;
let header = key_store.prepare_header(JsonWebSignatureAlg::Rs256).await?;
let header = key_store
.prepare_header(
client
.id_token_signed_response_alg
.unwrap_or(JsonWebSignatureAlg::Rs256),
)
.await?;
let id_token = DecodedJsonWebToken::new(header, claims);
let id_token = id_token.sign(key_store).await?;

View File

@@ -12,12 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::Arc;
use axum::{
extract::Extension,
response::{IntoResponse, Response},
Json,
Json, TypedHeader,
};
use mas_axum_utils::{internal_error, user_authorization::UserAuthorization};
use headers::ContentType;
use hyper::StatusCode;
use mas_axum_utils::{internal_error, user_authorization::UserAuthorization, UrlBuilder};
use mas_jose::{DecodedJsonWebToken, SigningKeystore, StaticKeystore};
use mime::Mime;
use oauth2_types::scope;
use serde::Serialize;
use serde_with::skip_serializing_none;
@@ -32,10 +38,21 @@ struct UserInfo {
email_verified: Option<bool>,
}
#[derive(Serialize)]
struct SignedUserInfo {
iss: String,
aud: String,
#[serde(flatten)]
user_info: UserInfo,
}
pub async fn get(
Extension(url_builder): Extension<UrlBuilder>,
Extension(pool): Extension<PgPool>,
Extension(key_store): Extension<Arc<StaticKeystore>>,
user_authorization: UserAuthorization,
) -> Result<impl IntoResponse, Response> {
) -> Result<Response, Response> {
// TODO: error handling
let mut conn = pool
.acquire()
.await
@@ -48,7 +65,7 @@ pub async fn get(
.map_err(IntoResponse::into_response)?;
let user = session.browser_session.user;
let mut res = UserInfo {
let mut user_info = UserInfo {
sub: user.sub,
username: user.username,
email: None,
@@ -57,10 +74,36 @@ pub async fn get(
if session.scope.contains(&scope::EMAIL) {
if let Some(email) = user.primary_email {
res.email_verified = Some(email.confirmed_at.is_some());
res.email = Some(email.email);
user_info.email_verified = Some(email.confirmed_at.is_some());
user_info.email = Some(email.email);
}
}
Ok(Json(res))
if let Some(alg) = session.client.userinfo_signed_response_alg {
let header = key_store
.prepare_header(alg)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
.map_err(IntoResponse::into_response)?;
let user_info = SignedUserInfo {
iss: url_builder.oidc_issuer().to_string(),
aud: session.client.client_id,
user_info,
};
let user_info = DecodedJsonWebToken::new(header, user_info);
let user_info = user_info
.sign(key_store.as_ref())
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
.map_err(IntoResponse::into_response)?;
let token = user_info.serialize();
let application_jwt: Mime = "application/jwt".parse().unwrap();
let content_type = ContentType::from(application_jwt);
Ok((TypedHeader(content_type), token).into_response())
} else {
Ok(Json(user_info).into_response())
}
}