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
Make sure all types of oauth2-types are documented
This commit is contained in:
committed by
Quentin Gliech
parent
c02f59bbaf
commit
66055b044e
@ -12,6 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Error types returned by an authorization server.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use parse_display::{Display, FromStr};
|
use parse_display::{Display, FromStr};
|
||||||
@ -56,6 +58,7 @@ impl From<ClientErrorCode> for ClientError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Client error codes defined in OAuth2.0, OpenID Connect and their extensions.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Display, FromStr, SerializeDisplay, DeserializeFromStr)]
|
#[derive(Debug, Clone, PartialEq, Eq, Display, FromStr, SerializeDisplay, DeserializeFromStr)]
|
||||||
#[display(style = "snake_case")]
|
#[display(style = "snake_case")]
|
||||||
pub enum ClientErrorCode {
|
pub enum ClientErrorCode {
|
||||||
|
@ -12,8 +12,21 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! [OAuth 2.0] and [OpenID Connect] types.
|
||||||
|
//!
|
||||||
|
//! This is part of the [Matrix Authentication Service] project.
|
||||||
|
//!
|
||||||
|
//! [OAuth 2.0]: https://oauth.net/2/
|
||||||
|
//! [OpenID Connect]: https://openid.net/connect/
|
||||||
|
//! [Matrix Authentication Service]: https://github.com/matrix-org/matrix-authentication-service
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(clippy::all, clippy::str_to_string, rustdoc::broken_intra_doc_links)]
|
#![deny(
|
||||||
|
clippy::all,
|
||||||
|
clippy::str_to_string,
|
||||||
|
rustdoc::broken_intra_doc_links,
|
||||||
|
missing_docs
|
||||||
|
)]
|
||||||
#![warn(clippy::pedantic)]
|
#![warn(clippy::pedantic)]
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
@ -26,6 +39,7 @@ pub mod response_type;
|
|||||||
pub mod scope;
|
pub mod scope;
|
||||||
pub mod webfinger;
|
pub mod webfinger;
|
||||||
|
|
||||||
|
/// Traits intended for blanket imports.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::pkce::CodeChallengeMethodExt;
|
pub use crate::pkce::CodeChallengeMethodExt;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Types to interact with the [OpenID Connect] specification.
|
||||||
|
//!
|
||||||
|
//! [OpenID Connect]: https://openid.net/connect/
|
||||||
|
|
||||||
use std::{ops::Deref, str::FromStr};
|
use std::{ops::Deref, str::FromStr};
|
||||||
|
|
||||||
use language_tags::LanguageTag;
|
use language_tags::LanguageTag;
|
||||||
@ -107,43 +111,63 @@ impl From<OAuthAccessTokenType> for AuthenticationMethodOrAccessTokenType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of an application.
|
||||||
#[derive(
|
#[derive(
|
||||||
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
||||||
)]
|
)]
|
||||||
#[display(style = "lowercase")]
|
#[display(style = "lowercase")]
|
||||||
pub enum ApplicationType {
|
pub enum ApplicationType {
|
||||||
|
/// A web application.
|
||||||
Web,
|
Web,
|
||||||
|
|
||||||
|
/// A native application.
|
||||||
Native,
|
Native,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subject Identifier types.
|
||||||
|
///
|
||||||
|
/// A Subject Identifier is a locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client.
|
||||||
#[derive(
|
#[derive(
|
||||||
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
||||||
)]
|
)]
|
||||||
#[display(style = "lowercase")]
|
#[display(style = "lowercase")]
|
||||||
pub enum SubjectType {
|
pub enum SubjectType {
|
||||||
|
/// This provides the same `sub` (subject) value to all Clients.
|
||||||
Public,
|
Public,
|
||||||
|
|
||||||
|
/// This provides a different `sub` value to each Client, so as not to enable Clients to correlate the End-User's activities without permission.
|
||||||
Pairwise,
|
Pairwise,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Claim types.
|
||||||
#[derive(
|
#[derive(
|
||||||
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr,
|
||||||
)]
|
)]
|
||||||
#[display(style = "lowercase")]
|
#[display(style = "lowercase")]
|
||||||
pub enum ClaimType {
|
pub enum ClaimType {
|
||||||
|
/// Claims that are directly asserted by the OpenID Provider.
|
||||||
Normal,
|
Normal,
|
||||||
|
|
||||||
|
/// Claims that are asserted by a Claims Provider other than the OpenID Provider but are returned by OpenID Provider.
|
||||||
Aggregated,
|
Aggregated,
|
||||||
|
|
||||||
|
/// Claims that are asserted by a Claims Provider other than the OpenID Provider but are returned as references by the OpenID Provider.
|
||||||
Distributed,
|
Distributed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default value of `response_modes_supported` if it is not set.
|
||||||
pub static DEFAULT_RESPONSE_MODES_SUPPORTED: &[ResponseMode] =
|
pub static DEFAULT_RESPONSE_MODES_SUPPORTED: &[ResponseMode] =
|
||||||
&[ResponseMode::Query, ResponseMode::Fragment];
|
&[ResponseMode::Query, ResponseMode::Fragment];
|
||||||
|
|
||||||
|
/// The default value of `grant_types_supported` if it is not set.
|
||||||
pub static DEFAULT_GRANT_TYPES_SUPPORTED: &[GrantType] =
|
pub static DEFAULT_GRANT_TYPES_SUPPORTED: &[GrantType] =
|
||||||
&[GrantType::AuthorizationCode, GrantType::Implicit];
|
&[GrantType::AuthorizationCode, GrantType::Implicit];
|
||||||
|
|
||||||
|
/// The default value of `token_endpoint_auth_methods_supported` if it is not set.
|
||||||
pub static DEFAULT_AUTH_METHODS_SUPPORTED: &[OAuthClientAuthenticationMethod] =
|
pub static DEFAULT_AUTH_METHODS_SUPPORTED: &[OAuthClientAuthenticationMethod] =
|
||||||
&[OAuthClientAuthenticationMethod::ClientSecretBasic];
|
&[OAuthClientAuthenticationMethod::ClientSecretBasic];
|
||||||
|
|
||||||
|
/// The default value of `claim_types_supported` if it is not set.
|
||||||
pub static DEFAULT_CLAIM_TYPES_SUPPORTED: &[ClaimType] = &[ClaimType::Normal];
|
pub static DEFAULT_CLAIM_TYPES_SUPPORTED: &[ClaimType] = &[ClaimType::Normal];
|
||||||
|
|
||||||
/// Authorization server metadata, as described by the [IANA registry].
|
/// Authorization server metadata, as described by the [IANA registry].
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Types for the [Proof Key for Code Exchange].
|
||||||
|
//!
|
||||||
|
//! [Proof Key for Code Exchange]: https://www.rfc-editor.org/rfc/rfc7636
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use data_encoding::BASE64URL_NOPAD;
|
use data_encoding::BASE64URL_NOPAD;
|
||||||
@ -20,20 +24,26 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Errors that can occur when verifying a code challenge.
|
||||||
#[derive(Debug, Error, PartialEq, Eq)]
|
#[derive(Debug, Error, PartialEq, Eq)]
|
||||||
pub enum CodeChallengeError {
|
pub enum CodeChallengeError {
|
||||||
|
/// The code verifier should be at least 43 characters long.
|
||||||
#[error("code_verifier should be at least 43 characters long")]
|
#[error("code_verifier should be at least 43 characters long")]
|
||||||
TooShort,
|
TooShort,
|
||||||
|
|
||||||
|
/// The code verifier should be at most 128 characters long.
|
||||||
#[error("code_verifier should be at most 128 characters long")]
|
#[error("code_verifier should be at most 128 characters long")]
|
||||||
TooLong,
|
TooLong,
|
||||||
|
|
||||||
|
/// The code verifier contains invalid characters.
|
||||||
#[error("code_verifier contains invalid characters")]
|
#[error("code_verifier contains invalid characters")]
|
||||||
InvalidCharacters,
|
InvalidCharacters,
|
||||||
|
|
||||||
|
/// The challenge verification failed.
|
||||||
#[error("challenge verification failed")]
|
#[error("challenge verification failed")]
|
||||||
VerificationFailed,
|
VerificationFailed,
|
||||||
|
|
||||||
|
/// The challenge method is unsupported.
|
||||||
#[error("unknown challenge method")]
|
#[error("unknown challenge method")]
|
||||||
UnknownChallengeMethod,
|
UnknownChallengeMethod,
|
||||||
}
|
}
|
||||||
@ -57,6 +67,7 @@ fn validate_verifier(verifier: &str) -> Result<(), CodeChallengeError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper trait to compute and verify code challenges.
|
||||||
pub trait CodeChallengeMethodExt {
|
pub trait CodeChallengeMethodExt {
|
||||||
/// Compute the challenge for a given verifier
|
/// Compute the challenge for a given verifier
|
||||||
///
|
///
|
||||||
@ -105,14 +116,20 @@ impl CodeChallengeMethodExt for PkceCodeChallengeMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The code challenge data added to an authorization request.
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct AuthorizationRequest {
|
pub struct AuthorizationRequest {
|
||||||
|
/// The code challenge method.
|
||||||
pub code_challenge_method: PkceCodeChallengeMethod,
|
pub code_challenge_method: PkceCodeChallengeMethod,
|
||||||
|
|
||||||
|
/// The code challenge computed from the verifier and the method.
|
||||||
pub code_challenge: String,
|
pub code_challenge: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The code challenge data added to a token request.
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct TokenRequest {
|
pub struct TokenRequest {
|
||||||
|
/// The code challenge verifier.
|
||||||
pub code_challenge_verifier: String,
|
pub code_challenge_verifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Types for [Dynamic Client Registration].
|
||||||
|
//!
|
||||||
|
//! [Dynamic Client Registration]: https://openid.net/specs/openid-connect-registration-1_0.html
|
||||||
|
|
||||||
use std::{collections::HashMap, ops::Deref};
|
use std::{collections::HashMap, ops::Deref};
|
||||||
|
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
@ -35,18 +39,24 @@ use crate::{
|
|||||||
mod client_metadata_serde;
|
mod client_metadata_serde;
|
||||||
use client_metadata_serde::ClientMetadataSerdeHelper;
|
use client_metadata_serde::ClientMetadataSerdeHelper;
|
||||||
|
|
||||||
|
/// The default value of `response_types` if it is not set.
|
||||||
pub const DEFAULT_RESPONSE_TYPES: [OAuthAuthorizationEndpointResponseType; 1] =
|
pub const DEFAULT_RESPONSE_TYPES: [OAuthAuthorizationEndpointResponseType; 1] =
|
||||||
[OAuthAuthorizationEndpointResponseType::Code];
|
[OAuthAuthorizationEndpointResponseType::Code];
|
||||||
|
|
||||||
|
/// The default value of `grant_types` if it is not set.
|
||||||
pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode];
|
pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode];
|
||||||
|
|
||||||
|
/// The default value of `application_type` if it is not set.
|
||||||
pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web;
|
pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web;
|
||||||
|
|
||||||
|
/// The default value of `token_endpoint_auth_method` if it is not set.
|
||||||
pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod =
|
pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod =
|
||||||
&OAuthClientAuthenticationMethod::ClientSecretBasic;
|
&OAuthClientAuthenticationMethod::ClientSecretBasic;
|
||||||
|
|
||||||
|
/// The default value of `id_token_signed_response_alg` if it is not set.
|
||||||
pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256;
|
pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256;
|
||||||
|
|
||||||
|
/// The default value of `id_token_encrypted_response_enc` if it is not set.
|
||||||
pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc =
|
pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc =
|
||||||
&JsonWebEncryptionEnc::A128CbcHs256;
|
&JsonWebEncryptionEnc::A128CbcHs256;
|
||||||
|
|
||||||
@ -841,19 +851,26 @@ pub enum ClientMetadataVerificationError {
|
|||||||
MissingEncryptionAlg(&'static str),
|
MissingEncryptionAlg(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The issuer response to dynamic client registration.
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub struct ClientRegistrationResponse {
|
pub struct ClientRegistrationResponse {
|
||||||
|
/// A unique client identifier.
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
|
|
||||||
|
/// A client secret, if the `token_endpoint_auth_method` requires one.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub client_secret: Option<String>,
|
pub client_secret: Option<String>,
|
||||||
|
|
||||||
|
/// Time at which the Client Identifier was issued.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
||||||
pub client_id_issued_at: Option<DateTime<Utc>>,
|
pub client_id_issued_at: Option<DateTime<Utc>>,
|
||||||
|
|
||||||
|
/// Time at which the client_secret will expire or 0 if it will not expire.
|
||||||
|
///
|
||||||
|
/// Required if `client_secret` is issued.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
|
||||||
pub client_secret_expires_at: Option<DateTime<Utc>>,
|
pub client_secret_expires_at: Option<DateTime<Utc>>,
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Requests and response types to interact with the [OAuth 2.0] specification.
|
||||||
|
//!
|
||||||
|
//! [OAuth 2.0]: https://oauth.net/2/
|
||||||
|
|
||||||
use std::{collections::HashSet, fmt, hash::Hash, num::NonZeroU32};
|
use std::{collections::HashSet, fmt, hash::Hash, num::NonZeroU32};
|
||||||
|
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
@ -323,7 +327,10 @@ impl fmt::Debug for AuthorizationRequest {
|
|||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||||
pub struct AuthorizationResponse<R> {
|
pub struct AuthorizationResponse<R> {
|
||||||
|
/// The authorization code generated by the authorization server.
|
||||||
pub code: Option<String>,
|
pub code: Option<String>,
|
||||||
|
|
||||||
|
/// Other fields of the response.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub response: R,
|
pub response: R,
|
||||||
}
|
}
|
||||||
@ -345,6 +352,7 @@ pub struct DeviceAuthorizationRequest {
|
|||||||
pub scope: Option<Scope>,
|
pub scope: Option<Scope>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default value of the `interval` between polling requests, if it is not set.
|
||||||
pub const DEFAULT_DEVICE_AUTHORIZATION_INTERVAL_SECONDS: i64 = 5;
|
pub const DEFAULT_DEVICE_AUTHORIZATION_INTERVAL_SECONDS: i64 = 5;
|
||||||
|
|
||||||
/// A successful response from the [Device Authorization Endpoint].
|
/// A successful response from the [Device Authorization Endpoint].
|
||||||
@ -384,7 +392,7 @@ pub struct DeviceAuthorizationResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceAuthorizationResponse {
|
impl DeviceAuthorizationResponse {
|
||||||
///The minimum amount of time in seconds that the client should wait
|
/// The minimum amount of time in seconds that the client should wait
|
||||||
/// between polling requests to the token endpoint.
|
/// between polling requests to the token endpoint.
|
||||||
///
|
///
|
||||||
/// Defaults to [`DEFAULT_DEVICE_AUTHORIZATION_INTERVAL_SECONDS`].
|
/// Defaults to [`DEFAULT_DEVICE_AUTHORIZATION_INTERVAL_SECONDS`].
|
||||||
@ -535,11 +543,20 @@ pub enum GrantType {
|
|||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
#[serde(tag = "grant_type", rename_all = "snake_case")]
|
#[serde(tag = "grant_type", rename_all = "snake_case")]
|
||||||
pub enum AccessTokenRequest {
|
pub enum AccessTokenRequest {
|
||||||
|
/// A request in the Authorization Code flow.
|
||||||
AuthorizationCode(AuthorizationCodeGrant),
|
AuthorizationCode(AuthorizationCodeGrant),
|
||||||
|
|
||||||
|
/// A request to refresh an access token.
|
||||||
RefreshToken(RefreshTokenGrant),
|
RefreshToken(RefreshTokenGrant),
|
||||||
|
|
||||||
|
/// A request in the Client Credentials flow.
|
||||||
ClientCredentials(ClientCredentialsGrant),
|
ClientCredentials(ClientCredentialsGrant),
|
||||||
|
|
||||||
|
/// A request in the Device Code flow.
|
||||||
#[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
|
#[serde(rename = "urn:ietf:params:oauth:grant-type:device_code")]
|
||||||
DeviceCode(DeviceCodeGrant),
|
DeviceCode(DeviceCodeGrant),
|
||||||
|
|
||||||
|
/// An unsupported request.
|
||||||
#[serde(skip, other)]
|
#[serde(skip, other)]
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! [Response types] in the OpenID Connect specification.
|
||||||
|
//!
|
||||||
|
//! [Response types]: https://openid.net/specs/openid-connect-core-1_0.html#Authentication
|
||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
use std::{collections::BTreeSet, fmt, iter::FromIterator, str::FromStr};
|
use std::{collections::BTreeSet, fmt, iter::FromIterator, str::FromStr};
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Types to define an [access token's scope].
|
||||||
|
//!
|
||||||
|
//! [access token's scope]: https://www.rfc-editor.org/rfc/rfc6749#section-3.3
|
||||||
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
use std::{borrow::Cow, collections::BTreeSet, iter::FromIterator, ops::Deref, str::FromStr};
|
use std::{borrow::Cow, collections::BTreeSet, iter::FromIterator, ops::Deref, str::FromStr};
|
||||||
@ -20,10 +24,12 @@ use itertools::Itertools;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// The error type returned when a scope is invalid.
|
||||||
#[derive(Debug, Error, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Error, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[error("Invalid scope format")]
|
#[error("Invalid scope format")]
|
||||||
pub struct InvalidScope;
|
pub struct InvalidScope;
|
||||||
|
|
||||||
|
/// A scope token or scope value.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ScopeToken(Cow<'static, str>);
|
pub struct ScopeToken(Cow<'static, str>);
|
||||||
|
|
||||||
@ -36,11 +42,34 @@ impl ScopeToken {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `openid`.
|
||||||
|
///
|
||||||
|
/// Must be included in OpenID Connect requests.
|
||||||
pub const OPENID: ScopeToken = ScopeToken::from_static("openid");
|
pub const OPENID: ScopeToken = ScopeToken::from_static("openid");
|
||||||
|
|
||||||
|
/// `profile`.
|
||||||
|
///
|
||||||
|
/// Requests access to the End-User's default profile Claims.
|
||||||
pub const PROFILE: ScopeToken = ScopeToken::from_static("profile");
|
pub const PROFILE: ScopeToken = ScopeToken::from_static("profile");
|
||||||
|
|
||||||
|
/// `email`.
|
||||||
|
///
|
||||||
|
/// Requests access to the `email` and `email_verified` Claims.
|
||||||
pub const EMAIL: ScopeToken = ScopeToken::from_static("email");
|
pub const EMAIL: ScopeToken = ScopeToken::from_static("email");
|
||||||
|
|
||||||
|
/// `address`.
|
||||||
|
///
|
||||||
|
/// Requests access to the `address` Claim.
|
||||||
pub const ADDRESS: ScopeToken = ScopeToken::from_static("address");
|
pub const ADDRESS: ScopeToken = ScopeToken::from_static("address");
|
||||||
|
|
||||||
|
/// `phone`.
|
||||||
|
///
|
||||||
|
/// Requests access to the `phone_number` and `phone_number_verified` Claims.
|
||||||
pub const PHONE: ScopeToken = ScopeToken::from_static("phone");
|
pub const PHONE: ScopeToken = ScopeToken::from_static("phone");
|
||||||
|
|
||||||
|
/// `offline_access`.
|
||||||
|
///
|
||||||
|
/// Requests that an OAuth 2.0 Refresh Token be issued that can be used to obtain an Access Token that grants access to the End-User's Userinfo Endpoint even when the End-User is not present (not logged in).
|
||||||
pub const OFFLINE_ACCESS: ScopeToken = ScopeToken::from_static("offline_access");
|
pub const OFFLINE_ACCESS: ScopeToken = ScopeToken::from_static("offline_access");
|
||||||
|
|
||||||
// As per RFC6749 appendix A:
|
// As per RFC6749 appendix A:
|
||||||
@ -81,6 +110,7 @@ impl ToString for ScopeToken {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A scope.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Scope(BTreeSet<ScopeToken>);
|
pub struct Scope(BTreeSet<ScopeToken>);
|
||||||
|
|
||||||
@ -108,17 +138,20 @@ impl FromStr for Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
|
/// Whether this `Scope` is empty.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
// This should never be the case?
|
// This should never be the case?
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The number of tokens in the `Scope`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this `Scope` contains the given value.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains(&self, token: &str) -> bool {
|
pub fn contains(&self, token: &str) -> bool {
|
||||||
ScopeToken::from_str(token)
|
ScopeToken::from_str(token)
|
||||||
@ -126,6 +159,9 @@ impl Scope {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts the given token in this `Scope`.
|
||||||
|
///
|
||||||
|
/// Returns whether the token was newly inserted.
|
||||||
pub fn insert(&mut self, value: ScopeToken) -> bool {
|
pub fn insert(&mut self, value: ScopeToken) -> bool {
|
||||||
self.0.insert(value)
|
self.0.insert(value)
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,25 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Types for provider discovery using [Webfinger].
|
||||||
|
//!
|
||||||
|
//! [Webfinger]: https://www.rfc-editor.org/rfc/rfc7033
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
/// The response of the Webfinger endpoint.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
pub struct WebFingerResponse {
|
pub struct WebFingerResponse {
|
||||||
|
/// A URI that identifies the entity described by the response.
|
||||||
subject: String,
|
subject: String,
|
||||||
|
|
||||||
|
/// Links that describe the subject.
|
||||||
links: Vec<WebFingerLink>,
|
links: Vec<WebFingerLink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebFingerResponse {
|
impl WebFingerResponse {
|
||||||
|
/// Creates a new `WebFingerResponse` with the given subject.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(subject: String) -> Self {
|
pub const fn new(subject: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -30,26 +39,34 @@ impl WebFingerResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the given link to this `WebFingerResponse`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_link(mut self, link: WebFingerLink) -> Self {
|
pub fn with_link(mut self, link: WebFingerLink) -> Self {
|
||||||
self.links.push(link);
|
self.links.push(link);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the given issuer to this `WebFingerResponse`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_issuer(self, issuer: Url) -> Self {
|
pub fn with_issuer(self, issuer: Url) -> Self {
|
||||||
self.with_link(WebFingerLink::issuer(issuer))
|
self.with_link(WebFingerLink::issuer(issuer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A link in a Webfinger response.
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||||
#[serde(tag = "rel")]
|
#[serde(tag = "rel")]
|
||||||
pub enum WebFingerLink {
|
pub enum WebFingerLink {
|
||||||
|
/// An OpenID Connect issuer.
|
||||||
#[serde(rename = "http://openid.net/specs/connect/1.0/issuer")]
|
#[serde(rename = "http://openid.net/specs/connect/1.0/issuer")]
|
||||||
OidcIssuer { href: Url },
|
OidcIssuer {
|
||||||
|
/// The URL of the issuer.
|
||||||
|
href: Url,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebFingerLink {
|
impl WebFingerLink {
|
||||||
|
/// Creates a new `WebFingerLink` for an OpenID Connect issuer.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn issuer(href: Url) -> Self {
|
pub const fn issuer(href: Url) -> Self {
|
||||||
Self::OidcIssuer { href }
|
Self::OidcIssuer { href }
|
||||||
|
Reference in New Issue
Block a user