egg_mode/
auth.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Types and methods used to authenticate calls to Twitter.
6//!
7//! # Authenticating Requests With Twitter
8//!
9//! egg-mode uses a [`Token`] type to represent successfully authenticating with Twitter. The
10//! process of obtaining a `Token` is somewhat complicated, and there are a few distinct routes you
11//! can take to get one. Which route you take depends on whether you want to only access public
12//! data versus wanting to act on behalf of a specific user, and whether you can open a web browser
13//! and/or redirect web requests to and from Twitter. Twitter's [Authentication Overview][auth] has
14//! the complete information.
15//!
16//! [`Token`]: enum.Token.html
17//! [auth]: https://developer.twitter.com/en/docs/basics/authentication/overview
18//!
19//! Regardless of which route you take, you need to [register] with Twitter's Developer site and
20//! [set up an app][apps] to get a set of keys that represent your code interacting with the
21//! Twitter service. These are called the "consumer key" and "consumer secret", and you can store
22//! them in a [`KeyPair`] to start the authentication process with egg-mode. At an HTTP request
23//! level, these keys are sent with every API call regardless of how you choose to authenticate.
24//! (There are other keys given when you register an app, but those will be mentioned below when
25//! talking about Access tokens.)
26//!
27//! [register]: https://developer.twitter.com/en/apply-for-access
28//! [apps]: https://developer.twitter.com/en/apps
29//! [`KeyPair`]: struct.KeyPair.html
30//!
31//! There are two kinds of `Tokens` used within egg-mode, representing the two major ways to
32//! interact with the Twitter API: Bearer tokens, for accessing public information on Twitter from
33//! the point of view of your app itself, and Access tokens, for performing actions or requesting
34//! data on behalf of a specific user.
35//!
36//! ## Bearer Tokens
37//!
38//! The simplest kind of `Token` you can get is called a "Bearer token".  Bearer tokens are for
39//! when you want to perform requests on behalf of your app itself, instead of a specific user.
40//! Bearer tokens are the API equivalent of viewing Twitter from a logged-out session. Anything
41//! that's already public can be viewed, but things like protected users or the home timeline can't
42//! be accessed with bearer tokens. On the other hand, because you don't need to authenticate a
43//! user, obtaining a bearer token is relatively simple.
44//!
45//! If a Bearer token will work for your purposes, use the following steps to get a Token:
46//!
47//! 1. With the consumer key and secret from Twitter, ask Twitter for the current [Bearer token]
48//!    for your application.
49//!
50//! [Bearer token]: fn.bearer_token.html
51//!
52//! And... that's it! This Bearer token can be cached and saved for future use. It will not expire
53//! until you ask Twitter to [invalidate] the token for you. Otherwise, this token can be used the
54//! same way as access tokens below, but with the restrictions mentioned earlier.
55//!
56//! [invalidate]: fn.invalidate_bearer.html
57//!
58//! ### Example (Bearer Token)
59//!
60//! ```rust,no_run
61//! # #[tokio::main]
62//! # async fn main() {
63//! let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
64//! let token = egg_mode::auth::bearer_token(&con_token).await.unwrap();
65//!
66//! // token can be given to *most* egg_mode methods that ask for a token
67//! // for restrictions, see docs for bearer_token
68//! # }
69//! ```
70//!
71//! ## Access Tokens
72//!
73//! Access tokens are for when you want to perform your requests on behalf of a specific user. This
74//! could be for something like posting to their account, sending and receiving direct messages for
75//! them, viewing protected accounts they follow, and other actions that only make sense from the
76//! perspective from a specific user. Because of the two-fold nature of making sure your requests
77//! are signed from your specific *app* and from that specific *user*, the authentication process
78//! for access tokens is relatively complicated.
79//!
80//! The process to get an access token for a specific user (with this library) has three basic
81//! steps:
82//!
83//! 1. Log your request with Twitter by getting a [request token][].
84//! 2. Direct the user to grant permission to your application by sending them to an
85//!    [authenticate][] or [authorize][] URL, depending on the nature of your app.
86//! 3. Convert the verifier given by the permission request into an [access token][].
87//!
88//! [request token]: fn.request_token.html
89//! [authorize]: fn.authorize_url.html
90//! [authenticate]: fn.authenticate_url.html
91//! [access token]: fn.access_token.html
92//!
93//! Before you get too deep into the authentication process, it helps to know a couple things about
94//! the app you're writing:
95//!
96//! * Is your app in an environment where directing users to and from a web page is easy? (e.g. a
97//!   website, or a mobile app)
98//! * Are you using Twitter authentication as a substitute for user accounts in your own
99//!   application, instead of wanting to interact with their Twitter account?
100//!
101//! Depending on your answer to the first question, you may need to use "PIN-Based Authorization",
102//! where the user completes the authentication/authorization process in a separate window and
103//! receives a numeric PIN in response that your app can use to complete the authentication
104//! process. The alternative to that is the standard OAuth flow, where a web browser is directed to
105//! Twitter to complete the login and authorization, then redirected back to your app to receive
106//! the access token proper. The way to signal one method or another is by the `callback` parameter
107//! to the [access token] request.
108//!
109//! The second question informs *where* you send the user to authorize your app. Using the "Sign In
110//! With Twitter" flow, your app could be able to transparently request another access token
111//! without the user needing to accept the connection every time. This is ideal for websites where
112//! a "Sign In With Twitter" button could replace a regular login button, using a user's Twitter
113//! account in place for regular username/password credentials. To be able to use the "Sign In With
114//! Twitter" flow, you must first enable it for your app on Twitter's Application Manager.  Then,
115//! for Step 2 of the authentication process, send the user to an [authenticate] URL. If you don't
116//! need or want to use the "Sign In With Twitter" process, send the user to an [authorize] URL
117//! instead.
118//!
119//! The primary difference between the different URLs for Step 2 is that an [authenticate] URL
120//! allows the above behavior, whereas an [authorize] URL does not require the extra setting in the
121//! app manager and always requires the user to re-authorize the app every time they're sent
122//! through the authentication process. Since access tokens can be cached and reused indefinitely
123//! until the app's access is revoked, you only really need to send the user through the
124//! authentication process once.
125//!
126//! The end result of Step 2 is that your app receives a "verifier" to vouch for the user's
127//! acceptance of your app. With PIN-Based Authorization, the user receives a PIN from Twitter that
128//! acts as the verifier. With "Sign In With Twitter" and its counterpart, "3-Legged
129//! Authorization", the verifier is given as a query parameter to the callback URL given back in
130//! Step 1. With this verifier and the original request token, you can combine them with your app's
131//! consumer token to get the [access token] that opens up the rest of the Twitter API.
132//!
133//! ### Example (Access Token)
134//!
135//! For "PIN-Based Authorization":
136//!
137//! ```rust,no_run
138//! # #[tokio::main]
139//! # async fn main() {
140//! let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
141//! // "oob" is needed for PIN-based auth; see docs for `request_token` for more info
142//! let request_token = egg_mode::auth::request_token(&con_token, "oob").await.unwrap();
143//! let auth_url = egg_mode::auth::authorize_url(&request_token);
144//!
145//! // give auth_url to the user, they can sign in to Twitter and accept your app's permissions.
146//! // they'll receive a PIN in return, they need to give this to your application
147//!
148//! let verifier = "123456"; //read the PIN from the user here
149//!
150//! // note this consumes con_token; if you want to sign in multiple accounts, clone it here
151//! let (token, user_id, screen_name) =
152//!     egg_mode::auth::access_token(con_token, &request_token, verifier).await.unwrap();
153//!
154//! // token can be given to any egg_mode method that asks for a token
155//! // user_id and screen_name refer to the user who signed in
156//! # }
157//! ```
158//!
159//! **WARNING**: The consumer token and preset access token mentioned below are as privileged as
160//! passwords! If your consumer key pair leaks or is visible to the public, anyone can impersonate
161//! your app! If you use a fixed token for your app, it's recommended to set them in separate files
162//! and use `include_str!()` (from the standard library), or save them in an environment file and
163//! use a library like `dotenv` to load them in, so you can safely exclude them from source
164//! control.
165//!
166//! ### Shortcut: Pre-Generated Access Token
167//!
168//! If you only want to sign in as yourself, there's a shortcut you can use to get an Access token.
169//! When you sign up for an app and get your consumer token, a second key/secret pair are given to
170//! you. This "access token" and "access token secret" act as authorization to access your own
171//! account with your own code, and can be used to directly construct an egg-mode `Token`:
172//!
173//! ```rust
174//! let con_token = egg_mode::KeyPair::new("consumer key", "consumer secret");
175//! let access_token = egg_mode::KeyPair::new("access token key", "access token secret");
176//! let token = egg_mode::Token::Access {
177//!     consumer: con_token,
178//!     access: access_token,
179//! };
180//!
181//! // token can be given to any egg_mode method that asks for a token
182//! ```
183//!
184//! For more information on the individual steps of the authentication process, see the
185//! documentation for the functions in this module.
186
187use std::borrow::Cow;
188
189use hyper::Method;
190use serde::{Deserialize, Serialize};
191use serde_json;
192
193use crate::common::*;
194use crate::{
195    error::{self, Result},
196    links,
197};
198
199pub(crate) mod raw;
200
201use raw::RequestBuilder;
202
203/// A key/secret pair representing the app that is sending a request or an authorization from a user.
204///
205/// This type is used as part of the authentication process and to sign API requests afterward. For
206/// the most part it's used internally as part of a [`Token`], but at the very beginning of the
207/// authentication process, you'll need to manually create one to hold onto your "consumer token"
208/// and request a [request token].
209///
210/// [`Token`]: enum.Token.html
211/// [request token]: fn.request_token.html
212///
213/// For more information, see the [authentication documentation][auth].
214///
215/// [auth]: index.html
216///
217/// # Example
218///
219/// ```rust
220/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer token");
221/// ```
222#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct KeyPair {
224    ///A key used to identify an application or user.
225    pub key: Cow<'static, str>,
226    ///A private key used to sign messages from an application or user.
227    pub secret: Cow<'static, str>,
228}
229
230impl KeyPair {
231    /// Creates a KeyPair with the given key and secret.
232    ///
233    /// This can be called with either `&'static str` (a string literal) or `String` for either
234    /// parameter.
235    pub fn new<K, S>(key: K, secret: S) -> KeyPair
236    where
237        K: Into<Cow<'static, str>>,
238        S: Into<Cow<'static, str>>,
239    {
240        KeyPair {
241            key: key.into(),
242            secret: secret.into(),
243        }
244    }
245
246    /// Internal function to create an empty KeyPair. Not meant to be used from user code.
247    fn empty() -> KeyPair {
248        KeyPair {
249            key: "".into(),
250            secret: "".into(),
251        }
252    }
253}
254
255/// A token that can be used to sign requests to Twitter.
256///
257/// Conceptually, a Token represents your authorization to call the Twitter API. It can either be a
258/// [Bearer token], representing a "logged-out" view of Twitter coming from your app itself; or an
259/// [Access token], representing a combination of your app's "consumer" key with a specific user
260/// granting access for your app to use the Twitter API on their behalf. For more information, see
261/// the [authentication documentation][auth].
262///
263/// [Bearer token]: index.html#bearer-tokens
264/// [Access token]: index.html#access-tokens
265/// [auth]: index.html
266///
267/// Once you have obtained a Token of either kind, the keys within may be saved and reused in the
268/// future, as long as the access has not been revoked. **Note** that the keys saved in this type
269/// work just like a password, and they should be handled with care when you save them! If you
270/// believe your keys have been compromised, you can generate a new consumer token in [Twitter's
271/// Apps Dashboard][apps], and if you've been using a Bearer token, you should [invalidate] it and
272/// generate a new one.
273///
274/// [apps]: https://developer.twitter.com/en/apps
275/// [invalidate]: fn.invalidate_bearer.html
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub enum Token {
278    /// An OAuth Access token indicating the request is coming from a specific user.
279    Access {
280        /// A "consumer" key/secret that represents the application sending the request.
281        consumer: KeyPair,
282        /// An "access" key/secret that represents the user's authorization of the application.
283        access: KeyPair,
284    },
285    /// An OAuth Bearer token indicating the request is coming from the application itself, not a
286    /// particular user.
287    Bearer(String),
288}
289
290/// With the given consumer KeyPair, ask Twitter for a request KeyPair that can be used to request
291/// access to the user's account.
292///
293/// # Access Token Authentication
294///
295/// [Authentication overview](index.html)
296///
297/// 1. **Request Token**: Authenticate your application
298/// 2. [Authorize]/[Authenticate]: Authenticate the user
299/// 3. [Access Token]: Confirm the authentication with Twitter
300///
301/// [Authorize]: fn.authorize_url.html
302/// [Authenticate]: fn.authenticate_url.html
303/// [Access Token]: fn.access_token.html
304///
305/// # Request Token: Authenticate your application
306///
307/// To begin the authentication process, first log your request with Twitter by authenticating your
308/// application by itself. This "request token" is used in later steps to match all the requests to
309/// the same authentication attempt.
310///
311/// The parameter `callback` is used differently based on how your program is set up, and which
312/// authentication process you'd like to use. For applications where directing users to and from
313/// another web page is difficult, you can use the special value `"oob"` to indicate that you would
314/// like to use PIN-Based Authentication.
315///
316/// Web-based applications and those that can handle web redirects transparently can instead supply
317/// a callback URL for that parameter. When the user completes the sign-in and authentication
318/// process, they will be directed to the provided URL with the information necessary to complete
319/// the authentication process. Depending on which Step 2 URL you use and whether you've enabled it
320/// for your app, this is called "Sign In With Twitter" or "3-Legged Authorization".
321///
322/// With this Request Token, you can assemble an [Authorize] or [Authenticate] URL that will allow
323/// the user to log in with Twitter and allow your app access to their account. See the
324/// Authentication Overview for more details, but the short version is that you want to use
325/// [Authenticate] for "Sign In With Twitter" functionality, and [Authorize] if not.
326///
327/// # Examples
328///
329/// ```rust,no_run
330/// # #[tokio::main]
331/// # async fn main() {
332/// let con_token = egg_mode::KeyPair::new("consumer key", "consumer token");
333/// // for PIN-Based Auth
334/// let req_token = egg_mode::auth::request_token(&con_token, "oob").await.unwrap();
335/// // for Sign In With Twitter/3-Legged Auth
336/// let req_token = egg_mode::auth::request_token(&con_token, "https://myapp.io/auth")
337///     .await
338///     .unwrap();
339/// # }
340/// ```
341pub async fn request_token<S: Into<String>>(con_token: &KeyPair, callback: S) -> Result<KeyPair> {
342    let request = RequestBuilder::new(Method::POST, links::auth::REQUEST_TOKEN)
343        .oauth_callback(callback.into())
344        .request_keys(con_token, None);
345
346    let (_, body) = raw_request(request).await?;
347
348    let body = std::str::from_utf8(&body).map_err(|_| {
349        std::io::Error::new(
350            std::io::ErrorKind::InvalidData,
351            "stream did not contain valid UTF-8",
352        )
353    })?;
354    let mut key: Option<String> = None;
355    let mut secret: Option<String> = None;
356
357    for elem in body.split('&') {
358        let mut kv = elem.splitn(2, '=');
359        match kv.next() {
360            Some("oauth_token") => key = kv.next().map(|s| s.to_string()),
361            Some("oauth_token_secret") => secret = kv.next().map(|s| s.to_string()),
362            Some(_) => (),
363            None => {
364                return Err(error::Error::InvalidResponse(
365                    "unexpected end of request_token response",
366                    None,
367                ))
368            }
369        }
370    }
371
372    Ok(KeyPair::new(
373        key.ok_or(error::Error::MissingValue("oauth_token"))?,
374        secret.ok_or(error::Error::MissingValue("oauth_token_secret"))?,
375    ))
376}
377
378/// With the given request KeyPair, return a URL that a user can access to accept or reject an
379/// authorization request.
380///
381/// # Access Token Authentication
382///
383/// [Authentication overview](index.html)
384///
385/// 1. [Request Token]: Authenticate your application
386/// 2. **Authorize**/[Authenticate]: Authenticate the user
387/// 3. [Access Token]: Confirm the authentication with Twitter
388///
389/// [Request Token]: fn.request_token.html
390/// [Authenticate]: fn.authenticate_url.html
391/// [Access Token]: fn.access_token.html
392///
393/// # Authorize: Authenticate the user
394///
395/// This function is part of the step of authenticating a user with Twitter so they can authorize
396/// your application to access their account. This function generates a URL with the given request
397/// token that you must give to the user. What happens with this URL depends on what you used as
398/// the `callback` parameter for `request_token`.
399///
400/// If you gave a callback URL to `request_token`, Twitter will redirect the user to that URL after
401/// they log in and accept your app's permissions. There will be two query string parameters added
402/// to the URL for this redirect: `oauth_token`, which contains the `key` from the [request token]
403/// used here, and `oauth_verifier`, which contains a verifier string that can be used to create
404/// the final [access token]. Note that if this URL is used instead of [Authenticate], the user
405/// will need to accept the app's connection each time, even if they have connected the app
406/// previously and have not revoked the app's permissions. This process is called [3-legged
407/// authorization]. If you would like the user to transparently be redirected without confirmation
408/// if they've already accepted the connection, see the docs for [Authenticate] to read about "Sign
409/// In With Twitter".
410///
411/// [3-legged authorization]: https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/obtaining-user-access-tokens
412///
413/// If you gave the special value `"oob"` to `request_token`, this URL can be directly shown to the
414/// user, who can enter it into a separate web browser to complete the authorization. This is
415/// called [PIN-based authorization] and it's required for applications that cannot be reached by
416/// redirecting a URL from a web browser. When the user loads this URL, they can sign in with
417/// Twitter and grant your app access to their account. If they grant this access, they are given a
418/// numeric PIN that your app can use as the "verifier" to create the final [access token].
419///
420/// [Pin-Based authorization]: https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a/pin-based-oauth
421pub fn authorize_url(request_token: &KeyPair) -> String {
422    format!(
423        "{}?oauth_token={}",
424        links::auth::AUTHORIZE,
425        request_token.key
426    )
427}
428
429/// With the given request KeyPair, return a URL to redirect a user to so they can accept or reject
430/// an authorization request.
431///
432/// # Access Token Authentication
433///
434/// [Authentication overview](index.html)
435///
436/// 1. [Request Token]: Authenticate your application
437/// 2. [Authorize]/ **Authenticate**: Authenticate the user
438/// 3. [Access Token]: Confirm the authentication with Twitter
439///
440/// [Request Token]: fn.request_token.html
441/// [Authorize]: fn.authorize_url.html
442/// [Access Token]: fn.access_token.html
443///
444/// # Authenticate: Authenticate the user (with Sign In To Twitter)
445///
446/// This function is part of the step of authenticating a user with Twitter so they can authorize
447/// your application to access their account. This function generates a URL with the given request
448/// token that you must give to the user.
449///
450/// The URL returned by this function acts the same as the [Authorize] URL, with one exception: If
451/// you have "[Sign In With Twitter]" enabled for your app, the user does not need to re-accept the
452/// app's connection if they've accepted it previously. If they're already logged in to Twitter,
453/// and have already accepted your app's access, they won't even see the redirect through Twitter.
454/// Twitter will immediately redirect the user to the `callback` URL given to the [request token].
455///
456/// [Sign In With Twitter]: https://developer.twitter.com/en/docs/basics/authentication/guides/log-in-with-twitter
457///
458/// If the user is redirected to a callback URL, Twitter will add two query string parameters:
459/// `oauth_token`, which contains the `key` from the [request token] used here, and
460/// `oauth_verifier`, which contains a verifier string that can be used to create the final [access
461/// token].
462pub fn authenticate_url(request_token: &KeyPair) -> String {
463    format!(
464        "{}?oauth_token={}",
465        links::auth::AUTHENTICATE,
466        request_token.key
467    )
468}
469
470/// With the given OAuth tokens and verifier, ask Twitter for an access KeyPair that can be used to
471/// sign further requests to the Twitter API.
472///
473/// # Access Token Authentication
474///
475/// [Authentication overview](index.html)
476///
477/// 1. [Request Token]: Authenticate your application
478/// 2. [Authorize]/[Authenticate]: Authenticate the user
479/// 3. **Access Token**: Confirm the authentication with Twitter
480///
481/// [Request Token]: fn.request_token.html
482/// [Authorize]: fn.authorize_url.html
483/// [Authenticate]: fn.authenticate_url.html
484///
485/// # Access Token: Confirm the authentication with Twitter
486///
487/// This is the final step in authenticating a user account to use your app. With this method, you
488/// combine the consumer `KeyPair` that represents your app, the [request token] that represents
489/// the session, and the "verifier" that represents the user's credentials and their acceptance of
490/// your app's access.
491///
492/// The `verifier` parameter comes from the Step 2 process you used. For PIN-Based Authorization,
493/// the verifier is the PIN returned to the user after they sign in. For "Sign In With Twitter" and
494/// 3-Legged Authorization, the verifier is the string passed by twitter to your app through the
495/// `oauth_verifier` query string parameter. For more information, see the documentation for the
496/// [Authorize] URL function.
497///
498/// Note that this function consumes `con_token`, because it is inserted into the `Token` that is
499/// returned. If you would like to use the consumer token to authenticate multiple accounts in the
500/// same session, clone the `KeyPair` when passing it into this function.
501///
502/// The `Future` returned by this function, on success, yields a tuple of three items: The
503/// final access token, the ID of the authenticated user, and the screen name of the authenticated
504/// user.
505pub async fn access_token<S: Into<String>>(
506    con_token: KeyPair,
507    request_token: &KeyPair,
508    verifier: S,
509) -> Result<(Token, u64, String)> {
510    let request = RequestBuilder::new(Method::POST, links::auth::ACCESS_TOKEN)
511        .oauth_verifier(verifier.into())
512        .request_keys(&con_token, Some(request_token));
513
514    let (_headers, urlencoded) = raw_request(request).await?;
515    let urlencoded = std::str::from_utf8(&urlencoded).map_err(|_| {
516        std::io::Error::new(
517            std::io::ErrorKind::InvalidData,
518            "stream did not contain valid UTF-8",
519        )
520    })?;
521
522    // TODO deserialize into a struct
523    let mut key: Option<String> = None;
524    let mut secret: Option<String> = None;
525    let mut id: Option<u64> = None;
526    let mut username: Option<String> = None;
527
528    for elem in urlencoded.split('&') {
529        let mut kv = elem.splitn(2, '=');
530        match kv.next() {
531            Some("oauth_token") => key = kv.next().map(|s| s.to_string()),
532            Some("oauth_token_secret") => secret = kv.next().map(|s| s.to_string()),
533            Some("user_id") => id = kv.next().and_then(|s| s.parse::<u64>().ok()),
534            Some("screen_name") => username = kv.next().map(|s| s.to_string()),
535            Some(_) => (),
536            None => {
537                return Err(error::Error::InvalidResponse(
538                    "unexpected end of response in access_token",
539                    None,
540                ))
541            }
542        }
543    }
544
545    let access_key = key.ok_or(error::Error::MissingValue("oauth_token"))?;
546    let access_secret = secret.ok_or(error::Error::MissingValue("oauth_token_secret"))?;
547
548    Ok((
549        Token::Access {
550            consumer: con_token,
551            access: KeyPair::new(access_key, access_secret),
552        },
553        id.ok_or(error::Error::MissingValue("user_id"))?,
554        username.ok_or(error::Error::MissingValue("screen_name"))?,
555    ))
556}
557
558/// With the given consumer KeyPair, request the current Bearer token to perform Application-only
559/// authentication.
560///
561/// If you don't need to use the Twitter API to perform actions on or with specific users, app-only
562/// auth provides a much easier way to authenticate with the Twitter API. The Token given by this
563/// function can be used to authenticate requests as if there were coming from your app itself.
564/// This comes with an important restriction, though: any request that requires a user context -
565/// direct messages, viewing protected user profiles, functions like `tweet::home_timeline` that
566/// operate in terms of the authenticated user - will not work with just a Bearer token. Attempts
567/// to perform those actions will return an authentication error.
568///
569/// Other things to note about Bearer tokens:
570///
571/// - Bearer tokens have a higher rate limit for the methods they can be used on, compared to
572///   regular Access tokens.
573/// - The bearer token returned by Twitter is the same token each time you call it. It can be
574///   cached and reused as long as you need it.
575/// - Since a Bearer token can be used to directly authenticate calls to Twitter, it should be
576///   treated with the same sensitivity as a password. If you believe your Bearer token to be
577///   compromised, call [`invalidate_bearer`] with your consumer KeyPair and the Bearer token you
578///   need to invalidate.  This will cause Twitter to generate a new Bearer token for your
579///   application, which will be returned the next time you call this function.
580///
581/// [`invalidate_bearer`]: fn.invalidate_bearer.html
582///
583/// For more information, see the Twitter documentation on [Application-only authentication][auth].
584///
585/// [auth]: https://developer.twitter.com/en/docs/basics/authentication/oauth-2-0/application-only
586pub async fn bearer_token(con_token: &KeyPair) -> Result<Token> {
587    let content = "application/x-www-form-urlencoded;charset=UTF-8";
588
589    let request = RequestBuilder::new(Method::POST, links::auth::BEARER_TOKEN)
590        .with_body("grant_type=client_credentials", content)
591        .request_consumer_bearer(con_token);
592
593    let decoded = request_with_json_response::<serde_json::Value>(request).await?;
594    let result = decoded
595        .get("access_token")
596        .and_then(|s| s.as_str())
597        .ok_or(error::Error::MissingValue("access_token"))?;
598
599    Ok(Token::Bearer(result.to_owned()))
600}
601
602/// Invalidate the given Bearer token using the given consumer KeyPair. Upon success, the future
603/// returned by this function yields the Token that was just invalidated.
604///
605/// For more information about Bearer tokens, see the [authentication overview][auth] and the
606/// documentation for the [`bearer_token`] function.
607///
608/// [auth]: index.html#bearer-tokens
609/// [`bearer_token`]: fn.bearer_token.html
610///
611/// # Panics
612///
613/// If this function is handed a `Token` that is not a Bearer token, this function will panic.
614pub async fn invalidate_bearer(con_token: &KeyPair, token: &Token) -> Result<Token> {
615    let token = if let Token::Bearer(ref token) = *token {
616        token
617    } else {
618        panic!("non-bearer token passed to invalidate_bearer");
619    };
620
621    let content = "application/x-www-form-urlencoded;charset=UTF-8";
622
623    let request = RequestBuilder::new(Method::POST, links::auth::INVALIDATE_BEARER)
624        .with_body(format!("access_token={}", token), content)
625        .request_consumer_bearer(con_token);
626
627    let decoded = request_with_json_response::<serde_json::Value>(request).await?;
628    let result = decoded
629        .get("access_token")
630        .and_then(|s| s.as_str())
631        .ok_or(error::Error::MissingValue("access_token"))?;
632
633    Ok(Token::Bearer(result.to_owned()))
634}
635
636/// If the given tokens are valid, return the user information for the authenticated user.
637///
638/// If you have cached access tokens, using this method is a convenient way to make sure they're
639/// still valid. If the user has revoked access from your app, this function will return an error
640/// from Twitter indicating that you don't have access to the user.
641pub async fn verify_tokens(token: &Token) -> Result<Response<crate::user::TwitterUser>> {
642    let req = get(links::auth::VERIFY_CREDENTIALS, token, None);
643    request_with_json_response(req).await
644}