oxide_auth_async/endpoint/
access_token.rs1use std::str::from_utf8;
2use std::{borrow::Cow, marker::PhantomData};
3
4use base64::Engine;
5use base64::engine::general_purpose::STANDARD;
6use oxide_auth::{
7 endpoint::{QueryParameter, WebRequest, OAuthError, WebResponse, Template, NormalizedParameter},
8 code_grant::{
9 accesstoken::{
10 Error as TokenError, Request as TokenRequest, Authorization as TokenAuthorization,
11 },
12 },
13};
14
15use super::Endpoint;
16use crate::{
17 code_grant::access_token::{Extension, Endpoint as TokenEndpoint, access_token},
18 primitives::{Issuer, Registrar, Authorizer},
19};
20
21pub struct AccessTokenFlow<E, R>
34where
35 E: Endpoint<R>,
36 R: WebRequest,
37{
38 endpoint: WrappedToken<E, R>,
39 allow_credentials_in_body: bool,
40}
41
42struct WrappedToken<E, R>
43where
44 E: Endpoint<R>,
45 R: WebRequest,
46{
47 inner: E,
48 extension_fallback: (),
49 r_type: PhantomData<R>,
50}
51
52#[derive(Clone)]
53pub struct WrappedRequest<R: WebRequest> {
54 body: NormalizedParameter,
56
57 authorization: Option<Authorization>,
59
60 error: Option<FailParse<R::Error>>,
62
63 allow_credentials_in_body: bool,
65}
66
67#[derive(Debug)]
68struct Invalid;
69
70#[derive(Clone)]
71enum FailParse<E> {
72 Invalid,
73 Err(E),
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
77struct Authorization(String, Option<Vec<u8>>);
78
79impl<E, R> AccessTokenFlow<E, R>
80where
81 E: Endpoint<R> + Send + Sync,
82 R: WebRequest + Send + Sync,
83 <R as WebRequest>::Error: Send + Sync,
84{
85 pub fn prepare(mut endpoint: E) -> Result<Self, E::Error> {
96 if endpoint.registrar().is_none() {
97 return Err(endpoint.error(OAuthError::PrimitiveError));
98 }
99
100 if endpoint.authorizer_mut().is_none() {
101 return Err(endpoint.error(OAuthError::PrimitiveError));
102 }
103
104 if endpoint.issuer_mut().is_none() {
105 return Err(endpoint.error(OAuthError::PrimitiveError));
106 }
107
108 Ok(AccessTokenFlow {
109 endpoint: WrappedToken {
110 inner: endpoint,
111 extension_fallback: (),
112 r_type: PhantomData,
113 },
114 allow_credentials_in_body: false,
115 })
116 }
117
118 pub fn allow_credentials_in_body(&mut self, allow: bool) {
126 self.allow_credentials_in_body = allow;
127 }
128
129 pub async fn execute(&mut self, mut request: R) -> Result<R::Response, E::Error> {
136 let issued = access_token(
137 &mut self.endpoint,
138 &WrappedRequest::new(&mut request, self.allow_credentials_in_body),
139 )
140 .await;
141
142 let token = match issued {
143 Err(error) => return token_error(&mut self.endpoint.inner, &mut request, error),
144 Ok(token) => token,
145 };
146
147 let mut response = self.endpoint.inner.response(&mut request, Template::new_ok())?;
148 response
149 .body_json(&token.to_json())
150 .map_err(|err| self.endpoint.inner.web_error(err))?;
151 Ok(response)
152 }
153}
154
155fn token_error<E, R>(
156 endpoint: &mut E, request: &mut R, error: TokenError,
157) -> Result<R::Response, E::Error>
158where
159 E: Endpoint<R>,
160 R: WebRequest,
161{
162 Ok(match error {
163 TokenError::Invalid(mut json) => {
164 let mut response =
165 endpoint.response(request, Template::new_bad(Some(json.description())))?;
166 response.client_error().map_err(|err| endpoint.web_error(err))?;
167 response
168 .body_json(&json.to_json())
169 .map_err(|err| endpoint.web_error(err))?;
170 response
171 }
172 TokenError::Unauthorized(mut json, scheme) => {
173 let mut response = endpoint.response(
174 request,
175 Template::new_unauthorized(None, Some(json.description())),
176 )?;
177 response
178 .unauthorized(&scheme)
179 .map_err(|err| endpoint.web_error(err))?;
180 response
181 .body_json(&json.to_json())
182 .map_err(|err| endpoint.web_error(err))?;
183 response
184 }
185 TokenError::Primitive(_) => {
186 return Err(endpoint.error(OAuthError::PrimitiveError));
188 }
189 })
190}
191
192impl<E, R> TokenEndpoint for WrappedToken<E, R>
193where
194 E: Endpoint<R>,
195 R: WebRequest,
196{
197 fn registrar(&self) -> &(dyn Registrar + Sync) {
198 self.inner.registrar().unwrap()
199 }
200
201 fn authorizer(&mut self) -> &mut (dyn Authorizer + Send) {
202 self.inner.authorizer_mut().unwrap()
203 }
204
205 fn issuer(&mut self) -> &mut (dyn Issuer + Send) {
206 self.inner.issuer_mut().unwrap()
207 }
208
209 fn extension(&mut self) -> &mut (dyn Extension + Send) {
210 self.inner
211 .extension()
212 .and_then(super::Extension::access_token)
213 .unwrap_or(&mut self.extension_fallback)
214 }
215}
216
217impl<R: WebRequest> WrappedRequest<R> {
218 pub fn new(request: &mut R, credentials: bool) -> Self {
219 Self::new_or_fail(request, credentials).unwrap_or_else(Self::from_err)
220 }
221
222 fn new_or_fail(request: &mut R, credentials: bool) -> Result<Self, FailParse<R::Error>> {
223 let authorization = match request.authheader() {
225 Err(err) => return Err(FailParse::Err(err)),
226 Ok(Some(header)) => Self::parse_header(header).map(Some)?,
227 Ok(None) => None,
228 };
229
230 Ok(WrappedRequest {
231 body: request.urlbody().map_err(FailParse::Err)?.into_owned(),
232 authorization,
233 error: None,
234 allow_credentials_in_body: credentials,
235 })
236 }
237
238 fn from_err(err: FailParse<R::Error>) -> Self {
239 WrappedRequest {
240 body: Default::default(),
241 authorization: None,
242 error: Some(err),
243 allow_credentials_in_body: false,
244 }
245 }
246
247 fn parse_header(header: Cow<str>) -> Result<Authorization, Invalid> {
248 let authorization = {
249 if !header.starts_with("Basic ") {
250 return Err(Invalid);
251 }
252
253 let combined = match STANDARD.decode(&header[6..]) {
254 Err(_) => return Err(Invalid),
255 Ok(vec) => vec,
256 };
257
258 let mut split = combined.splitn(2, |&c| c == b':');
259 let client_bin = match split.next() {
260 None => return Err(Invalid),
261 Some(client) => client,
262 };
263 let passwd = match split.next() {
264 None => return Err(Invalid),
265 Some([]) => None,
266 Some(passwd64) => Some(passwd64),
267 };
268
269 let client = match from_utf8(client_bin) {
270 Err(_) => return Err(Invalid),
271 Ok(client) => client,
272 };
273
274 Authorization(client.to_string(), passwd.map(|passwd| passwd.to_vec()))
275 };
276
277 Ok(authorization)
278 }
279}
280
281impl<R: WebRequest> TokenRequest for WrappedRequest<R> {
282 fn valid(&self) -> bool {
283 self.error.is_none()
284 }
285
286 fn code(&self) -> Option<Cow<str>> {
287 self.body.unique_value("code")
288 }
289
290 fn authorization(&self) -> TokenAuthorization {
291 match &self.authorization {
292 None => TokenAuthorization::None,
293 Some(Authorization(username, None)) => TokenAuthorization::Username(username.into()),
294 Some(Authorization(username, Some(password))) => {
295 TokenAuthorization::UsernamePassword(username.into(), password.into())
296 }
297 }
298 }
299
300 fn client_id(&self) -> Option<Cow<str>> {
301 self.body.unique_value("client_id")
302 }
303
304 fn redirect_uri(&self) -> Option<Cow<str>> {
305 self.body.unique_value("redirect_uri")
306 }
307
308 fn grant_type(&self) -> Option<Cow<str>> {
309 self.body.unique_value("grant_type")
310 }
311
312 fn extension(&self, key: &str) -> Option<Cow<str>> {
313 self.body.unique_value(key)
314 }
315
316 fn allow_credentials_in_body(&self) -> bool {
317 self.allow_credentials_in_body
318 }
319}
320
321impl<E> From<Invalid> for FailParse<E> {
322 fn from(_: Invalid) -> Self {
323 FailParse::Invalid
324 }
325}
326
327#[cfg(test)]
328mod test {
329 use oxide_auth::frontends::simple::request::Request;
330 use super::*;
331
332 #[test]
333 fn test_client_id_only() {
334 let result = WrappedRequest::<Request>::parse_header("Basic Zm9vOg==".into());
335 assert!(result.is_ok());
336 let result = result.unwrap();
337 assert_eq!(result, Authorization("foo".into(), None));
338 }
339
340 #[test]
341 fn test_client_id_and_secret() {
342 let result = WrappedRequest::<Request>::parse_header("Basic Zm9vOmJhcg==".into());
343 assert!(result.is_ok());
344 let result = result.unwrap();
345 assert_eq!(result, Authorization("foo".into(), Some("bar".into())));
346 }
347}