You've already forked authentication-service
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:
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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?;
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user