steam_auth/
lib.rs

1//! Allows you to implement a 'login with steam' feature on your website.
2//!
3//! ## Usage
4//!
5//! The easiest way to use this crate is with the `reqwest-09x` feature which allows the library to
6//! make HTTP requests on your behalf. Otherwise, you will need to do that manually.
7//!
8//! Using the `reqwest-09x` feature:
9//! ```rust
10//! # use steam_auth::{Redirector, Verifier};
11//! # fn main() {
12//! // First, create a redirector
13//! let redirector = Redirector::new("http://localhost:8080", "/callback").unwrap();
14//!
15//! // When a user wants to log in with steam, (e.g when they land on the `/login` route),
16//! // redirect them to this URL:
17//! let redirect_url = redirector.url();
18//!
19//! // Once they've finished authenticating, they will be returned to `/callback` with some data in
20//! // the query string that needs to be parsed and then verified by sending an HTTP request to the steam
21//! // servers.
22//! # let querystring = "openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.op_endpoint=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Flogin&openid.claimed_id=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Fid%2F92345666790633291&openid.identity=https%3A%2F%2Fsteamcommunity.com%2Fopenid%2Fid%2F12333456789000000&openid.return_to=http%3A%2F%2Flocalhost%3A8080%2Fcallback&openid.response_nonce=2019-06-15T00%3A36%3A00Z7nVIS5lDAcZe%2FT0gT4%2BQNQyexyA%3D&openid.assoc_handle=1234567890&openid.signed=signed%2Cop_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle&openid.sig=BK0zC%2F%2FKzERs7N%2BNlDO0aL06%2BBA%3D";
23//! match Verifier::make_verify_request(&reqwest::Client::new(), querystring) {
24//!     Ok(steam_id) => println!("Successfully logged in user with steam ID 64 {}", steam_id),
25//!     Err(e) => eprintln!("There was an error authenticating: {}", e),
26//! }
27//! # }
28//! ```
29//!
30//! There is an asynchronous variant: `Verifier::make_verify_request_async` which returns a
31//! future. You can also deserialize the data into a `SteamLoginData` struct and construct a
32//! `Verifier` from that if it is more convenient.
33//!
34//! If you don't want to depend on request, you'll need to send the HTTP request yourself. See the
35//! [example server](https://github.com/64/steam-auth/blob/master/examples/server.rs) and the
36//! `Verifier` documentation for more details on how this can be done.
37
38#[macro_use]
39extern crate serde_derive;
40#[macro_use]
41extern crate failure;
42
43mod redirector;
44mod verifier;
45
46pub use redirector::Redirector;
47pub use verifier::SteamLoginData;
48pub use verifier::Verifier;
49
50pub(crate) const STEAM_URL: &str = "https://steamcommunity.com/openid/login";
51
52#[derive(Debug, Fail)]
53pub enum Error {
54    #[fail(display = "bad site or return url: {}", _0)]
55    /// The site or return URL was incorrect
56    BadUrl(url::ParseError),
57    #[fail(display = "failed to parse SteamAuthRequest (please file bug): {}", _0)]
58    /// Internal error serializing the query string - should never happen.
59    ParseQueryString(serde_urlencoded::ser::Error),
60    #[fail(display = "authentication failed")]
61    /// The authentication failed because the data provided to the callback was invalid
62    AuthenticationFailed,
63    #[fail(display = "failed to parse steam id")]
64    /// There was an error parsing the Steam ID returned to the callback
65    ParseSteamId,
66    #[fail(display = "failed to build HTTP request or response: {}", _0)]
67    BuildHttpStruct(http::Error),
68    #[fail(display = "error serializing url encoded data: {}", _0)]
69    Serialize(serde_urlencoded::ser::Error),
70    #[fail(display = "error deserializing url encoded data: {}", _0)]
71    Deserialize(serde_urlencoded::de::Error),
72    #[fail(display = "reqwest error: {}", _0)]
73    #[cfg(feature = "reqwest-09x")]
74    /// There was an error during the verify request
75    Reqwest(reqwest::Error),
76}
77
78#[cfg(feature = "reqwest-0_9")]
79pub fn verify_response_async(
80    client: &reqwest::r#async::Client,
81    mut form: SteamAuthResponse,
82) -> impl futures::Future<Item = u64, Error = Error> {
83    client
84        .post(STEAM_URL)
85        .form(&form)
86        .send()
87        .map_err(Error::Reqwest)
88        .and_then(|res| res.into_body().concat2().map_err(Error::Reqwest))
89        .and_then(move |body| {
90            let s = std::str::from_utf8(&body)
91                .map_err(|_| Error::AuthenticationFailed)?
92                .to_owned();
93
94            parse_verify_response(&form.claimed_id, s)
95        })
96}