inth_oauth2/client/
mod.rs1mod error;
4
5pub mod response;
6pub use self::error::ClientError;
7
8use reqwest;
9use reqwest::header::{ACCEPT, CONTENT_TYPE};
10use serde_json::{self, Value};
11use url::form_urlencoded::Serializer;
12use url::Url;
13
14use client::response::FromResponse;
15use error::OAuth2Error;
16use provider::Provider;
17use token::{Lifetime, Refresh, Token};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Client<P> {
22 pub provider: P,
24
25 pub client_id: String,
27
28 pub client_secret: String,
30
31 pub redirect_uri: Option<String>,
33}
34
35impl<P: Provider> Client<P> {
36 pub fn new(
52 provider: P,
53 client_id: String,
54 client_secret: String,
55 redirect_uri: Option<String>,
56 ) -> Self {
57 Client {
58 provider,
59 client_id,
60 client_secret,
61 redirect_uri,
62 }
63 }
64
65 pub fn auth_uri(&self, scope: Option<&str>, state: Option<&str>) -> Url
88 {
89 let mut uri = self.provider.auth_uri().clone();
90
91 {
92 let mut query = uri.query_pairs_mut();
93
94 query.append_pair("response_type", "code");
95 query.append_pair("client_id", &self.client_id);
96
97 if let Some(ref redirect_uri) = self.redirect_uri {
98 query.append_pair("redirect_uri", redirect_uri);
99 }
100 if let Some(scope) = scope {
101 query.append_pair("scope", scope);
102 }
103 if let Some(state) = state {
104 query.append_pair("state", state);
105 }
106 }
107
108 uri
109 }
110
111 fn post_token(
112 &self,
113 http_client: &reqwest::Client,
114 mut body: Serializer<String>,
115 ) -> Result<Value, ClientError> {
116 if self.provider.credentials_in_body() {
117 body.append_pair("client_id", &self.client_id);
118 body.append_pair("client_secret", &self.client_secret);
119 }
120
121 let body = body.finish();
122
123 let mut response = http_client
124 .post(self.provider.token_uri().clone())
125 .basic_auth(&self.client_id, Some(&self.client_secret))
126 .header(ACCEPT, "application/json")
127 .header(CONTENT_TYPE, "application/x-www-form-urlencoded")
128 .body(body)
129 .send()?;
130
131 let json = serde_json::from_reader(&mut response)?;
132
133 let error = OAuth2Error::from_response(&json);
134
135 if let Ok(error) = error {
136 Err(ClientError::from(error))
137 } else {
138 Ok(json)
139 }
140 }
141
142 pub fn request_token(
146 &self,
147 http_client: &reqwest::Client,
148 code: &str,
149 ) -> Result<P::Token, ClientError> {
150 let mut body = Serializer::new(String::new());
151 body.append_pair("grant_type", "authorization_code");
152 body.append_pair("code", code);
153
154 if let Some(ref redirect_uri) = self.redirect_uri {
155 body.append_pair("redirect_uri", redirect_uri);
156 }
157
158 let json = self.post_token(http_client, body)?;
159 let token = P::Token::from_response(&json)?;
160 Ok(token)
161 }
162}
163
164impl<P> Client<P> where P: Provider, P::Token: Token<Refresh> {
165 pub fn refresh_token(
169 &self,
170 http_client: &reqwest::Client,
171 token: P::Token,
172 scope: Option<&str>,
173 ) -> Result<P::Token, ClientError> {
174 let mut body = Serializer::new(String::new());
175 body.append_pair("grant_type", "refresh_token");
176 body.append_pair("refresh_token", token.lifetime().refresh_token());
177
178 if let Some(scope) = scope {
179 body.append_pair("scope", scope);
180 }
181
182 let json = self.post_token(http_client, body)?;
183 let token = P::Token::from_response_inherit(&json, &token)?;
184 Ok(token)
185 }
186
187 pub fn ensure_token(
189 &self,
190 http_client: &reqwest::Client,
191 token: P::Token,
192 ) -> Result<P::Token, ClientError> {
193 if token.lifetime().expired() {
194 self.refresh_token(http_client, token, None)
195 } else {
196 Ok(token)
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use url::Url;
204 use token::{Bearer, Static};
205 use provider::Provider;
206 use super::Client;
207
208 struct Test {
209 auth_uri: Url,
210 token_uri: Url
211 }
212 impl Provider for Test {
213 type Lifetime = Static;
214 type Token = Bearer<Static>;
215 fn auth_uri(&self) -> &Url { &self.auth_uri }
216 fn token_uri(&self) -> &Url { &self.token_uri }
217 }
218 impl Test {
219 fn new() -> Self {
220 Test {
221 auth_uri: Url::parse("http://example.com/oauth2/auth").unwrap(),
222 token_uri: Url::parse("http://example.com/oauth2/token").unwrap()
223 }
224 }
225 }
226
227 #[test]
228 fn auth_uri() {
229 let client = Client::new(Test::new(), String::from("foo"), String::from("bar"), None);
230 assert_eq!(
231 "http://example.com/oauth2/auth?response_type=code&client_id=foo",
232 client.auth_uri(None, None).as_str()
233 );
234 }
235
236 #[test]
237 fn auth_uri_with_redirect_uri() {
238 let client = Client::new(
239 Test::new(),
240 String::from("foo"),
241 String::from("bar"),
242 Some(String::from("http://example.com/oauth2/callback")),
243 );
244 assert_eq!(
245 "http://example.com/oauth2/auth?response_type=code&client_id=foo&redirect_uri=http%3A%2F%2Fexample.com%2Foauth2%2Fcallback",
246 client.auth_uri(None, None).as_str()
247 );
248 }
249
250 #[test]
251 fn auth_uri_with_scope() {
252 let client = Client::new(Test::new(), String::from("foo"), String::from("bar"), None);
253 assert_eq!(
254 "http://example.com/oauth2/auth?response_type=code&client_id=foo&scope=baz",
255 client.auth_uri(Some("baz"), None).as_str()
256 );
257 }
258
259 #[test]
260 fn auth_uri_with_state() {
261 let client = Client::new(Test::new(), String::from("foo"), String::from("bar"), None);
262 assert_eq!(
263 "http://example.com/oauth2/auth?response_type=code&client_id=foo&state=baz",
264 client.auth_uri(None, Some("baz")).as_str()
265 );
266 }
267}