oxide_auth/frontends/
mod.rs

1//! A base for implementing front-ends.
2//!
3//! Front-ends are glue adapters from other http server crates to the interface exposed by
4//! individual methods offered in this crate. The exact usage of the front-end varies from
5//! implementation to implementation. Composability and usability are the main concerns for
6//! front-ends, full feature support is a secondary concern.
7//!
8//! ## Usage
9//!
10//! This only adds some base functionality for front-ends. The following front-ends have been
11//! implemented in separate crates:
12//!
13//! * `simple`: Implemented here, can be reused in other web servers.
14//! * `actix`: `oxide-auth-actix`
15//! * `iron`: `oxide-auth-iron`
16//! * `rouille`: `oxide-auth-rouille`
17//! * `rocket`: `oxide-auth-rocket`
18//!
19//! ## Guide to implementing a custom front-end
20//!
21//! All front-end implementations should start with two closely related traits: [`WebRequest`] and
22//! [`WebResponse`].  These central interfaces are used to interact with the libraries supported
23//! token flows (currently only authorization code grant).
24//!
25//! Lets step through those implementations one by one.  As an example request type, let's pretend
26//! that the web interface consists of the following types:
27//!
28//! ```
29//! use oxide_auth::frontends::dev::*;
30//!
31//! struct ExampleRequest {
32//!     /// The query part of the retrieved uri, conveniently pre-parsed.
33//!     query: NormalizedParameter,
34//!
35//!     /// The value of the authorization header if any was wet.
36//!     authorization_header: Option<String>,
37//!
38//!     /// A correctly interpreted version of the body of the request, only if its content type
39//!     /// `application/x-form-urlencoded`
40//!     urlbody: Option<NormalizedParameter>,
41//! }
42//!
43//! struct ExampleResponse {
44//!     /// The http status code, 200 for OK
45//!     status: u16,
46//!
47//!     /// The Content or MIME type of the body
48//!     content_type: Option<String>,
49//!
50//!     /// The value of the `WWW-Authenticate` header if any
51//!     www_authenticate: Option<String>,
52//!
53//!     /// The value of the `Location` header if any
54//!     location: Option<String>,
55//!
56//!     /// The body sent
57//!     body: Option<String>,
58//! }
59//! ```
60//! This is obviously incredibly simplified but will showcase the most valuable features of this
61//! library. Let's implement the required traits:
62//!
63//! ```
64//! # use std::collections::HashMap;
65//! use oxide_auth::frontends::dev::*;
66//! # struct ExampleRequest {
67//! #    /// The query part of the retrieved uri, conveniently pre-parsed.
68//! #    query: NormalizedParameter,
69//! #
70//! #    /// The value of the authorization header if any was wet.
71//! #    authorization_header: Option<String>,
72//! #
73//! #    /// The body of the request, only if its content type was `application/x-form-urlencoded`
74//! #    urlbody: Option<NormalizedParameter>,
75//! # }
76//! #
77//! # struct ExampleResponse {
78//! #    /// The http status code, 200 for OK
79//! #    status: u16,
80//! #
81//! #    /// The Content or MIME type of the body
82//! #    content_type: Option<String>,
83//! #
84//! #    /// The value of the `WWW-Authenticate` header if any
85//! #    www_authenticate: Option<String>,
86//! #
87//! #    /// The value of the `Location` header if any
88//! #    location: Option<String>,
89//! #
90//! #    /// The body sent
91//! #    body: Option<String>,
92//! # }
93//!
94//! impl WebRequest for ExampleRequest {
95//!     // Declare the corresponding response type.
96//!     type Response = ExampleResponse;
97//!
98//!     // Our internal frontends error type is `OAuthError`
99//!     type Error = OAuthError;
100//!
101//!     fn query(&mut self) -> Result<Cow<QueryParameter + 'static>, OAuthError> {
102//!         Ok(Cow::Borrowed(&self.query))
103//!     }
104//!
105//!     fn urlbody(&mut self) -> Result<Cow<QueryParameter + 'static>, OAuthError> {
106//!         self.urlbody.as_ref()
107//!             .map(|body| Cow::Borrowed(body as &QueryParameter))
108//!             .ok_or(OAuthError::PrimitiveError)
109//!     }
110//!
111//!     fn authheader(&mut self) -> Result<Option<Cow<str>>, OAuthError> {
112//!         // Borrow the data if it exists, else we had no header. No error cases.
113//!         Ok(self.authorization_header.as_ref().map(|string| string.as_str().into()))
114//!     }
115//! }
116//!
117//! impl WebResponse for ExampleResponse {
118//!     // Redeclare our error type as in the request, those two must be the same.
119//!     type Error = OAuthError;
120//!
121//!     fn ok(&mut self) -> Result<(), OAuthError> {
122//!         self.status = 200;
123//!         self.www_authenticate = None;
124//!         self.location = None;
125//!         Ok(())
126//!     }
127//!
128//!     fn redirect(&mut self, target: Url) -> Result<(), OAuthError> {
129//!         self.status = 302;
130//!         self.www_authenticate = None;
131//!         self.location = Some(target.into_string());
132//!         Ok(())
133//!     }
134//!
135//!     fn client_error(&mut self) -> Result<(), OAuthError> {
136//!         self.status = 400;
137//!         self.www_authenticate = None;
138//!         self.location = None;
139//!         Ok(())
140//!     }
141//!
142//!     fn unauthorized(&mut self, www_authenticate: &str) -> Result<(), OAuthError> {
143//!         self.status = 401;
144//!         self.www_authenticate = Some(www_authenticate.to_string());
145//!         self.location = None;
146//!         Ok(())
147//!     }
148//!
149//!     fn body_text(&mut self, text: &str) -> Result<(), OAuthError> {
150//!         self.body = Some(text.to_string());
151//!         self.content_type = Some("text/plain".to_string());
152//!         Ok(())
153//!     }
154//!
155//!     fn body_json(&mut self, json: &str) -> Result<(), OAuthError> {
156//!         self.body = Some(json.to_string());
157//!         self.content_type = Some("application/json".to_string());
158//!         Ok(())
159//!     }
160//! }
161//! ```
162//!
163//! And we're done, the library is fully usable. In fact, the implementation for `simple` is
164//! almost the same as what we just did with some minor extras. All that is missing is your web
165//! servers main loop to drive the thing and a look into the
166//! [`code_grant::endpoint::{AuthorizationFlow, GrantFlow, AccessFlow}`] which will explain the usage
167//! of the above traits in the context of the Authorization Code Grant.
168//!
169//! Of course, this style might not the intended way for some server libraries. In this case, you
170//! may want to provide additional wrappers. The `actix` front-end adds utilities for abstracting
171//! futures and actor messaging, for example.
172//!
173//! [`code_grant::endpoint::{AuthorizationFlow, GrantFlow, AccessFlow}`]: ../code_grant/endpoint/index.html
174//!
175
176pub mod simple;
177
178/// Simply a prelude useful for writing front-ends.
179pub mod dev {
180    pub use std::borrow::Cow;
181    pub use url::Url;
182    pub use crate::endpoint::{Endpoint, WebRequest, WebResponse};
183    pub use crate::endpoint::{OAuthError, OwnerSolicitor, NormalizedParameter, QueryParameter};
184}