ruma_common/api/
auth_scheme.rs

1//! The `AuthScheme` trait used to specify the authentication scheme used by endpoints and the types
2//! that implement it.
3
4#![allow(clippy::exhaustive_structs)]
5
6use http::{header, HeaderName, HeaderValue};
7
8use crate::api::{error::IntoHttpError, SendAccessToken};
9
10/// Trait implemented by types representing an authentication scheme used by an endpoint.
11pub trait AuthScheme: Sized {
12    /// The `Authorization` HTTP header to add to an outgoing request with this scheme.
13    ///
14    /// Transforms the `SendAccessToken` into an access token if the endpoint requires it, or if it
15    /// is `SendAccessToken::Force`.
16    ///
17    /// Fails if the endpoint requires an access token but the parameter is `SendAccessToken::None`,
18    /// or if the access token can't be converted to a [`HeaderValue`].
19    fn authorization_header(
20        access_token: SendAccessToken<'_>,
21    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError>;
22}
23
24/// No authentication is performed.
25#[derive(Debug, Clone, Copy, Default)]
26pub struct NoAuthentication;
27
28impl AuthScheme for NoAuthentication {
29    fn authorization_header(
30        access_token: SendAccessToken<'_>,
31    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
32        access_token
33            .get_not_required_for_endpoint()
34            .map(access_token_to_authorization_header)
35            .transpose()
36    }
37}
38
39/// Authentication is performed by including an access token in the `Authentication` http
40/// header, or an `access_token` query parameter.
41///
42/// Using the query parameter is deprecated since Matrix 1.11.
43#[derive(Debug, Clone, Copy, Default)]
44pub struct AccessToken;
45
46impl AuthScheme for AccessToken {
47    fn authorization_header(
48        access_token: SendAccessToken<'_>,
49    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
50        let token =
51            access_token.get_required_for_endpoint().ok_or(IntoHttpError::NeedsAuthentication)?;
52        access_token_to_authorization_header(token).map(Some)
53    }
54}
55
56/// Authentication is optional, and it is performed by including an access token in the
57/// `Authentication` http header, or an `access_token` query parameter.
58///
59/// Using the query parameter is deprecated since Matrix 1.11.
60#[derive(Debug, Clone, Copy, Default)]
61pub struct AccessTokenOptional;
62
63impl AuthScheme for AccessTokenOptional {
64    fn authorization_header(
65        access_token: SendAccessToken<'_>,
66    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
67        access_token
68            .get_required_for_endpoint()
69            .map(access_token_to_authorization_header)
70            .transpose()
71    }
72}
73
74/// Authentication is required, and can only be performed for appservices, by including an
75/// appservice access token in the `Authentication` http header, or `access_token` query
76/// parameter.
77///
78/// Using the query parameter is deprecated since Matrix 1.11.
79#[derive(Debug, Clone, Copy, Default)]
80pub struct AppserviceToken;
81
82impl AuthScheme for AppserviceToken {
83    fn authorization_header(
84        access_token: SendAccessToken<'_>,
85    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
86        let token =
87            access_token.get_required_for_appservice().ok_or(IntoHttpError::NeedsAuthentication)?;
88        access_token_to_authorization_header(token).map(Some)
89    }
90}
91
92/// No authentication is performed for clients, but it can be performed for appservices, by
93/// including an appservice access token in the `Authentication` http header, or an
94/// `access_token` query parameter.
95///
96/// Using the query parameter is deprecated since Matrix 1.11.
97#[derive(Debug, Clone, Copy, Default)]
98pub struct AppserviceTokenOptional;
99
100impl AuthScheme for AppserviceTokenOptional {
101    fn authorization_header(
102        access_token: SendAccessToken<'_>,
103    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
104        access_token
105            .get_required_for_appservice()
106            .map(access_token_to_authorization_header)
107            .transpose()
108    }
109}
110
111/// Authentication is performed by including X-Matrix signatures in the request headers,
112/// as defined in the federation API.
113#[derive(Debug, Clone, Copy, Default)]
114pub struct ServerSignatures;
115
116impl AuthScheme for ServerSignatures {
117    fn authorization_header(
118        _access_token: SendAccessToken<'_>,
119    ) -> Result<Option<(HeaderName, HeaderValue)>, IntoHttpError> {
120        Ok(None)
121    }
122}
123
124/// Convert the given access token to an `Authorization` HTTP header.
125fn access_token_to_authorization_header(
126    token: &str,
127) -> Result<(HeaderName, HeaderValue), IntoHttpError> {
128    Ok((header::AUTHORIZATION, format!("Bearer {token}").try_into()?))
129}