You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Allow to get verified provider metadata with minimal checks
This commit is contained in:
committed by
Quentin Gliech
parent
b8c0b5c156
commit
940965287b
@ -366,115 +366,147 @@ impl ProviderMetadata {
|
|||||||
/// Will return `Err` if validation fails.
|
/// Will return `Err` if validation fails.
|
||||||
///
|
///
|
||||||
/// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
/// [OpenID Connect Discovery Spec 1.0]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn validate(
|
pub fn validate(
|
||||||
self,
|
self,
|
||||||
issuer: &Url,
|
issuer: &Url,
|
||||||
) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
|
) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
|
||||||
self.issuer
|
let metadata = self.insecure_verify_metadata()?;
|
||||||
.as_ref()
|
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingIssuer)
|
|
||||||
.and_then(|url| {
|
|
||||||
validate_url("issuer", url, ExtraUrlRestrictions::NoQueryOrFragment)?;
|
|
||||||
|
|
||||||
if url != issuer {
|
if metadata.issuer() != issuer {
|
||||||
return Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch);
|
return Err(ProviderMetadataVerificationError::IssuerUrlsDontMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
validate_url(
|
||||||
})?;
|
"issuer",
|
||||||
|
metadata.issuer(),
|
||||||
|
ExtraUrlRestrictions::NoQueryOrFragment,
|
||||||
|
)?;
|
||||||
|
|
||||||
self.authorization_endpoint
|
validate_url(
|
||||||
.as_ref()
|
"authorization_endpoint",
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)
|
metadata.authorization_endpoint(),
|
||||||
.and_then(|url| {
|
ExtraUrlRestrictions::NoFragment,
|
||||||
validate_url(
|
)?;
|
||||||
"authorization_endpoint",
|
|
||||||
url,
|
|
||||||
ExtraUrlRestrictions::NoFragment,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.token_endpoint
|
validate_url(
|
||||||
.as_ref()
|
"token_endpoint",
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingTokenEndpoint)
|
metadata.token_endpoint(),
|
||||||
.and_then(|url| {
|
ExtraUrlRestrictions::NoFragment,
|
||||||
validate_url("token_endpoint", url, ExtraUrlRestrictions::NoFragment)
|
)?;
|
||||||
})?;
|
|
||||||
|
|
||||||
self.jwks_uri
|
validate_url("jwks_uri", metadata.jwks_uri(), ExtraUrlRestrictions::None)?;
|
||||||
.as_ref()
|
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingJwksUri)
|
|
||||||
.and_then(|url| validate_url("jwks_uri", url, ExtraUrlRestrictions::None))?;
|
|
||||||
|
|
||||||
if let Some(url) = &self.registration_endpoint {
|
if let Some(url) = &metadata.registration_endpoint {
|
||||||
validate_url("registration_endpoint", url, ExtraUrlRestrictions::None)?;
|
validate_url("registration_endpoint", url, ExtraUrlRestrictions::None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scopes) = &self.scopes_supported {
|
if let Some(scopes) = &metadata.scopes_supported {
|
||||||
if !scopes.iter().any(|s| s == "openid") {
|
if !scopes.iter().any(|s| s == "openid") {
|
||||||
return Err(ProviderMetadataVerificationError::ScopesMissingOpenid);
|
return Err(ProviderMetadataVerificationError::ScopesMissingOpenid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.response_types_supported
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingResponseTypesSupported)?;
|
|
||||||
|
|
||||||
validate_signing_alg_values_supported(
|
validate_signing_alg_values_supported(
|
||||||
"token_endpoint",
|
"token_endpoint",
|
||||||
&self.token_endpoint_auth_signing_alg_values_supported,
|
&metadata.token_endpoint_auth_signing_alg_values_supported,
|
||||||
&self.token_endpoint_auth_methods_supported,
|
&metadata.token_endpoint_auth_methods_supported,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(url) = &self.revocation_endpoint {
|
if let Some(url) = &metadata.revocation_endpoint {
|
||||||
validate_url("revocation_endpoint", url, ExtraUrlRestrictions::NoFragment)?;
|
validate_url("revocation_endpoint", url, ExtraUrlRestrictions::NoFragment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_signing_alg_values_supported(
|
validate_signing_alg_values_supported(
|
||||||
"revocation_endpoint",
|
"revocation_endpoint",
|
||||||
&self.revocation_endpoint_auth_signing_alg_values_supported,
|
&metadata.revocation_endpoint_auth_signing_alg_values_supported,
|
||||||
&self.revocation_endpoint_auth_methods_supported,
|
&metadata.revocation_endpoint_auth_methods_supported,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(url) = &self.introspection_endpoint {
|
if let Some(url) = &metadata.introspection_endpoint {
|
||||||
validate_url("introspection_endpoint", url, ExtraUrlRestrictions::None)?;
|
validate_url("introspection_endpoint", url, ExtraUrlRestrictions::None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_signing_alg_values_supported(
|
validate_signing_alg_values_supported(
|
||||||
"introspection_endpoint",
|
"introspection_endpoint",
|
||||||
&self.introspection_endpoint_auth_signing_alg_values_supported,
|
&metadata.introspection_endpoint_auth_signing_alg_values_supported,
|
||||||
&self.introspection_endpoint_auth_methods_supported,
|
&metadata.introspection_endpoint_auth_methods_supported,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(url) = &self.userinfo_endpoint {
|
if let Some(url) = &metadata.userinfo_endpoint {
|
||||||
validate_url("userinfo_endpoint", url, ExtraUrlRestrictions::None)?;
|
validate_url("userinfo_endpoint", url, ExtraUrlRestrictions::None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !metadata
|
||||||
|
.id_token_signing_alg_values_supported()
|
||||||
|
.contains(&JsonWebSignatureAlg::Rs256)
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
ProviderMetadataVerificationError::SigningAlgValuesMissingRs256("id_token"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(url) = &metadata.pushed_authorization_request_endpoint {
|
||||||
|
validate_url(
|
||||||
|
"pushed_authorization_request_endpoint",
|
||||||
|
url,
|
||||||
|
ExtraUrlRestrictions::None,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify this `ProviderMetadata`.
|
||||||
|
///
|
||||||
|
/// Contrary to [`ProviderMetadata::validate()`], it only checks that the
|
||||||
|
/// required fields are present.
|
||||||
|
///
|
||||||
|
/// This can be used during development to test against a local OpenID
|
||||||
|
/// Provider, for example.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
///
|
||||||
|
/// - `issuer`: The issuer that was discovered to get this
|
||||||
|
/// `ProviderMetadata`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if a required field is missing.
|
||||||
|
///
|
||||||
|
/// # Warning
|
||||||
|
///
|
||||||
|
/// It is not recommended to use this method in production as it doesn't
|
||||||
|
/// ensure that the issuer implements the proper security practices.
|
||||||
|
pub fn insecure_verify_metadata(
|
||||||
|
self,
|
||||||
|
) -> Result<VerifiedProviderMetadata, ProviderMetadataVerificationError> {
|
||||||
|
self.issuer
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(ProviderMetadataVerificationError::MissingIssuer)?;
|
||||||
|
|
||||||
|
self.authorization_endpoint
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(ProviderMetadataVerificationError::MissingAuthorizationEndpoint)?;
|
||||||
|
|
||||||
|
self.token_endpoint
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(ProviderMetadataVerificationError::MissingTokenEndpoint)?;
|
||||||
|
|
||||||
|
self.jwks_uri
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(ProviderMetadataVerificationError::MissingJwksUri)?;
|
||||||
|
|
||||||
|
self.response_types_supported
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(ProviderMetadataVerificationError::MissingResponseTypesSupported)?;
|
||||||
|
|
||||||
self.subject_types_supported
|
self.subject_types_supported
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingSubjectTypesSupported)?;
|
.ok_or(ProviderMetadataVerificationError::MissingSubjectTypesSupported)?;
|
||||||
|
|
||||||
self.id_token_signing_alg_values_supported
|
self.id_token_signing_alg_values_supported
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)
|
.ok_or(ProviderMetadataVerificationError::MissingIdTokenSigningAlgValuesSupported)?;
|
||||||
.and_then(|types| {
|
|
||||||
if !types.contains(&JsonWebSignatureAlg::Rs256) {
|
|
||||||
return Err(
|
|
||||||
ProviderMetadataVerificationError::SigningAlgValuesMissingRs256("id_token"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if let Some(url) = &self.pushed_authorization_request_endpoint {
|
|
||||||
validate_url(
|
|
||||||
"pushed_authorization_request_endpoint",
|
|
||||||
url,
|
|
||||||
ExtraUrlRestrictions::None,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(VerifiedProviderMetadata { inner: self })
|
Ok(VerifiedProviderMetadata { inner: self })
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user