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
Axum migration: /oauth2/keys.json and /.well-known/openid-configuration
This commit is contained in:
@@ -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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,9 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mas_config::HttpConfig;
|
||||
use axum::{extract::Extension, response::IntoResponse, Json};
|
||||
use mas_axum_utils::UrlBuilder;
|
||||
use mas_iana::{
|
||||
jose::JsonWebSignatureAlg,
|
||||
oauth::{
|
||||
@@ -22,88 +23,71 @@ use mas_iana::{
|
||||
PkceCodeChallengeMethod,
|
||||
},
|
||||
};
|
||||
use mas_jose::SigningKeystore;
|
||||
use mas_warp_utils::filters::{self, url_builder::UrlBuilder};
|
||||
use mas_jose::{SigningKeystore, StaticKeystore};
|
||||
use oauth2_types::{
|
||||
oidc::{ClaimType, Metadata, SubjectType},
|
||||
requests::{Display, GrantType, Prompt, ResponseMode},
|
||||
scope,
|
||||
};
|
||||
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn filter(
|
||||
key_store: &impl SigningKeystore,
|
||||
http_config: &HttpConfig,
|
||||
) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||
let builder = UrlBuilder::from(http_config);
|
||||
|
||||
pub(crate) async fn get(
|
||||
Extension(key_store): Extension<Arc<StaticKeystore>>,
|
||||
Extension(url_builder): Extension<UrlBuilder>,
|
||||
) -> impl IntoResponse {
|
||||
// This is how clients can authenticate
|
||||
let client_auth_methods_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(OAuthClientAuthenticationMethod::ClientSecretBasic);
|
||||
s.insert(OAuthClientAuthenticationMethod::ClientSecretPost);
|
||||
s.insert(OAuthClientAuthenticationMethod::ClientSecretJwt);
|
||||
s.insert(OAuthClientAuthenticationMethod::PrivateKeyJwt);
|
||||
s.insert(OAuthClientAuthenticationMethod::None);
|
||||
s
|
||||
});
|
||||
let client_auth_methods_supported = Some(vec![
|
||||
OAuthClientAuthenticationMethod::ClientSecretBasic,
|
||||
OAuthClientAuthenticationMethod::ClientSecretPost,
|
||||
OAuthClientAuthenticationMethod::ClientSecretJwt,
|
||||
OAuthClientAuthenticationMethod::PrivateKeyJwt,
|
||||
OAuthClientAuthenticationMethod::None,
|
||||
]);
|
||||
|
||||
let client_auth_signing_alg_values_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(JsonWebSignatureAlg::Hs256);
|
||||
s.insert(JsonWebSignatureAlg::Hs384);
|
||||
s.insert(JsonWebSignatureAlg::Hs512);
|
||||
s.insert(JsonWebSignatureAlg::Rs256);
|
||||
s.insert(JsonWebSignatureAlg::Rs384);
|
||||
s.insert(JsonWebSignatureAlg::Rs512);
|
||||
s
|
||||
});
|
||||
let client_auth_signing_alg_values_supported = Some(vec![
|
||||
JsonWebSignatureAlg::Hs256,
|
||||
JsonWebSignatureAlg::Hs384,
|
||||
JsonWebSignatureAlg::Hs512,
|
||||
JsonWebSignatureAlg::Rs256,
|
||||
JsonWebSignatureAlg::Rs384,
|
||||
JsonWebSignatureAlg::Rs512,
|
||||
]);
|
||||
|
||||
// This is how we can sign stuff
|
||||
let jwt_signing_alg_values_supported = Some(key_store.supported_algorithms());
|
||||
let jwt_signing_alg_values_supported = Some({
|
||||
let algs = key_store.supported_algorithms();
|
||||
let mut algs = Vec::from_iter(algs);
|
||||
algs.sort();
|
||||
algs
|
||||
});
|
||||
|
||||
// Prepare all the endpoints
|
||||
let issuer = Some(builder.oidc_issuer());
|
||||
let authorization_endpoint = Some(builder.oauth_authorization_endpoint());
|
||||
let token_endpoint = Some(builder.oauth_token_endpoint());
|
||||
let jwks_uri = Some(builder.jwks_uri());
|
||||
let introspection_endpoint = Some(builder.oauth_introspection_endpoint());
|
||||
let userinfo_endpoint = Some(builder.oidc_userinfo_endpoint());
|
||||
let issuer = Some(url_builder.oidc_issuer());
|
||||
let authorization_endpoint = Some(url_builder.oauth_authorization_endpoint());
|
||||
let token_endpoint = Some(url_builder.oauth_token_endpoint());
|
||||
let jwks_uri = Some(url_builder.jwks_uri());
|
||||
let introspection_endpoint = Some(url_builder.oauth_introspection_endpoint());
|
||||
let userinfo_endpoint = Some(url_builder.oidc_userinfo_endpoint());
|
||||
|
||||
let scopes_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(scope::OPENID.to_string());
|
||||
s.insert(scope::EMAIL.to_string());
|
||||
s
|
||||
});
|
||||
let scopes_supported = Some(vec![scope::OPENID.to_string(), scope::EMAIL.to_string()]);
|
||||
|
||||
let response_types_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::Code);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::Token);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::IdToken);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::CodeToken);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::CodeIdToken);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::IdTokenToken);
|
||||
s.insert(OAuthAuthorizationEndpointResponseType::CodeIdToken);
|
||||
s
|
||||
});
|
||||
let response_types_supported = Some(vec![
|
||||
OAuthAuthorizationEndpointResponseType::Code,
|
||||
OAuthAuthorizationEndpointResponseType::Token,
|
||||
OAuthAuthorizationEndpointResponseType::IdToken,
|
||||
OAuthAuthorizationEndpointResponseType::CodeToken,
|
||||
OAuthAuthorizationEndpointResponseType::CodeIdToken,
|
||||
OAuthAuthorizationEndpointResponseType::IdTokenToken,
|
||||
OAuthAuthorizationEndpointResponseType::CodeIdToken,
|
||||
]);
|
||||
|
||||
let response_modes_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(ResponseMode::FormPost);
|
||||
s.insert(ResponseMode::Query);
|
||||
s.insert(ResponseMode::Fragment);
|
||||
s
|
||||
});
|
||||
let response_modes_supported = Some(vec![
|
||||
ResponseMode::FormPost,
|
||||
ResponseMode::Query,
|
||||
ResponseMode::Fragment,
|
||||
]);
|
||||
|
||||
let grant_types_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(GrantType::AuthorizationCode);
|
||||
s.insert(GrantType::RefreshToken);
|
||||
s
|
||||
});
|
||||
let grant_types_supported = Some(vec![GrantType::AuthorizationCode, GrantType::RefreshToken]);
|
||||
|
||||
let token_endpoint_auth_methods_supported = client_auth_methods_supported.clone();
|
||||
let token_endpoint_auth_signing_alg_values_supported =
|
||||
@@ -113,58 +97,36 @@ pub(super) fn filter(
|
||||
let introspection_endpoint_auth_signing_alg_values_supported =
|
||||
client_auth_signing_alg_values_supported;
|
||||
|
||||
let code_challenge_methods_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(PkceCodeChallengeMethod::Plain);
|
||||
s.insert(PkceCodeChallengeMethod::S256);
|
||||
s
|
||||
});
|
||||
let code_challenge_methods_supported = Some(vec![
|
||||
PkceCodeChallengeMethod::Plain,
|
||||
PkceCodeChallengeMethod::S256,
|
||||
]);
|
||||
|
||||
let subject_types_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(SubjectType::Public);
|
||||
s
|
||||
});
|
||||
let subject_types_supported = Some(vec![SubjectType::Public]);
|
||||
|
||||
let id_token_signing_alg_values_supported = jwt_signing_alg_values_supported;
|
||||
|
||||
let display_values_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(Display::Page);
|
||||
s
|
||||
});
|
||||
let display_values_supported = Some(vec![Display::Page]);
|
||||
|
||||
let claim_types_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(ClaimType::Normal);
|
||||
s
|
||||
});
|
||||
let claim_types_supported = Some(vec![ClaimType::Normal]);
|
||||
|
||||
let claims_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert("iss".to_string());
|
||||
s.insert("sub".to_string());
|
||||
s.insert("aud".to_string());
|
||||
s.insert("iat".to_string());
|
||||
s.insert("exp".to_string());
|
||||
s.insert("nonce".to_string());
|
||||
s.insert("auth_time".to_string());
|
||||
s.insert("at_hash".to_string());
|
||||
s.insert("c_hash".to_string());
|
||||
s
|
||||
});
|
||||
let claims_supported = Some(vec![
|
||||
"iss".to_string(),
|
||||
"sub".to_string(),
|
||||
"aud".to_string(),
|
||||
"iat".to_string(),
|
||||
"exp".to_string(),
|
||||
"nonce".to_string(),
|
||||
"auth_time".to_string(),
|
||||
"at_hash".to_string(),
|
||||
"c_hash".to_string(),
|
||||
]);
|
||||
|
||||
let claims_parameter_supported = Some(false);
|
||||
let request_parameter_supported = Some(false);
|
||||
let request_uri_parameter_supported = Some(false);
|
||||
|
||||
let prompt_values_supported = Some({
|
||||
let mut s = HashSet::new();
|
||||
s.insert(Prompt::None);
|
||||
s.insert(Prompt::Login);
|
||||
s.insert(Prompt::Create);
|
||||
s
|
||||
});
|
||||
let prompt_values_supported = Some(vec![Prompt::None, Prompt::Login, Prompt::Create]);
|
||||
|
||||
let metadata = Metadata {
|
||||
issuer,
|
||||
@@ -194,12 +156,5 @@ pub(super) fn filter(
|
||||
..Metadata::default()
|
||||
};
|
||||
|
||||
warp::path!(".well-known" / "openid-configuration")
|
||||
.and(filters::trace::name("GET /.well-known/configuration"))
|
||||
.and(warp::get())
|
||||
.map(move || {
|
||||
let ret: Box<dyn Reply> = Box::new(warp::reply::json(&metadata));
|
||||
ret
|
||||
})
|
||||
.boxed()
|
||||
Json(metadata)
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,23 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
|
||||
use axum::{extract::Extension, response::IntoResponse, Json};
|
||||
use mas_jose::StaticKeystore;
|
||||
use mas_warp_utils::filters;
|
||||
use tower::{Service, ServiceExt};
|
||||
use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
|
||||
|
||||
pub(super) fn filter(key_store: &Arc<StaticKeystore>) -> BoxedFilter<(Box<dyn Reply>,)> {
|
||||
let key_store = key_store.clone();
|
||||
warp::path!("oauth2" / "keys.json")
|
||||
.and(filters::trace::name("GET /oauth2/keys.json"))
|
||||
.and(warp::get().map(move || key_store.clone()).and_then(get))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
async fn get(key_store: Arc<StaticKeystore>) -> Result<Box<dyn Reply>, Rejection> {
|
||||
pub(crate) async fn get(
|
||||
Extension(key_store): Extension<Arc<StaticKeystore>>,
|
||||
) -> Result<impl IntoResponse, Infallible> {
|
||||
let mut key_store: &StaticKeystore = key_store.as_ref();
|
||||
let jwks = key_store.ready().await?.call(()).await?;
|
||||
Ok(Box::new(warp::reply::json(&jwks)))
|
||||
Ok(Json(jwks))
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,58 +12,45 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
// pub mod authorization;
|
||||
pub mod discovery;
|
||||
// pub mod introspection;
|
||||
pub mod keys;
|
||||
// pub mod token;
|
||||
// pub mod userinfo;
|
||||
|
||||
use hyper::{header::AUTHORIZATION, Method};
|
||||
use mas_config::{Encrypter, HttpConfig};
|
||||
use mas_jose::StaticKeystore;
|
||||
use mas_templates::Templates;
|
||||
use mas_warp_utils::filters::cors::cors;
|
||||
use sqlx::PgPool;
|
||||
use warp::{filters::BoxedFilter, Filter, Reply};
|
||||
|
||||
mod authorization;
|
||||
mod discovery;
|
||||
mod introspection;
|
||||
mod keys;
|
||||
mod token;
|
||||
mod userinfo;
|
||||
|
||||
pub(crate) use self::authorization::ContinueAuthorizationGrant;
|
||||
use self::{
|
||||
authorization::filter as authorization, discovery::filter as discovery,
|
||||
introspection::filter as introspection, keys::filter as keys, token::filter as token,
|
||||
userinfo::filter as userinfo,
|
||||
use hyper::{
|
||||
http::uri::{Parts, PathAndQuery},
|
||||
Uri,
|
||||
};
|
||||
use mas_data_model::AuthorizationGrant;
|
||||
use mas_storage::{oauth2::authorization_grant::get_grant_by_id, PostgresqlBackend};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgConnection;
|
||||
|
||||
pub fn filter(
|
||||
pool: &PgPool,
|
||||
templates: &Templates,
|
||||
key_store: &Arc<StaticKeystore>,
|
||||
encrypter: &Encrypter,
|
||||
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);
|
||||
let userinfo = userinfo(pool);
|
||||
let introspection = introspection(pool, encrypter, http_config);
|
||||
let token = token(pool, encrypter, key_store, http_config);
|
||||
|
||||
let filter = discovery
|
||||
.or(keys)
|
||||
.unify()
|
||||
.or(userinfo)
|
||||
.unify()
|
||||
.or(token)
|
||||
.unify()
|
||||
.or(introspection)
|
||||
.unify()
|
||||
.with(
|
||||
cors()
|
||||
.allow_methods([Method::POST, Method::GET])
|
||||
.allow_headers([AUTHORIZATION]),
|
||||
);
|
||||
|
||||
filter.or(authorization).boxed()
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub(crate) struct ContinueAuthorizationGrant {
|
||||
data: String,
|
||||
}
|
||||
|
||||
// TEMP
|
||||
impl ContinueAuthorizationGrant {
|
||||
pub fn build_uri(&self) -> anyhow::Result<Uri> {
|
||||
let qs = serde_urlencoded::to_string(self)?;
|
||||
let path_and_query = PathAndQuery::try_from(format!("/oauth2/authorize/step?{}", qs))?;
|
||||
let uri = Uri::from_parts({
|
||||
let mut parts = Parts::default();
|
||||
parts.path_and_query = Some(path_and_query);
|
||||
parts
|
||||
})?;
|
||||
Ok(uri)
|
||||
}
|
||||
|
||||
pub async fn fetch_authorization_grant(
|
||||
&self,
|
||||
conn: &mut PgConnection,
|
||||
) -> anyhow::Result<AuthorizationGrant<PostgresqlBackend>> {
|
||||
let data = self.data.parse()?;
|
||||
get_grant_by_id(conn, data).await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user