1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Types and methods used to authenticate calls to Twitter.
//!
//! This module is meant to be internal, since the OAuth mechanisms are fairly specific to Twitter.
//! Any relevant items for obtaining a Token are re-exported in the crate root. As such, the
//! authentication overview is written on the Token type, rather than in this module docs.

use std;
use std::error::Error;
use std::borrow::Cow;
use std::time::{UNIX_EPOCH, SystemTime};

use futures::{Future, Poll, Async};
use hmac::{Hmac, Mac};
use hyper::header::{Authorization, Scheme, ContentType, Basic, Bearer, Headers};
use hyper::{Method, Request};
use mime::Mime;
use rand::{self, Rng};
use rustc_serialize::base64::{self, ToBase64};
use rustc_serialize::json;
use sha_1::Sha1;
use tokio_core::reactor::Handle;
use url::percent_encoding::{EncodeSet, utf8_percent_encode};

use links;
use error;
use common::*;

//the encode sets in the url crate don't quite match what twitter wants, so i'll make up my own
#[derive(Copy, Clone)]
struct TwitterEncodeSet;

impl EncodeSet for TwitterEncodeSet {
    fn contains(&self, byte: u8) -> bool {
        match byte {
            b'a' ... b'z' | b'A' ... b'Z' | b'0' ... b'9'
                | b'-' | b'.' | b'_' | b'~' => false,
            _ => true
        }
    }
}

///Encodes the given string slice for transmission to Twitter.
fn percent_encode(src: &str) -> String {
    utf8_percent_encode(src, TwitterEncodeSet).collect::<String>()
}

///OAuth header set given to Twitter calls.
///
///Since different authorization/authentication calls have various parameters that go into this
///header, they're optionally placed at the end of this header.  On the other hand, `signature` is
///optional so a structured header can be passed to `sign()` for signature.
#[derive(Clone, Debug)]
struct TwitterOAuth {
    consumer_key: String,
    nonce: String,
    signature: Option<String>,
    timestamp: u64,
    token: Option<String>,
    callback: Option<String>,
    verifier: Option<String>,
}

impl std::str::FromStr for TwitterOAuth {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut consumer_key: Option<String> = None;
        let mut nonce: Option<String> = None;
        let mut signature: Option<String> = None;
        let mut timestamp: Option<u64> = None;
        let mut token: Option<String> = None;
        let mut callback: Option<String> = None;
        let mut verifier: Option<String> = None;

        for substr in s.split(',') {
            let mut parts = substr.trim().split('=');
            match parts.next() {
                Some("oauth_consumer_key") => consumer_key = parts.next().map(str::to_string),
                Some("oauth_nonce") => nonce = parts.next().map(str::to_string),
                Some("oauth_signature") => signature = parts.next().map(str::to_string),
                Some("oauth_timestamp") => match parts.next().map(<u64 as std::str::FromStr>::from_str) {
                    Some(Ok(n)) => timestamp = Some(n),
                    Some(Err(e)) => return Err(e.description().to_string()),
                    None => timestamp = None,
                },
                Some("oauth_token") => token = parts.next().map(str::to_string),
                Some("oauth_callback") => callback = parts.next().map(str::to_string),
                Some("oauth_verifier") => verifier = parts.next().map(str::to_string),
                Some(_) => return Err("unexpected OAuth Authorization header field".to_string()),
                None => return Err("unexpected header format".to_string()),
            }
        }

        Ok(TwitterOAuth {
            consumer_key: try!(consumer_key.ok_or("no oauth_consumer_key")),
            nonce: try!(nonce.ok_or("no oauth_nonce")),
            signature: signature,
            timestamp: try!(timestamp.ok_or("no oauth_timestamp")),
            token: token,
            callback: callback,
            verifier: verifier,
        })
    }
}

impl Scheme for TwitterOAuth {
    fn scheme() -> Option<&'static str> {
        Some("OAuth")
    }

    fn fmt_scheme(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        try!(write!(f, "oauth_consumer_key=\"{}\"", percent_encode(&self.consumer_key)));

        try!(write!(f, ", oauth_nonce=\"{}\"", percent_encode(&self.nonce)));

        if let Some(ref signature) = self.signature {
            try!(write!(f, ", oauth_signature=\"{}\"", percent_encode(signature)));
        }

        try!(write!(f, ", oauth_signature_method=\"{}\"", percent_encode("HMAC-SHA1")));

        try!(write!(f, ", oauth_timestamp=\"{}\"", self.timestamp));

        if let Some(ref token) = self.token {
            try!(write!(f, ", oauth_token=\"{}\"", percent_encode(token)));
        }

        try!(write!(f, ", oauth_version=\"{}\"", "1.0"));

        if let Some(ref callback) = self.callback {
            try!(write!(f, ", oauth_callback=\"{}\"", percent_encode(callback)));
        }

        if let Some(ref verifier) = self.verifier {
            try!(write!(f, ", oauth_verifier=\"{}\"", percent_encode(verifier)));
        }

        Ok(())
    }
}

/// A key/secret pair representing an OAuth token.
///
/// This struct is used as part of the authentication process. You'll need to manually create at
/// least one of these, to hold onto your consumer token.
///
/// For more information, see the documentation for [Tokens][].
///
/// [Tokens]: enum.Token.html
///
/// # Example
///
/// ```rust
/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer token");
/// ```
#[derive(Debug, Clone)]
pub struct KeyPair {
    ///A key used to identify an application or user.
    pub key: Cow<'static, str>,
    ///A private key used to sign messages from an application or user.
    pub secret: Cow<'static, str>,
}

impl KeyPair {
    ///Creates a KeyPair with the given key and secret.
    ///
    ///This can be called with either `&'static str` (a string literal) or `String` for either
    ///parameter.
    pub fn new<K, S>(key: K, secret: S) -> KeyPair
        where K: Into<Cow<'static, str>>,
              S: Into<Cow<'static, str>>
    {
        KeyPair {
            key: key.into(),
            secret: secret.into(),
        }
    }
}

/// A token that can be used to sign requests to Twitter.
///
/// # Authenticating Requests With Twitter
///
/// A Token is given at the end of the authentication process, and is what you use to authenticate
/// every other call you make with Twitter. The process is different depending on whether you're
/// wanting to operate on behalf of a user, and on how easily your application can open a web
/// browser and/or redirect web requests to and from Twitter. For more information, see Twitter's
/// [OAuth documentation overview][OAuth].
///
/// [OAuth]: https://dev.twitter.com/oauth/overview
///
/// The very first thing you'll need to do to get access to the Twitter API is to head to
/// [Twitter's Application Manager][twitter-apps] and create an app. Once you've done that, there
/// are two sets of keys immediately available to you. First are the "consumer key" and "consumer
/// secret", that are used to represent you as the application author when signing requests. These
/// keys are given to every single API call regardless of permission level. Related are an "access
/// token" and "access token secret", that can be used to skip the authentication steps if you're
/// only interacting with your own account or with no account in particular. Generally, if you want
/// to read or write to a particular user's stream, you'll need to request authorization and get an
/// access token to work on their behalf.
///
/// [twitter-apps]: https://apps.twitter.com/
///
/// ## Access Tokens
///
/// Access tokens are for when you want to perform your requests on behalf of a specific user. This
/// could be for something like posting to their account, reading their home timeline, viewing
/// protected accounts they follow, and other actions that only make sense from the perspective
/// from a specific user. Because of the two-fold nature of making sure your requests are signed
/// from your specific *app* and from that specific *user*, the authentication process for access
/// tokens is fairly complicated.
///
/// The process to get an access token for a specific user (with this library) has three basic
/// steps:
///
/// 1. Log your request with Twitter by getting a [request token][].
/// 2. Direct the user to grant permission to your application by sending them to an
///    [authenticate][] or [authorize][] URL, depending on the nature of your app.
/// 3. Convert the verifier given by the permission request into an [access token][].
///
/// [request token]: fn.request_token.html
/// [authorize]: fn.authorize_url.html
/// [authenticate]: fn.authenticate_url.html
/// [access token]: fn.access_token.html
///
/// Before you get too deep into the authentication process, it helps to know a couple things about
/// the app you're writing:
///
/// * Is your app in an environment where directing users to and from a web page is easy? (e.g. a
///   website, or a mobile app)
/// * Are you using Twitter authentication as a substitute for user accounts, instead of just to
///   interact with their Twitter account?
///
/// Depending on your answer to the first question, you may need to use "PIN-Based Authorization",
/// where the user completes the authentication/authorization process in a separate window and
/// receives a numeric PIN in response that your app can use to complete the authentication
/// process. The alternative to that is the standard OAuth flow, where a web browser is directed to
/// Twitter to complete the login and authorization, then redirected back to your app to receive
/// the access token proper. The way to signal one method or another is by the `callback` parameter
/// to the [access token] request.
///
/// The second question informs *where* you send the user to authorize your app. Using the "Sign In
/// With Twitter" flow, your app could be able to transparently request another access token
/// without the user needing to accept the connection every time. This is ideal for websites where
/// a "Sign In With Twitter" button could replace a regular login button, instead using a user's
/// Twitter account in place for regular username/password credentials. To be able to use the "Sign
/// In With Twitter" flow, you must first enable it for your app on Twitter's Application Manager.
/// Then, for Step 2 of the authentication process, send the user to an [authenticate] URL.
///
/// The primary difference between the different URLs for Step 2 is that an [authenticate] URL
/// allows the above behavior, whereas an [authorize] URL does not require the extra setting in the
/// app manager and always requires the user to re-authorize the app every time they're sent
/// through the authentication process. As access tokens can be cached indefinitely until the app's
/// access is revoked, this is not necessarily as onerous as it sounds.
///
/// The end result of Step 2 is that your app receives a "verifier" to vouch for the user's
/// acceptance of your app. With PIN-Based Authorization, the user receives a PIN from Twitter that
/// acts as the verifier. With "Sign In With Twitter" and its counterpart, "3-Legged
/// Authorization", the verifier is given as a query parameter to the callback URL given back in
/// Step 1. With this verifier and the original request token, you can combine them with your app's
/// consumer token to get the [access token] that opens up the rest of the Twitter API.
///
/// ### Example (Access Token)
///
/// For "PIN-Based Authorization":
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core;
/// # use tokio_core::reactor::{Core, Handle};
/// # fn main() {
/// # let (mut core, handle): (Core, Handle) = unimplemented!();
/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
/// // "oob" is needed for PIN-based auth; see docs for `request_token` for more info
/// let request_token = core.run(egg_mode::request_token(&con_token, "oob", &handle)).unwrap();
/// let auth_url = egg_mode::authorize_url(&request_token);
///
/// // give auth_url to the user, they can sign in to Twitter and accept your app's permissions.
/// // they'll receive a PIN in return, they need to give this to your application
///
/// let verifier = "123456"; //read the PIN from the user here
///
/// // note this consumes con_token; if you want to sign in multiple accounts, clone it here
/// let (token, user_id, screen_name) =
///     core.run(egg_mode::access_token(con_token, &request_token, verifier, &handle)).unwrap();
///
/// // token can be given to any egg_mode method that asks for a token
/// // user_id and screen_name refer to the user who signed in
/// # }
/// ```
///
/// **WARNING**: The consumer token and preset access token mentioned below are as privileged as
/// passwords! If your consumer key pair leaks or is visible to the public, anyone can impersonate
/// your app! If you use a fixed token for your app, it's recommended to set them in separate files
/// and use `include_str!()` (from the standard library) to load them in, so you can safely exclude
/// them from source control.
///
/// ### Shortcut: Pre-Generated Access Token
///
/// If you only want to sign in as yourself, you can skip the request token authentication flow
/// entirely and instead use the access token key pair given alongside your app keys:
///
/// ```rust
/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
/// let access_token = egg_mode::KeyPair::new("access token key", "access token secret");
/// let token = egg_mode::Token::Access {
///     consumer: con_token,
///     access: access_token,
/// };
///
/// // token can be given to any egg_mode method that asks for a token
/// ```
///
/// ## Bearer Tokens
///
/// Bearer tokens are for when you want to perform requests on behalf of your app itself, instead
/// of a specific user. Bearer tokens are the API equivalent of viewing Twitter from a logged-out
/// session. Anything that's already public can be viewed, but things like protected users or the
/// home timeline can't be accessed with bearer tokens. On the other hand, because you don't need
/// to authenticate a user, obtaining a bearer token is relatively simple.
///
/// If a Bearer token will work for your purposes, use the following steps to get a Token:
///
/// 1. With the consumer key/secret obtained the same way as above, ask Twitter for the current
///    [Bearer token] for your application.
///
/// [Bearer token]: fn.bearer_token.html
///
/// And... that's it! This Bearer token can be cached and saved for future use. It will not expire
/// until you ask Twitter to [invalidate] the token for you. Otherwise, this token can be used the
/// same way as the [access token] from earlier, but with the restrictions mentioned above.
///
/// [invalidate]: fn.invalidate_bearer.html
///
/// ### Example (Bearer Token)
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core;
/// # use tokio_core::reactor::{Core, Handle};
/// # fn main() {
/// # let (mut core, handle): (Core, Handle) = unimplemented!();
/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
/// let token = core.run(egg_mode::bearer_token(&con_token, &handle)).unwrap();
///
/// // token can be given to *most* egg_mode methods that ask for a token
/// // for restrictions, see docs for bearer_token
/// # }
/// ```
#[derive(Debug, Clone)]
pub enum Token {
    ///An OAuth Access token indicating the request is coming from a specific user.
    Access {
        ///A "consumer" key/secret that represents the application sending the request.
        consumer: KeyPair,
        ///An "access" key/secret that represents the user's authorization of the application.
        access: KeyPair,
    },
    ///An OAuth Bearer token indicating the request is coming from the application itself, not a
    ///particular user.
    Bearer(String),
}

///With the given OAuth header and method parameters, create an OAuth signature and return the
///header with the signature inline.
fn sign(header: TwitterOAuth,
        method: Method,
        uri: &str,
        params: Option<&ParamList>,
        con_token: &KeyPair,
        access_token: Option<&KeyPair>) -> TwitterOAuth {
    let query_string = {
        let mut sig_params = params.cloned().unwrap_or_default();

        add_param(&mut sig_params, "oauth_consumer_key", header.consumer_key.as_str());
        add_param(&mut sig_params, "oauth_nonce", header.nonce.as_str());
        add_param(&mut sig_params, "oauth_signature_method", "HMAC-SHA1");
        add_param(&mut sig_params, "oauth_timestamp", format!("{}", header.timestamp));
        add_param(&mut sig_params, "oauth_version", "1.0");

        if let Some(ref token) = header.token {
            add_param(&mut sig_params, "oauth_token", token.as_str());
        }

        if let Some(ref callback) = header.callback {
            add_param(&mut sig_params, "oauth_callback", callback.as_str());
        }

        if let Some(ref verifier) = header.verifier {
            add_param(&mut sig_params, "oauth_verifier", verifier.as_str());
        }

        let mut query = sig_params.iter()
                                  .map(|(k, v)| format!("{}={}", percent_encode(k), percent_encode(v)))
                                  .collect::<Vec<_>>();
        query.sort();

        query.join("&")
    };

    let base_str = format!("{}&{}&{}",
                           percent_encode(method.as_ref()),
                           percent_encode(uri),
                           percent_encode(&query_string));
    let key = format!("{}&{}",
                      percent_encode(&con_token.secret),
                      percent_encode(&access_token.unwrap_or(&KeyPair::new("", "")).secret));

    let mut digest = Hmac::<Sha1>::new(key.as_bytes());
    digest.input(base_str.as_bytes());

    let config = base64::Config {
        char_set: base64::CharacterSet::Standard,
        newline: base64::Newline::LF,
        pad: true,
        line_length: None,
    };

    TwitterOAuth {
        signature: Some(digest.result().code().to_base64(config)),
        ..header
    }
}

///With the given method parameters, return a signed OAuth header.
fn get_header(method: Method,
              uri: &str,
              con_token: &KeyPair,
              access_token: Option<&KeyPair>,
              callback: Option<String>,
              verifier: Option<String>,
              params: Option<&ParamList>) -> TwitterOAuth {
    let now_s = match SystemTime::now().duration_since(UNIX_EPOCH) {
        Ok(dur) => dur,
        Err(err) => err.duration(),
    }.as_secs();
    let header = TwitterOAuth {
        consumer_key: con_token.key.to_string(),
        nonce: rand::thread_rng().gen_ascii_chars().take(32).collect::<String>(),
        signature: None,
        timestamp: now_s,
        token: access_token.map(|tok| tok.key.to_string()),
        callback: callback,
        verifier: verifier,
    };

    sign(header, method, uri, params, con_token, access_token)
}

fn bearer_request(con_token: &KeyPair) -> Basic {
    Basic {
        username: percent_encode(&con_token.key),
        password: Some(percent_encode(&con_token.secret)),
    }
}

/// Assemble a signed GET request to the given URL with the given parameters.
pub fn get(uri: &str,
           token: &Token,
           params: Option<&ParamList>) -> Request {
    let full_url = if let Some(p) = params {
        let query = p.iter()
                     .map(|(k, v)| format!("{}={}", percent_encode(k), percent_encode(v)))
                     .collect::<Vec<_>>()
                     .join("&");

        format!("{}?{}", uri, query)
    } else {
        uri.to_string()
    };

    let mut request: Request = Request::new(Method::Get, full_url.parse().unwrap());
    match *token {
        Token::Access {
            consumer: ref con_token,
            access: ref access_token,
        } => {
            let header = get_header(Method::Get, uri, con_token, Some(access_token),
                                    None, None, params);
            request.headers_mut().set(Authorization(header));
        },
        Token::Bearer(ref token) => {
            request.headers_mut().set(Authorization(Bearer { token: token.clone() }));
        },
    }

    request
}

/// Assemble a signed POST request to the given URL with the given parameters.
pub fn post(uri: &str,
            token: &Token,
            params: Option<&ParamList>) -> Request {
    let content: Mime = "application/x-www-form-urlencoded".parse().unwrap();
    let body = if let Some(p) = params {
        p.iter()
         .map(|(k, v)| format!("{}={}", k, percent_encode(v)))
         .collect::<Vec<_>>()
         .join("&")
    } else {
        "".to_string()
    };

    let mut request: Request = Request::new(Method::Post, uri.parse().unwrap());
    request.set_body(body);
    request.headers_mut().set(ContentType(content));

    match *token {
        Token::Access {
            consumer: ref con_token,
            access: ref access_token,
        } => {
            let header = get_header(Method::Post, uri, con_token, Some(access_token),
                                    None, None, params);

            request.headers_mut().set(Authorization(header));
        },
        Token::Bearer(ref token) => {
            request.headers_mut().set(Authorization(Bearer { token: token.clone() }));
        },
    }

    request
}

/// Assemble a signed POST request to the given URL with the given JSON body.
pub fn post_json(uri: &str, token: &Token, body: &json::Json) -> Request {
    let content: Mime = "application/json; charset=UTF-8".parse().unwrap();
    let body = body.to_string();

    let mut request: Request = Request::new(Method::Post, uri.parse().unwrap());
    request.set_body(body);
    request.headers_mut().set(ContentType(content));

    match *token {
        Token::Access {
            consumer: ref con_token,
            access: ref access_token,
        } => {
            let header = get_header(Method::Post, uri, con_token, Some(access_token),
                                    None, None, None);

            request.headers_mut().set(Authorization(header));
        },
        Token::Bearer(ref token) => {
            request.headers_mut().set(Authorization(Bearer { token: token.clone() }));
        },
    }

    request
}

/// With the given consumer KeyPair, ask Twitter for a request KeyPair that can be used to request
/// access to the user's account.
///
/// # Access Token Authentication
///
/// [Authentication overview](enum.Token.html)
///
/// 1. **Request Token**: Authenticate your application
/// 2. [Authorize]/[Authenticate]: Authenticate the user
/// 3. [Access Token]: Combine the authentication
///
/// [Authorize]: fn.authorize_url.html
/// [Authenticate]: fn.authenticate_url.html
/// [Access Token]: fn.access_token.html
///
/// # Request Token: Authenticate your application
///
/// To begin the authentication process, first log your request with Twitter by authenticating your
/// application by itself. This "request token" is used in later steps to match all the requests to
/// the same authentication attempt.
///
/// The parameter `callback` is used differently based on how your program is set up, and which
/// authentication process you'd like to use. For applications where directing users to and from
/// another web page is difficult, you can use the special value `"oob"` to indicate that you would
/// like to use PIN-Based Authentication.
///
/// Web-based applications and those that can handle web redirects transparently can instead supply
/// a callback URL for that parameter. When the user completes the sign-in and authentication
/// process, they will be directed to the provided URL with the information necessary to complete
/// the authentication process. Depending on which Step 2 URL you use and whether you've enabled it
/// for your app, this is called "Sign In With Twitter" or "3-Legged Authorization".
///
/// With this Request Token, you can assemble an [Authorize] or [Authenticate] URL that will allow
/// the user to log in with Twitter and allow your app access to their account. See the
/// Authentication Overview for more details, but the short version is that you want to use
/// [Authenticate] for "Sign In With Twitter" functionality, and [Authorize] if not.
///
/// # Examples
///
/// ```rust,no_run
/// # extern crate egg_mode; extern crate tokio_core;
/// # use tokio_core::reactor::{Core, Handle};
/// # fn main() {
/// # let (mut core, handle): (Core, Handle) = unimplemented!();
/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer token");
/// // for PIN-Based Auth
/// let req_token = core.run(egg_mode::request_token(&con_token, "oob", &handle)).unwrap();
/// // for Sign In With Twitter/3-Legged Auth
/// let req_token = core.run(egg_mode::request_token(&con_token,
///                                                  "https://myapp.io/auth",
///                                                  &handle)).unwrap();
/// # }
/// ```
pub fn request_token<S: Into<String>>(con_token: &KeyPair, callback: S, handle: &Handle)
    -> TwitterFuture<KeyPair>
{
    let header = get_header(Method::Post, links::auth::REQUEST_TOKEN,
                            con_token, None, Some(callback.into()), None, None);

    let mut request = Request::new(Method::Post, links::auth::REQUEST_TOKEN.parse().unwrap());
    request.headers_mut().set(Authorization(header));

    fn parse_tok(full_resp: String, _: &Headers) -> Result<KeyPair, error::Error> {
        let mut key: Option<String> = None;
        let mut secret: Option<String> = None;

        for elem in full_resp.split('&') {
            let mut kv = elem.splitn(2, '=');
            match kv.next() {
                Some("oauth_token") => key = kv.next().map(|s| s.to_string()),
                Some("oauth_token_secret") => secret = kv.next().map(|s| s.to_string()),
                Some(_) => (),
                None =>
                    return Err(
                        error::Error::InvalidResponse(
                            "unexpected end of request_token response", None
                        )
                    ),
            }
        }

        Ok(KeyPair::new(try!(key.ok_or(error::Error::MissingValue("oauth_token"))),
                        try!(secret.ok_or(error::Error::MissingValue("oauth_token_secret")))))
    }

    make_future(handle, request, parse_tok)
}

/// With the given request KeyPair, return a URL that a user can access to accept or reject an
/// authorization request.
///
/// # Access Token Authentication
///
/// [Authentication overview](enum.Token.html)
///
/// 1. [Request Token]: Authenticate your application
/// 2. **Authorize**/[Authenticate]: Authenticate the user
/// 3. [Access Token]: Combine the authentication
///
/// [Request Token]: fn.request_token.html
/// [Authenticate]: fn.authenticate_url.html
/// [Access Token]: fn.access_token.html
///
/// # Authorize: Authenticate the user
///
/// This function is part of the step of authenticating a user with Twitter so they can authorize
/// your application to access their account. This function generates a URL with the given request
/// token that you must give to the user. What happens with this URL depends on what you used as
/// the `callback` parameter for `request_token`.
///
/// If you gave a callback URL to `request_token`, Twitter will redirect the user to that URL after
/// they log in and accept your app's permissions. There will be two query string parameters added
/// to the URL for this redirect: `oauth_token`, which contains the `key` from the [request token]
/// used here, and `oauth_verifier`, which contains a verifier string that can be used to create
/// the final [access token]. Note that if this URL is used instead of [Authenticate], the user
/// will need to accept the app's connection each time, even if they have connected the app
/// previously and have not revoked the app's permissions. This process is called [3-legged
/// authorization]. If you would like the user to transparently be redirected without confirmation
/// if they've already accepted the connection, see the docs for [Authenticate] to read about "Sign
/// In With Twitter".
///
/// [3-legged authorization]: https://dev.twitter.com/oauth/3-legged
///
/// If you gave the special value `"oob"` to `request_token`, this URL can be directly shown to the
/// user, who can enter it into a separate web browser to complete the authorization. This is
/// called [PIN-based authorization] and it's required for applications that cannot be reached by
/// redirecting a URL from a web browser. When the user loads this URL, they can sign in with
/// Twitter and grant your app access to their account. If they grant this access, they are given a
/// numeric PIN that your app can use as the "verifier" to create the final [access token].
///
/// [Pin-Based authorization]: https://dev.twitter.com/oauth/pin-based
pub fn authorize_url(request_token: &KeyPair) -> String {
    format!("{}?oauth_token={}", links::auth::AUTHORIZE, request_token.key)
}

/// With the given request KeyPair, return a URL to redirect a user to so they can accept or reject
/// an authorization request.
///
/// # Access Token Authentication
///
/// [Authentication overview](enum.Token.html)
///
/// 1. [Request Token]: Authenticate your application
/// 2. [Authorize]/ **Authenticate**: Authenticate the user
/// 3. [Access Token]: Combine the authentication
///
/// [Request Token]: fn.request_token.html
/// [Authorize]: fn.authorize_url.html
/// [Access Token]: fn.access_token.html
///
/// # Authenticate: Authenticate the user (with Sign In To Twitter)
///
/// This function is part of the step of authenticating a user with Twitter so they can authorize
/// your application to access their account. This function generates a URL with the given request
/// token that you must give to the user.
///
/// The URL returned by this function acts the same as the [Authorize] URL, with one exception: If
/// you have "[Sign In With Twitter]" enabled for your app, the user does not need to re-accept the
/// app's connection if they've accepted it previously. If they're already logged in to Twitter,
/// and have already accepted your app's access, they won't even see the redirect through Twitter.
/// Twitter will immediately redirect the user to the `callback` URL given to the [request token].
///
/// [Sign In With Twitter]: https://dev.twitter.com/web/sign-in/implementing
///
/// If the user is redirected to a callback URL, Twitter will add two query string parameters:
/// `oauth_token`, which contains the `key` from the [request token] used here, and
/// `oauth_verifier`, which contains a verifier string that can be used to create the final [access
/// token].
pub fn authenticate_url(request_token: &KeyPair) -> String {
    format!("{}?oauth_token={}", links::auth::AUTHENTICATE, request_token.key)
}

/// With the given OAuth tokens and verifier, ask Twitter for an access KeyPair that can be used to
/// sign further requests to the Twitter API.
///
/// # Access Token Authentication
///
/// [Authentication overview](enum.Token.html)
///
/// 1. [Request Token]: Authenticate your application
/// 2. [Authorize]/[Authenticate]: Authenticate the user
/// 3. **Access Token**: Combine the authentication
///
/// [Request Token]: fn.request_token.html
/// [Authorize]: fn.authorize_url.html
/// [Authenticate]: fn.authenticate_url.html
///
/// # Access Token: Combine the app and user authentication
///
/// This is the final step in authenticating a user account to use your app. With this method, you
/// combine the consumer `KeyPair` that represents your app, the [request token] that represents
/// the session, and the "verifier" that represents the user's credentials and their acceptance of
/// your app's access.
///
/// The `verifier` parameter comes from the Step 2 process you used. For PIN-Based Authorization,
/// the verifier is the PIN returned to the user after they sign in. For "Sign In With Twitter" and
/// 3-Legged Authorization, the verifier is the string passed by twitter to your app through the
/// `oauth_verifier` query string parameter. For more information, see the documentation for the
/// [Authorize] URL function.
///
/// Note that this function consumes `con_token`, because it is inserted into the `Token` that is
/// returned. If you would like to use the consumer token to authenticate multiple accounts in the
/// same session, clone the `KeyPair` when passing it into this function.
///
/// The `AuthFuture` returned by this function, on success, yields a tuple of three items: The
/// final access token, the ID of the authenticated user, and the screen name of the authenticated
/// user.
pub fn access_token<S: Into<String>>(con_token: KeyPair,
                                     request_token: &KeyPair,
                                     verifier: S,
                                     handle: &Handle)
    -> AuthFuture
{
    let header = get_header(Method::Post, links::auth::ACCESS_TOKEN,
                            &con_token, Some(request_token), None, Some(verifier.into()), None);
    let mut request = Request::new(Method::Post, links::auth::ACCESS_TOKEN.parse().unwrap());
    request.headers_mut().set(Authorization(header));

    AuthFuture {
        con_token: Some(con_token),
        loader: make_raw_future(handle, request),
    }
}

/// `Future` which yields an access token when it finishes.
///
/// See the docs for [`access_token`][] for more details.
///
/// [`access_token`]: fn.access_token.html
///
/// The `Future` implementation yields a tuple of three items upon success: The final access token,
/// the ID of the authenticated user, and the screen name of the authenticated user.
#[must_use = "futures do nothing unless polled"]
pub struct AuthFuture {
    con_token: Option<KeyPair>,
    loader: RawFuture,
}

impl Future for AuthFuture {
    type Item = (Token, u64, String);
    type Error = error::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let full_resp = match self.loader.poll() {
            Err(e) => return Err(e),
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Ok(Async::Ready(resp)) => resp,
        };

        if let Some(con_token) = self.con_token.take() {
            let mut key: Option<String> = None;
            let mut secret: Option<String> = None;
            let mut id: Option<u64> = None;
            let mut username: Option<String> = None;

            for elem in full_resp.split('&') {
                let mut kv = elem.splitn(2, '=');
                match kv.next() {
                    Some("oauth_token") => key = kv.next().map(|s| s.to_string()),
                    Some("oauth_token_secret") => secret = kv.next().map(|s| s.to_string()),
                    Some("user_id") => id = kv.next().and_then(|s| u64::from_str_radix(s, 10).ok()),
                    Some("screen_name") => username = kv.next().map(|s| s.to_string()),
                    Some(_) => (),
                    None => return Err(
                        error::Error::InvalidResponse(
                            "unexpected end of response in access_token", None)
                    ),
                }
            }

            let access_key = try!(key.ok_or(error::Error::MissingValue("oauth_token")));
            let access_secret = try!(secret.ok_or(error::Error::MissingValue("oauth_token_secret")));

            Ok(Async::Ready((Token::Access {
                    consumer: con_token,
                    access: KeyPair::new(access_key, access_secret),
                },
                try!(id.ok_or(error::Error::MissingValue("user_id"))),
                try!(username.ok_or(error::Error::MissingValue("screen_name"))))))
        } else {
            Err(error::Error::FutureAlreadyCompleted)
        }
    }
}

/// With the given consumer KeyPair, request the current Bearer token to perform Application-only
/// authentication.
///
/// If you don't need to use the Twitter API to perform actions on or with specific users, app-only
/// auth provides a much easier way to authenticate with the Twitter API. The Token given by this
/// function can be used to authenticate requests as if there were coming from your app itself.
/// This comes with an important restriction, though: any request that requires a user context -
/// direct messages, viewing protected user profiles, functions like `tweet::home_timeline` that
/// operate in terms of the authenticated user - will not work with just a Bearer token. Attempts
/// to perform those actions will return an authentication error.
///
/// Other things to note about Bearer tokens:
///
/// - Bearer tokens have a higher rate limit for the methods they can be used on, compared to
///   regular Access tokens.
/// - The bearer token returned by Twitter is the same token each time you call it. It can be
///   cached and reused as long as you need it.
/// - Since a Bearer token can be used to directly authenticate calls to Twitter, it should be
///   treated with the same sensitivity as a password. If you believe your Bearer token to be
///   compromised, call [`invalidate_bearer`] with your consumer KeyPair and the Bearer token you
///   need to invalidate.  This will cause Twitter to generate a new Bearer token for your
///   application, which will be returned the next time you call this function.
///
/// [`invalidate_bearer`]: fn.invalidate_bearer.html
///
/// For more information, see the Twitter documentation on [Application-only authentication][auth].
///
/// [auth]: https://dev.twitter.com/oauth/application-only
pub fn bearer_token(con_token: &KeyPair, handle: &Handle)
    -> TwitterFuture<Token>
{
    let content: Mime = "application/x-www-form-urlencoded;charset=UTF-8".parse().unwrap();

    let auth_header = bearer_request(con_token);
    let mut request = Request::new(Method::Post, links::auth::BEARER_TOKEN.parse().unwrap());
    request.headers_mut().set(Authorization(auth_header));
    request.headers_mut().set(ContentType(content));
    request.set_body("grant_type=client_credentials");

    fn parse_tok(full_resp: String, _: &Headers) -> Result<Token, error::Error> {
        let decoded = try!(json::Json::from_str(&full_resp));
        let result = try!(decoded.find("access_token")
                                 .and_then(|s| s.as_string())
                                 .ok_or(error::Error::MissingValue("access_token")));

        Ok(Token::Bearer(result.to_owned()))
    }

    make_future(handle, request, parse_tok)
}

/// Invalidate the given Bearer token using the given consumer KeyPair. Upon success, the future
/// returned by this function yields the Token that was just invalidated.
///
/// # Panics
///
/// If this function is handed a `Token` that is not a Bearer token, this function will panic.
pub fn invalidate_bearer(handle: &Handle, con_token: &KeyPair, token: &Token)
    -> TwitterFuture<Token>
{
    let token = if let Token::Bearer(ref token) = *token {
        token
    } else {
        panic!("non-bearer token passed to invalidate_bearer");
    };

    let content: Mime = "application/x-www-form-urlencoded;charset=UTF-8".parse().unwrap();

    let auth_header = bearer_request(con_token);
    let mut request = Request::new(Method::Post, links::auth::INVALIDATE_BEARER.parse().unwrap());
    request.headers_mut().set(Authorization(auth_header));
    request.headers_mut().set(ContentType(content));
    request.set_body(format!("access_token={}", token));

    fn parse_tok(full_resp: String, _: &Headers) -> Result<Token, error::Error> {
        let decoded = try!(json::Json::from_str(&full_resp));
        let result = try!(decoded.find("access_token")
                                 .and_then(|s| s.as_string())
                                 .ok_or(error::Error::MissingValue("access_token")));

        Ok(Token::Bearer(result.to_owned()))
    }

    make_future(handle, request, parse_tok)
}

/// If the given tokens are valid, return the user information for the authenticated user.
///
/// If you have cached access tokens, using this method is a convenient way to make sure they're
/// still valid. If the user has revoked access from your app, this function will return an error
/// from Twitter indicating that you don't have access to the user.
pub fn verify_tokens(token: &Token, handle: &Handle)
    -> FutureResponse<::user::TwitterUser>
{
    let req = get(links::auth::VERIFY_CREDENTIALS, token, None);

    make_parsed_future(handle, req)
}

#[cfg(test)]
mod tests {
    use super::bearer_request;
    use hyper::header::Authorization;

    #[test]
    fn bearer_header() {
        let con_key = "xvz1evFS4wEEPTGEFPHBog";
        let con_secret = "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg";
        let con_token = super::KeyPair::new(con_key, con_secret);

        let header = Authorization(bearer_request(&con_token));

        let output = header.to_string();

        assert_eq!(output, "Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEJvZzpMOHFxOVBaeVJnNmllS0dFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==");
    }
}