oauth2_apple/
client_credentials_grant.rs1use oauth2_client::{
2 re_exports::{Body, ClientId, ClientSecret, Request, Url, UrlParseError},
3 Provider, ProviderExtClientCredentialsGrant,
4};
5
6use crate::{AppleScope, OAUTH2_TOKEN_URL};
7
8#[derive(Debug, Clone)]
12pub struct AppleProviderForSearchAdsApi {
13 client_id: ClientId,
14 client_secret: ClientSecret,
15 token_endpoint_url: Url,
17}
18impl AppleProviderForSearchAdsApi {
19 pub fn new(client_id: ClientId, client_secret: ClientSecret) -> Result<Self, UrlParseError> {
20 Ok(Self {
21 client_id,
22 client_secret,
23 token_endpoint_url: OAUTH2_TOKEN_URL.parse()?,
24 })
25 }
26}
27impl Provider for AppleProviderForSearchAdsApi {
28 type Scope = AppleScope;
29
30 fn client_id(&self) -> Option<&ClientId> {
31 Some(&self.client_id)
32 }
33
34 fn client_secret(&self) -> Option<&ClientSecret> {
35 Some(&self.client_secret)
36 }
37
38 fn token_endpoint_url(&self) -> &Url {
39 &self.token_endpoint_url
40 }
41}
42impl ProviderExtClientCredentialsGrant for AppleProviderForSearchAdsApi {
43 fn scopes_default(&self) -> Option<Vec<<Self as Provider>::Scope>> {
44 Some(vec![AppleScope::Searchadsorg])
45 }
46
47 fn access_token_request_url_modifying(&self, url: &mut Url) {
48 let mut query_pairs_mut = url.query_pairs_mut();
49 query_pairs_mut.clear();
50
51 query_pairs_mut.append_pair("grant_type", "client_credentials");
52 query_pairs_mut.append_pair("scope", AppleScope::Searchadsorg.to_string().as_str());
53 query_pairs_mut.append_pair("client_id", &self.client_id);
54 query_pairs_mut.append_pair("client_secret", &self.client_secret);
55
56 query_pairs_mut.finish();
57 }
58
59 fn access_token_request_modifying(&self, request: &mut Request<Body>) {
60 let body = request.body_mut();
61 body.clear();
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 use oauth2_client::{
70 client_credentials_grant::AccessTokenEndpoint,
71 re_exports::{Endpoint as _, Response},
72 };
73
74 #[test]
75 fn access_token_request_for_search_ads_api() -> Result<(), Box<dyn std::error::Error>> {
76 let provider = AppleProviderForSearchAdsApi::new(
77 "SEARCHADS.27478e71-3bb0-4588-998c-182e2b405577".to_owned(),
78 "eyJhbGciOiJFUzI1NiIsImtpZCI6ImJhY2FlYmRhLWUyMTktNDFlZS1hOTA3LWUyYzI1YjI0ZDFiMiJ9.eyJpc3MiOiJTRUFSQ0hBRFMuMjc0NzhlNzEtM2JiMC00NTg4LTk5OGMtMTgyZTJiNDA1NTc3IiwiaWF0IjoxNjU0NDczNjAwLCJleHAiOjE2NzAwMjU2MDAsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJTRUFSQ0hBRFMuMjc0NzhlNzEtM2JiMC00NTg4LTk5OGMtMTgyZTJiNDA1NTc3In0.bN3KRWDJft-rjqRbOuuzfsImPT4RPEy01ILYJRBe4v_WJtJdi-7xBpi9UCcSN1WRe3Ozobvou5ruxXjVFnB_6Q".to_owned(),
79 )?;
80
81 let request = AccessTokenEndpoint::new(&provider, None).render_request()?;
82
83 assert_eq!(request.method(), "POST");
84 assert_eq!(
85 request.uri(),
86 "https://appleid.apple.com/auth/oauth2/token?grant_type=client_credentials&scope=searchadsorg&client_id=SEARCHADS.27478e71-3bb0-4588-998c-182e2b405577&client_secret=eyJhbGciOiJFUzI1NiIsImtpZCI6ImJhY2FlYmRhLWUyMTktNDFlZS1hOTA3LWUyYzI1YjI0ZDFiMiJ9.eyJpc3MiOiJTRUFSQ0hBRFMuMjc0NzhlNzEtM2JiMC00NTg4LTk5OGMtMTgyZTJiNDA1NTc3IiwiaWF0IjoxNjU0NDczNjAwLCJleHAiOjE2NzAwMjU2MDAsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJTRUFSQ0hBRFMuMjc0NzhlNzEtM2JiMC00NTg4LTk5OGMtMTgyZTJiNDA1NTc3In0.bN3KRWDJft-rjqRbOuuzfsImPT4RPEy01ILYJRBe4v_WJtJdi-7xBpi9UCcSN1WRe3Ozobvou5ruxXjVFnB_6Q"
87 );
88 assert_eq!(
89 request.headers().get("Content-Type").unwrap().as_bytes(),
90 b"application/x-www-form-urlencoded"
91 );
92 assert_eq!(request.body(), b"");
93
94 Ok(())
95 }
96
97 #[test]
98 fn access_token_response_for_search_ads_api() -> Result<(), Box<dyn std::error::Error>> {
99 let provider =
100 AppleProviderForSearchAdsApi::new("CLIENT_ID".to_owned(), "CLIENT_SECRET".to_owned())?;
101
102 let response_body =
103 include_str!("../tests/response_body_json_files/access_token_for_search_ads_api.json");
104 let body_ret = AccessTokenEndpoint::new(&provider, None)
105 .parse_response(Response::builder().body(response_body.as_bytes().to_vec())?)?;
106
107 match body_ret {
108 Ok(body) => {
109 assert_eq!(body.expires_in, Some(3600));
110 }
111 Err(body) => panic!("{body:?}"),
112 }
113
114 Ok(())
115 }
116}