oauth2/
revocation.rs

1use serde::{Deserialize, Serialize};
2use std::fmt::Error as FormatterError;
3use std::fmt::{Debug, Display, Formatter};
4
5use crate::{basic::BasicErrorResponseType, ErrorResponseType};
6use crate::{AccessToken, RefreshToken};
7
8///
9/// A revocable token.
10///
11/// Implement this trait to indicate support for token revocation per [RFC 7009 OAuth 2.0 Token Revocation](https://tools.ietf.org/html/rfc7009#section-2.2).
12///
13pub trait RevocableToken {
14    ///
15    /// The actual token value to be revoked.
16    ///
17    fn secret(&self) -> &str;
18
19    ///
20    /// Indicates the type of the token being revoked, as defined by [RFC 7009, Section 2.1](https://tools.ietf.org/html/rfc7009#section-2.1).
21    ///
22    /// Implementations should return `Some(...)` values for token types that the target authorization servers are
23    /// expected to know (e.g. because they are registered in the [OAuth Token Type Hints Registry](https://tools.ietf.org/html/rfc7009#section-4.1.2))
24    /// so that they can potentially optimize their search for the token to be revoked.
25    ///
26    fn type_hint(&self) -> Option<&str>;
27}
28
29///
30/// A token representation usable with authorization servers that support [RFC 7009](https://tools.ietf.org/html/rfc7009) token revocation.
31///
32/// For use with [`revoke_token()`].
33///
34/// Automatically reports the correct RFC 7009 [`token_type_hint`](https://tools.ietf.org/html/rfc7009#section-2.1) value corresponding to the token type variant used, i.e.
35/// `access_token` for [`AccessToken`] and `secret_token` for [`RefreshToken`].
36///
37/// # Example
38///
39/// Per [RFC 7009, Section 2](https://tools.ietf.org/html/rfc7009#section-2) prefer revocation by refresh token which,
40/// if issued to the client, must be supported by the server, otherwise fallback to access token (which may or may not
41/// be supported by the server).
42///
43/// ```ignore
44/// let token_to_revoke: StandardRevocableToken = match token_response.refresh_token() {
45///     Some(token) => token.into(),
46///     None => token_response.access_token().into(),
47/// };
48///
49/// client
50///     .revoke_token(token_to_revoke)
51///     .request(http_client)
52///     .unwrap();
53/// ```
54///
55/// [`revoke_token()`]: crate::Client::revoke_token()
56///
57#[derive(Clone, Debug, Deserialize, Serialize)]
58#[non_exhaustive]
59pub enum StandardRevocableToken {
60    /// A representation of an [`AccessToken`] suitable for use with [`revoke_token()`](crate::Client::revoke_token()).
61    AccessToken(AccessToken),
62    /// A representation of an [`RefreshToken`] suitable for use with [`revoke_token()`](crate::Client::revoke_token()).
63    RefreshToken(RefreshToken),
64}
65impl RevocableToken for StandardRevocableToken {
66    fn secret(&self) -> &str {
67        match self {
68            Self::AccessToken(token) => token.secret(),
69            Self::RefreshToken(token) => token.secret(),
70        }
71    }
72
73    ///
74    /// Indicates the type of the token to be revoked, as defined by [RFC 7009, Section 2.1](https://tools.ietf.org/html/rfc7009#section-2.1), i.e.:
75    ///
76    /// * `access_token`: An access token as defined in [RFC 6749,
77    ///   Section 1.4](https://tools.ietf.org/html/rfc6749#section-1.4)
78    ///
79    /// * `refresh_token`: A refresh token as defined in [RFC 6749,
80    ///   Section 1.5](https://tools.ietf.org/html/rfc6749#section-1.5)
81    ///
82    fn type_hint(&self) -> Option<&str> {
83        match self {
84            StandardRevocableToken::AccessToken(_) => Some("access_token"),
85            StandardRevocableToken::RefreshToken(_) => Some("refresh_token"),
86        }
87    }
88}
89
90impl From<AccessToken> for StandardRevocableToken {
91    fn from(token: AccessToken) -> Self {
92        Self::AccessToken(token)
93    }
94}
95
96impl From<&AccessToken> for StandardRevocableToken {
97    fn from(token: &AccessToken) -> Self {
98        Self::AccessToken(token.clone())
99    }
100}
101
102impl From<RefreshToken> for StandardRevocableToken {
103    fn from(token: RefreshToken) -> Self {
104        Self::RefreshToken(token)
105    }
106}
107
108impl From<&RefreshToken> for StandardRevocableToken {
109    fn from(token: &RefreshToken) -> Self {
110        Self::RefreshToken(token.clone())
111    }
112}
113
114///
115/// OAuth 2.0 Token Revocation error response types.
116///
117/// These error types are defined in
118/// [Section 2.2.1 of RFC 7009](https://tools.ietf.org/html/rfc7009#section-2.2.1) and
119/// [Section 5.2 of RFC 6749](https://tools.ietf.org/html/rfc8628#section-5.2)
120///
121#[derive(Clone, PartialEq)]
122pub enum RevocationErrorResponseType {
123    ///
124    /// The authorization server does not support the revocation of the presented token type.
125    ///
126    UnsupportedTokenType,
127    ///
128    /// The authorization server responded with some other error as defined [RFC 6749](https://tools.ietf.org/html/rfc6749) error.
129    ///
130    Basic(BasicErrorResponseType),
131}
132impl RevocationErrorResponseType {
133    fn from_str(s: &str) -> Self {
134        match BasicErrorResponseType::from_str(s) {
135            BasicErrorResponseType::Extension(ext) => match ext.as_str() {
136                "unsupported_token_type" => RevocationErrorResponseType::UnsupportedTokenType,
137                _ => RevocationErrorResponseType::Basic(BasicErrorResponseType::Extension(ext)),
138            },
139            basic => RevocationErrorResponseType::Basic(basic),
140        }
141    }
142}
143impl AsRef<str> for RevocationErrorResponseType {
144    fn as_ref(&self) -> &str {
145        match self {
146            RevocationErrorResponseType::UnsupportedTokenType => "unsupported_token_type",
147            RevocationErrorResponseType::Basic(basic) => basic.as_ref(),
148        }
149    }
150}
151impl<'de> serde::Deserialize<'de> for RevocationErrorResponseType {
152    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153    where
154        D: serde::de::Deserializer<'de>,
155    {
156        let variant_str = String::deserialize(deserializer)?;
157        Ok(Self::from_str(&variant_str))
158    }
159}
160impl serde::ser::Serialize for RevocationErrorResponseType {
161    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
162    where
163        S: serde::ser::Serializer,
164    {
165        serializer.serialize_str(self.as_ref())
166    }
167}
168impl ErrorResponseType for RevocationErrorResponseType {}
169impl Debug for RevocationErrorResponseType {
170    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
171        Display::fmt(self, f)
172    }
173}
174
175impl Display for RevocationErrorResponseType {
176    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
177        write!(f, "{}", self.as_ref())
178    }
179}