inth-oauth2/src/client/mod.rs

154 lines
4.2 KiB
Rust

//! Client.
use std::marker::PhantomData;
use hyper;
use url::{self, form_urlencoded, Url};
use provider::Provider;
pub use self::from_response::{FromResponse, ParseError};
mod from_response;
/// OAuth 2.0 client.
pub struct Client<P: Provider> {
http_client: hyper::Client,
client_id: String,
client_secret: String,
redirect_uri: Option<String>,
provider: PhantomData<P>,
}
impl<P: Provider> Client<P> {
/// Creates a client.
///
/// # Examples
///
/// ```
/// use inth_oauth2::client::Client;
/// use inth_oauth2::provider::Google;
///
/// let client = Client::<Google>::new(
/// Default::default(),
/// "CLIENT_ID",
/// "CLIENT_SECRET",
/// Some("urn:ietf:wg:oauth:2.0:oob")
/// );
/// ```
pub fn new<S>(
http_client: hyper::Client,
client_id: S,
client_secret: S,
redirect_uri: Option<S>
) -> Self where S: Into<String> {
Client {
http_client: http_client,
client_id: client_id.into(),
client_secret: client_secret.into(),
redirect_uri: redirect_uri.map(Into::into),
provider: PhantomData,
}
}
/// Returns an authorization endpoint URI to direct the user to.
///
/// See [RFC 6749, section 3.1](http://tools.ietf.org/html/rfc6749#section-3.1).
///
/// # Examples
///
/// ```
/// use inth_oauth2::client::Client;
/// use inth_oauth2::provider::Google;
///
/// let client = Client::<Google>::new(
/// Default::default(),
/// "CLIENT_ID",
/// "CLIENT_SECRET",
/// Some("urn:ietf:wg:oauth:2.0:oob")
/// );
///
/// let auth_uri = client.auth_uri(
/// Some("https://www.googleapis.com/auth/userinfo.email"),
/// None
/// );
/// ```
pub fn auth_uri(&self, scope: Option<&str>, state: Option<&str>) -> Result<String, url::ParseError>
{
let mut uri = try!(Url::parse(P::auth_uri()));
let mut query_pairs = vec![
("response_type", "code"),
("client_id", &self.client_id),
];
if let Some(ref redirect_uri) = self.redirect_uri {
query_pairs.push(("redirect_uri", redirect_uri));
}
if let Some(scope) = scope {
query_pairs.push(("scope", scope));
}
if let Some(state) = state {
query_pairs.push(("state", state));
}
uri.set_query_from_pairs(query_pairs.iter());
Ok(uri.serialize())
}
}
#[cfg(test)]
mod tests {
use token::{Bearer, Static};
use provider::Provider;
use super::Client;
struct Test;
impl Provider for Test {
type Lifetime = Static;
type Token = Bearer<Static>;
fn auth_uri() -> &'static str { "http://example.com/oauth2/auth" }
fn token_uri() -> &'static str { "http://example.com/oauth2/token" }
}
#[test]
fn auth_uri() {
let client = Client::<Test>::new(Default::default(), "foo", "bar", None);
assert_eq!(
"http://example.com/oauth2/auth?response_type=code&client_id=foo",
client.auth_uri(None, None).unwrap()
);
}
#[test]
fn auth_uri_with_redirect_uri() {
let client = Client::<Test>::new(
Default::default(),
"foo",
"bar",
Some("http://example.com/oauth2/callback")
);
assert_eq!(
"http://example.com/oauth2/auth?response_type=code&client_id=foo&redirect_uri=http%3A%2F%2Fexample.com%2Foauth2%2Fcallback",
client.auth_uri(None, None).unwrap()
);
}
#[test]
fn auth_uri_with_scope() {
let client = Client::<Test>::new(Default::default(), "foo", "bar", None);
assert_eq!(
"http://example.com/oauth2/auth?response_type=code&client_id=foo&scope=baz",
client.auth_uri(Some("baz"), None).unwrap()
);
}
#[test]
fn auth_uri_with_state() {
let client = Client::<Test>::new(Default::default(), "foo", "bar", None);
assert_eq!(
"http://example.com/oauth2/auth?response_type=code&client_id=foo&state=baz",
client.auth_uri(None, Some("baz")).unwrap()
);
}
}