matrix_sdk/authentication/oidc/error.rs
1// Copyright 2025 Kévin Commaille
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Error types used in the [`Oidc`](super::Oidc) API.
16
17pub use mas_oidc_client::error::*;
18use matrix_sdk_base::deserialized_responses::PrivOwnedStr;
19use oauth2::ErrorResponseType;
20pub use oauth2::{
21 basic::{
22 BasicErrorResponse, BasicErrorResponseType, BasicRequestTokenError,
23 BasicRevocationErrorResponse,
24 },
25 ConfigurationError, HttpClientError, RequestTokenError, RevocationErrorResponseType,
26 StandardErrorResponse,
27};
28use ruma::serde::{PartialEqAsRefStr, StringEnum};
29
30pub use super::cross_process::CrossProcessRefreshLockError;
31
32/// An error when trying to parse the query of a redirect URI.
33#[derive(Debug, Clone, thiserror::Error)]
34#[non_exhaustive]
35pub enum RedirectUriQueryParseError {
36 /// There is no query part in the URI.
37 #[error("No query in URI")]
38 MissingQuery,
39
40 /// Deserialization failed.
41 #[error("Query is not using one of the defined formats")]
42 UnknownFormat,
43}
44
45/// All errors that can occur when using the OpenID Connect API.
46#[derive(Debug, thiserror::Error)]
47#[non_exhaustive]
48pub enum OidcError {
49 /// An error occurred when interacting with the provider.
50 #[error(transparent)]
51 Oidc(Error),
52
53 /// An error occurred when discovering the authorization server's issuer.
54 #[error("authorization server discovery failed: {0}")]
55 Discovery(#[from] OauthDiscoveryError),
56
57 /// The OpenID Connect Provider doesn't support dynamic client registration.
58 ///
59 /// The provider probably offers another way to register clients.
60 #[error("no dynamic registration support")]
61 NoRegistrationSupport,
62
63 /// The client has not registered while the operation requires it.
64 #[error("client not registered")]
65 NotRegistered,
66
67 /// The device ID was not returned by the homeserver after login.
68 #[error("missing device ID in response")]
69 MissingDeviceId,
70
71 /// The client is not authenticated while the request requires it.
72 #[error("client not authenticated")]
73 NotAuthenticated,
74
75 /// An error occurred using the OAuth 2.0 authorization code grant.
76 #[error("authorization code grant failed: {0}")]
77 AuthorizationCode(#[from] OauthAuthorizationCodeError),
78
79 /// An error occurred interacting with the OAuth 2.0 authorization server
80 /// while refreshing the access token.
81 #[error("failed to refresh token: {0}")]
82 RefreshToken(BasicRequestTokenError<HttpClientError<reqwest::Error>>),
83
84 /// An error occurred revoking an OAuth 2.0 access token.
85 #[error("failed to log out: {0}")]
86 Logout(#[from] OauthTokenRevocationError),
87
88 /// An error occurred parsing a URL.
89 #[error(transparent)]
90 Url(url::ParseError),
91
92 /// An error occurred caused by the cross-process locks.
93 #[error(transparent)]
94 LockError(#[from] CrossProcessRefreshLockError),
95
96 /// An unknown error occurred.
97 #[error("unknown error")]
98 UnknownError(#[source] Box<dyn std::error::Error + Send + Sync>),
99}
100
101impl<E> From<E> for OidcError
102where
103 E: Into<Error>,
104{
105 fn from(value: E) -> Self {
106 Self::Oidc(value.into())
107 }
108}
109
110/// All errors that can occur when discovering the OAuth 2.0 server metadata.
111#[derive(Debug, thiserror::Error)]
112#[non_exhaustive]
113pub enum OauthDiscoveryError {
114 /// OAuth 2.0 is not supported by the homeserver.
115 #[error("OAuth 2.0 is not supported by the homeserver")]
116 NotSupported,
117
118 /// An error occurred when making a request to the homeserver.
119 #[error(transparent)]
120 Http(#[from] crate::HttpError),
121
122 /// The server metadata is invalid.
123 #[error(transparent)]
124 Json(#[from] serde_json::Error),
125
126 /// An error occurred when making a request to the OpenID Connect provider.
127 #[error(transparent)]
128 Oidc(#[from] DiscoveryError),
129}
130
131impl OauthDiscoveryError {
132 /// Whether this error occurred because OAuth 2.0 is not supported by the
133 /// homeserver.
134 pub fn is_not_supported(&self) -> bool {
135 matches!(self, Self::NotSupported)
136 }
137}
138
139/// All errors that can occur when using the Authorization Code grant with the
140/// OAuth 2.0 API.
141#[derive(Debug, thiserror::Error)]
142#[non_exhaustive]
143pub enum OauthAuthorizationCodeError {
144 /// The query of the redirect URI doesn't have the expected format.
145 #[error(transparent)]
146 RedirectUri(#[from] RedirectUriQueryParseError),
147
148 /// The user cancelled the authorization in the web UI.
149 #[error("authorization cancelled by the user")]
150 Cancelled,
151
152 /// An error occurred when getting the authorization from the user in the
153 /// web UI.
154 #[error("authorization failed: {0}")]
155 Authorization(StandardErrorResponse<AuthorizationCodeErrorResponseType>),
156
157 /// The state used to complete authorization doesn't match any of the
158 /// ongoing authorizations.
159 #[error("authorization state value is unexpected")]
160 InvalidState,
161
162 /// An error occurred interacting with the OAuth 2.0 authorization server
163 /// while exchanging the authorization code for an access token.
164 #[error("failed to request token: {0}")]
165 RequestToken(BasicRequestTokenError<HttpClientError<reqwest::Error>>),
166}
167
168impl From<StandardErrorResponse<AuthorizationCodeErrorResponseType>>
169 for OauthAuthorizationCodeError
170{
171 fn from(value: StandardErrorResponse<AuthorizationCodeErrorResponseType>) -> Self {
172 if *value.error() == AuthorizationCodeErrorResponseType::AccessDenied {
173 // The user cancelled the login in the web view.
174 Self::Cancelled
175 } else {
176 Self::Authorization(value)
177 }
178 }
179}
180
181/// Error response returned by server after requesting an authorization code.
182///
183/// The fields in this structure are defined in [Section 4.1.2.1 of RFC 6749].
184///
185/// [Section 4.1.2.1 of RFC 6749]: https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1
186#[derive(Clone, StringEnum, PartialEqAsRefStr, Eq)]
187#[ruma_enum(rename_all = "snake_case")]
188#[non_exhaustive]
189pub enum AuthorizationCodeErrorResponseType {
190 /// The request is invalid.
191 ///
192 /// It is missing a required parameter, includes an invalid parameter value,
193 /// includes a parameter more than once, or is otherwise malformed.
194 InvalidRequest,
195
196 /// The client is not authorized to request an authorization code using this
197 /// method.
198 UnauthorizedClient,
199
200 /// The resource owner or authorization server denied the request.
201 AccessDenied,
202
203 /// The authorization server does not support obtaining an authorization
204 /// code using this method.
205 UnsupportedResponseType,
206
207 /// The requested scope is invalid, unknown, or malformed.
208 InvalidScope,
209
210 /// The authorization server encountered an unexpected error.
211 ServerError,
212
213 /// The authorization server is currently unable to handle the request due
214 /// to a temporary overloading or maintenance of the server.
215 TemporarilyUnavailable,
216
217 #[doc(hidden)]
218 _Custom(PrivOwnedStr),
219}
220
221impl ErrorResponseType for AuthorizationCodeErrorResponseType {}
222
223/// All errors that can occur when revoking an OAuth 2.0 token.
224#[derive(Debug, thiserror::Error)]
225#[non_exhaustive]
226pub enum OauthTokenRevocationError {
227 /// Revocation is not supported by the OAuth 2.0 authorization server.
228 #[error("token revocation is not supported")]
229 NotSupported,
230
231 /// The revocation endpoint URL is insecure.
232 #[error(transparent)]
233 Url(ConfigurationError),
234
235 /// An error occurred interacting with the OAuth 2.0 authorization server
236 /// while revoking the token.
237 #[error("failed to revoke token: {0}")]
238 Revoke(RequestTokenError<HttpClientError<reqwest::Error>, BasicRevocationErrorResponse>),
239}