1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use serde::{Deserialize, Serialize};
use std::fmt::Error as FormatterError;
use std::fmt::{Debug, Display, Formatter};

use crate::{basic::BasicErrorResponseType, ErrorResponseType};
use crate::{AccessToken, RefreshToken};

///
/// A revocable token.
///
/// 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).
///
pub trait RevocableToken {
    ///
    /// The actual token value to be revoked.
    ///
    fn secret(&self) -> &str;

    ///
    /// 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).
    ///
    /// Implementations should return `Some(...)` values for token types that the target authorization servers are
    /// 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))
    /// so that they can potentially optimize their search for the token to be revoked.
    ///
    fn type_hint(&self) -> Option<&str>;
}

///
/// A token representation usable with authorization servers that support [RFC 7009](https://tools.ietf.org/html/rfc7009) token revocation.
///
/// For use with [`revoke_token()`].
///
/// 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.
/// `access_token` for [`AccessToken`] and `secret_token` for [`RefreshToken`].
///
/// # Example
///
/// Per [RFC 7009, Section 2](https://tools.ietf.org/html/rfc7009#section-2) prefer revocation by refresh token which,
/// if issued to the client, must be supported by the server, otherwise fallback to access token (which may or may not
/// be supported by the server).
///
/// ```ignore
/// let token_to_revoke: StandardRevocableToken = match token_response.refresh_token() {
///     Some(token) => token.into(),
///     None => token_response.access_token().into(),
/// };
///
/// client
///     .revoke_token(token_to_revoke)
///     .request(http_client)
///     .unwrap();
/// ```
///
/// [`revoke_token()`]: crate::Client::revoke_token()
///
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub enum StandardRevocableToken {
    /// A representation of an [`AccessToken`] suitable for use with [`revoke_token()`](crate::Client::revoke_token()).
    AccessToken(AccessToken),
    /// A representation of an [`RefreshToken`] suitable for use with [`revoke_token()`](crate::Client::revoke_token()).
    RefreshToken(RefreshToken),
}
impl RevocableToken for StandardRevocableToken {
    fn secret(&self) -> &str {
        match self {
            Self::AccessToken(token) => token.secret(),
            Self::RefreshToken(token) => token.secret(),
        }
    }

    ///
    /// 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.:
    ///
    /// * `access_token`: An access token as defined in [RFC 6749,
    ///   Section 1.4](https://tools.ietf.org/html/rfc6749#section-1.4)
    ///
    /// * `refresh_token`: A refresh token as defined in [RFC 6749,
    ///   Section 1.5](https://tools.ietf.org/html/rfc6749#section-1.5)
    ///
    fn type_hint(&self) -> Option<&str> {
        match self {
            StandardRevocableToken::AccessToken(_) => Some("access_token"),
            StandardRevocableToken::RefreshToken(_) => Some("refresh_token"),
        }
    }
}

impl From<AccessToken> for StandardRevocableToken {
    fn from(token: AccessToken) -> Self {
        Self::AccessToken(token)
    }
}

impl From<&AccessToken> for StandardRevocableToken {
    fn from(token: &AccessToken) -> Self {
        Self::AccessToken(token.clone())
    }
}

impl From<RefreshToken> for StandardRevocableToken {
    fn from(token: RefreshToken) -> Self {
        Self::RefreshToken(token)
    }
}

impl From<&RefreshToken> for StandardRevocableToken {
    fn from(token: &RefreshToken) -> Self {
        Self::RefreshToken(token.clone())
    }
}

///
/// OAuth 2.0 Token Revocation error response types.
///
/// These error types are defined in
/// [Section 2.2.1 of RFC 7009](https://tools.ietf.org/html/rfc7009#section-2.2.1) and
/// [Section 5.2 of RFC 6749](https://tools.ietf.org/html/rfc8628#section-5.2)
///
#[derive(Clone, PartialEq)]
pub enum RevocationErrorResponseType {
    ///
    /// The authorization server does not support the revocation of the presented token type.
    ///
    UnsupportedTokenType,
    ///
    /// The authorization server responded with some other error as defined [RFC 6749](https://tools.ietf.org/html/rfc6749) error.
    ///
    Basic(BasicErrorResponseType),
}
impl RevocationErrorResponseType {
    fn from_str(s: &str) -> Self {
        match BasicErrorResponseType::from_str(s) {
            BasicErrorResponseType::Extension(ext) => match ext.as_str() {
                "unsupported_token_type" => RevocationErrorResponseType::UnsupportedTokenType,
                _ => RevocationErrorResponseType::Basic(BasicErrorResponseType::Extension(ext)),
            },
            basic => RevocationErrorResponseType::Basic(basic),
        }
    }
}
impl AsRef<str> for RevocationErrorResponseType {
    fn as_ref(&self) -> &str {
        match self {
            RevocationErrorResponseType::UnsupportedTokenType => "unsupported_token_type",
            RevocationErrorResponseType::Basic(basic) => basic.as_ref(),
        }
    }
}
impl<'de> serde::Deserialize<'de> for RevocationErrorResponseType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let variant_str = String::deserialize(deserializer)?;
        Ok(Self::from_str(&variant_str))
    }
}
impl serde::ser::Serialize for RevocationErrorResponseType {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        serializer.serialize_str(self.as_ref())
    }
}
impl ErrorResponseType for RevocationErrorResponseType {}
impl Debug for RevocationErrorResponseType {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
        Display::fmt(self, f)
    }
}

impl Display for RevocationErrorResponseType {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatterError> {
        write!(f, "{}", self.as_ref())
    }
}