tlns_google_oauth2/
lib.rs1#![doc = include_str!("../README.md")]
2
3use std::fmt::Debug;
4
5use oauth2::{
6 self,
7 basic::{
8 BasicErrorResponse, BasicRevocationErrorResponse, BasicTokenIntrospectionResponse,
9 BasicTokenResponse, BasicTokenType,
10 },
11 CsrfToken, EmptyExtraTokenFields, EndpointNotSet, EndpointSet, StandardRevocableToken,
12};
13
14pub use tlns_google_oauth2_traits::{FromGoogleScope, ToGoogleScope};
15
16#[cfg(feature = "grouped-scopes")]
17pub mod grouped_scopes;
18
19#[cfg(feature = "scopes")]
20pub mod scopes;
21
22#[cfg(not(any(feature = "grouped-scopes", feature = "scopes")))]
23compile_error!("You must enable either `grouped-scopes` or `scopes` feature to use this crate.");
24
25#[derive(Clone, Debug)]
48pub struct GoogleOAuth2Client {
49 pub client: oauth2::Client<
52 BasicErrorResponse,
53 BasicTokenResponse,
54 BasicTokenIntrospectionResponse,
55 StandardRevocableToken,
56 BasicRevocationErrorResponse,
57 EndpointSet,
58 EndpointNotSet,
59 EndpointNotSet,
60 EndpointNotSet,
61 EndpointSet,
62 >,
63}
64
65#[derive(Clone, Debug)]
66pub struct Authentication<'a> {
68 pub redirect_url: String,
70 pub csrf_token: CsrfToken,
72 pub scopes: &'a [&'a dyn ToGoogleScope],
74}
75
76impl GoogleOAuth2Client {
77 pub fn new(
79 client_id: impl Into<String>,
80 client_secret: impl Into<String>,
81 redirect_uri: impl Into<String>,
82 ) -> Result<Self, oauth2::url::ParseError> {
83 let url = oauth2::RedirectUrl::new(redirect_uri.into())?;
84 Ok(Self {
85 client: oauth2::basic::BasicClient::new(oauth2::ClientId::new(client_id.into()))
86 .set_client_secret(oauth2::ClientSecret::new(client_secret.into()))
87 .set_auth_uri(
88 oauth2::AuthUrl::new(
89 "https://accounts.google.com/o/oauth2/v2/auth".to_string(),
90 )
91 .unwrap(),
92 )
93 .set_token_uri(
94 oauth2::TokenUrl::new("https://oauth2.googleapis.com/token".to_string())
95 .unwrap(),
96 )
97 .set_redirect_uri(url),
98 })
99 }
100
101 pub fn build_authorize_url<'a>(
120 &self,
121 csrf_token: Option<fn() -> CsrfToken>,
122 scopes: &'a [&'a dyn ToGoogleScope],
123 ) -> Authentication<'a> {
124 let auth_req = self
125 .client
126 .authorize_url(csrf_token.unwrap_or(CsrfToken::new_random))
127 .add_scopes(
128 scopes
129 .iter()
130 .map(|e| oauth2::Scope::new(e.to_google_scope().to_string())),
131 );
132
133 let res = auth_req.url();
134 Authentication {
135 redirect_url: res.0.to_string(),
136 csrf_token: res.1,
137 scopes,
138 }
139 }
140
141 pub async fn get_token(
144 &self,
145 auth_code: impl Into<String>,
146 http_client: Option<&oauth2::reqwest::Client>,
147 ) -> Result<
148 oauth2::StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>,
149 oauth2::RequestTokenError<
150 oauth2::HttpClientError<oauth2::reqwest::Error>,
151 oauth2::StandardErrorResponse<oauth2::basic::BasicErrorResponseType>,
152 >,
153 > {
154 let http_client = http_client
156 .map(|i| std::borrow::Cow::Borrowed(i))
157 .unwrap_or(std::borrow::Cow::Owned(oauth2::reqwest::Client::new()));
158 let res = self
159 .client
160 .exchange_code(oauth2::AuthorizationCode::new(auth_code.into()))
161 .request_async(http_client.as_ref())
162 .await?;
163 Ok(res)
164 }
165
166 pub async fn refresh_token(
169 &self,
170 refresh_token: impl Into<String>,
171 http_client: Option<&oauth2::reqwest::Client>,
172 ) -> Result<
173 oauth2::StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>,
174 oauth2::RequestTokenError<
175 oauth2::HttpClientError<oauth2::reqwest::Error>,
176 oauth2::StandardErrorResponse<oauth2::basic::BasicErrorResponseType>,
177 >,
178 > {
179 let http_client = http_client
181 .map(|i| std::borrow::Cow::Borrowed(i))
182 .unwrap_or(std::borrow::Cow::Owned(oauth2::reqwest::Client::new()));
183 let res = self
184 .client
185 .exchange_refresh_token(&oauth2::RefreshToken::new(refresh_token.into()))
186 .request_async(http_client.as_ref())
187 .await?;
188 Ok(res)
189 }
190}