facebook.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. use hyper;
  2. use hyper::net::NetworkListener;
  3. use hyper::server::Request;
  4. use hyper::server::Response;
  5. use hyper::uri::RequestUri;
  6. use hyper::header::AccessControlAllowOrigin;
  7. use rand::{self, Rng};
  8. use serde_json;
  9. use std::collections::BTreeMap;
  10. use std::io::Read;
  11. use std::sync::{mpsc, Mutex};
  12. use url;
  13. use protocol::authentication::AuthenticationType;
  14. use authentication::Credentials;
  15. use ::spotilocal::ssl_context;
  16. struct ServerHandler {
  17. token_tx: Mutex<mpsc::Sender<String>>,
  18. csrf: String,
  19. }
  20. impl ServerHandler {
  21. fn handle_login(&self, params: &BTreeMap<String, String>) -> hyper::status::StatusCode {
  22. let token = params.get("access_token").unwrap();
  23. let csrf = params.get("csrf").unwrap();
  24. if *csrf == self.csrf {
  25. self.token_tx.lock().unwrap().send(token.to_owned()).unwrap();
  26. hyper::status::StatusCode::Ok
  27. } else {
  28. hyper::status::StatusCode::Forbidden
  29. }
  30. }
  31. }
  32. impl hyper::server::Handler for ServerHandler {
  33. fn handle<'a, 'k>(&'a self, request: Request<'a, 'k>, mut response: Response<'a, hyper::net::Fresh>) {
  34. response.headers_mut().set(AccessControlAllowOrigin::Value("https://login.spotify.com".to_owned()));
  35. *response.status_mut() = if let RequestUri::AbsolutePath(path) = request.uri {
  36. let (path, query, _) = url::parse_path(&path).unwrap();
  37. let params = query.map_or(vec![], |q| url::form_urlencoded::parse(q.as_bytes()))
  38. .into_iter().collect::<BTreeMap<_,_>>();
  39. debug!("{:?} {:?} {:?}", request.method, path, params);
  40. if request.method == hyper::method::Method::Get && path == vec!["login", "facebook_login_sso.json"] {
  41. self.handle_login(&params)
  42. } else {
  43. hyper::status::StatusCode::NotFound
  44. }
  45. } else {
  46. hyper::status::StatusCode::NotFound
  47. }
  48. }
  49. }
  50. fn facebook_get_me_id(token: &str) -> Result<String, ()> {
  51. let url = format!("https://graph.facebook.com/me?fields=id&access_token={}", token);
  52. let client = hyper::Client::new();
  53. let mut response = client.get(&url).send().unwrap();
  54. let mut body = String::new();
  55. response.read_to_string(&mut body).unwrap();
  56. let mut result : BTreeMap<String, String> = serde_json::from_str(&body).unwrap();
  57. Ok(result.remove("id").unwrap())
  58. }
  59. pub fn facebook_login() -> Result<Credentials, ()> {
  60. let (tx, rx) = mpsc::channel();
  61. let csrf = rand::thread_rng().gen_ascii_chars().take(32).collect::<String>();
  62. let handler = ServerHandler {
  63. token_tx: Mutex::new(tx),
  64. csrf: csrf.clone()
  65. };
  66. let ssl = ssl_context().unwrap();
  67. let mut listener = hyper::net::HttpsListener::new("127.0.0.1:0", ssl).unwrap();
  68. let port = listener.local_addr().unwrap().port();
  69. let mut server = hyper::Server::new(listener).handle(handler).unwrap();
  70. println!("Logging in using Facebook, please visit https://login.spotify.com/login-facebook-sso/?csrf={}&port={} in your browser.",
  71. csrf, port);
  72. let token = rx.recv().unwrap();
  73. let user_id = facebook_get_me_id(&token).unwrap();
  74. let cred = Credentials {
  75. username: user_id,
  76. auth_type: AuthenticationType::AUTHENTICATION_FACEBOOK_TOKEN,
  77. auth_data: token.as_bytes().to_owned(),
  78. };
  79. server.close().unwrap();
  80. Ok(cred)
  81. }