oauth2_amazon/
authorization_code_grant.rs

1use oauth2_client::{
2    authorization_code_grant::provider_ext::ProviderExtAuthorizationCodeGrantPkceSupportType,
3    re_exports::{ClientId, ClientSecret, RedirectUri, Url, UrlParseError},
4    Provider, ProviderExtAuthorizationCodeGrant,
5};
6
7use crate::{token_url, AmazonScope, AmazonTokenUrlRegion, AUTHORIZATION_URL};
8
9#[derive(Debug, Clone)]
10pub struct AmazonProviderWithWebServices {
11    client_id: ClientId,
12    client_secret: ClientSecret,
13    redirect_uri: RedirectUri,
14    //
15    token_endpoint_url: Url,
16    authorization_endpoint_url: Url,
17}
18impl AmazonProviderWithWebServices {
19    pub fn new(
20        client_id: ClientId,
21        client_secret: ClientSecret,
22        redirect_uri: RedirectUri,
23        token_url_region: impl Into<Option<AmazonTokenUrlRegion>>,
24    ) -> Result<Self, UrlParseError> {
25        Ok(Self {
26            client_id,
27            client_secret,
28            redirect_uri,
29            token_endpoint_url: token_url(token_url_region).parse()?,
30            authorization_endpoint_url: AUTHORIZATION_URL.parse()?,
31        })
32    }
33}
34impl Provider for AmazonProviderWithWebServices {
35    type Scope = AmazonScope;
36
37    fn client_id(&self) -> Option<&ClientId> {
38        Some(&self.client_id)
39    }
40
41    fn client_secret(&self) -> Option<&ClientSecret> {
42        Some(&self.client_secret)
43    }
44
45    fn token_endpoint_url(&self) -> &Url {
46        &self.token_endpoint_url
47    }
48}
49impl ProviderExtAuthorizationCodeGrant for AmazonProviderWithWebServices {
50    fn redirect_uri(&self) -> Option<&RedirectUri> {
51        Some(&self.redirect_uri)
52    }
53
54    fn pkce_support_type(&self) -> Option<ProviderExtAuthorizationCodeGrantPkceSupportType> {
55        Some(ProviderExtAuthorizationCodeGrantPkceSupportType::Yes)
56    }
57
58    fn scopes_default(&self) -> Option<Vec<<Self as Provider>::Scope>> {
59        Some(vec![AmazonScope::Profile])
60    }
61
62    fn authorization_endpoint_url(&self) -> &Url {
63        &self.authorization_endpoint_url
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    use oauth2_client::{
72        authorization_code_grant::{AccessTokenEndpoint, AuthorizationEndpoint},
73        re_exports::{Endpoint as _, Response},
74    };
75
76    #[test]
77    fn authorization_request() -> Result<(), Box<dyn std::error::Error>> {
78        let provider = AmazonProviderWithWebServices::new(
79            "CLIENT_ID".to_owned(),
80            "CLIENT_SECRET".to_owned(),
81            RedirectUri::new("https://client.example.com/cb")?,
82            None,
83        )?;
84
85        let request = AuthorizationEndpoint::new(
86            &provider,
87            vec![AmazonScope::Profile, AmazonScope::PostalCode],
88        )
89        .configure(|x| x.state = Some("STATE".to_owned()))
90        .render_request()?;
91
92        assert_eq!(request.uri(), "https://www.amazon.com/ap/oa?response_type=code&client_id=CLIENT_ID&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&scope=profile+postal_code&state=STATE");
93
94        Ok(())
95    }
96
97    #[test]
98    fn access_token_request() -> Result<(), Box<dyn std::error::Error>> {
99        let provider = AmazonProviderWithWebServices::new(
100            "CLIENT_ID".to_owned(),
101            "CLIENT_SECRET".to_owned(),
102            RedirectUri::new("https://client.example.com/cb")?,
103            None,
104        )?;
105
106        let request = AccessTokenEndpoint::new(&provider, "CODE".to_owned()).render_request()?;
107
108        assert_eq!(request.body(), b"grant_type=authorization_code&code=CODE&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb&client_id=CLIENT_ID&client_secret=CLIENT_SECRET");
109
110        Ok(())
111    }
112
113    #[test]
114    fn access_token_response() -> Result<(), Box<dyn std::error::Error>> {
115        let provider = AmazonProviderWithWebServices::new(
116            "CLIENT_ID".to_owned(),
117            "CLIENT_SECRET".to_owned(),
118            RedirectUri::new("https://client.example.com/cb")?,
119            None,
120        )?;
121
122        let response_body = include_str!(
123            "../tests/response_body_json_files/access_token_with_authorization_code_grant.json"
124        );
125        let body_ret = AccessTokenEndpoint::new(&provider, "CODE".to_owned())
126            .parse_response(Response::builder().body(response_body.as_bytes().to_vec())?)?;
127
128        match body_ret {
129            Ok(_body) => {}
130            Err(body) => panic!("{body:?}"),
131        }
132
133        Ok(())
134    }
135}