diff --git a/examples/auth-with-state.rs b/examples/auth-with-state.rs index 117e51d..95f01fe 100644 --- a/examples/auth-with-state.rs +++ b/examples/auth-with-state.rs @@ -1,16 +1,28 @@ use ratpack::prelude::*; +// We'll use authstate to (optionally) capture information about the token +// being correct. if it is Some(true), the user was authed, if None, there was no +// authentication performed. #[derive(Clone)] struct AuthedState { authed: Option, } +// All transient state structs must have an initial state, which will be +// initialized internally in the router. impl TransientState for AuthedState { fn initial() -> Self { Self { authed: None } } } +// our authtoken validator, this queries the app state and the header +// `X-AuthToken` and compares the two. If there are any discrepancies, it +// returns `401 Unauthorized`. +// +// every handler & middleware takes and returns the same params and has the +// same prototype. +// async fn validate_authtoken( req: Request, resp: Option>, @@ -18,24 +30,16 @@ async fn validate_authtoken( app: App, mut authstate: AuthedState, ) -> HTTPResult { - let token = req.headers().get("X-AuthToken"); - if token.is_none() { - return Err(Error::StatusCode(StatusCode::UNAUTHORIZED)); + if let (Some(token), Some(state)) = (req.headers().get("X-AuthToken"), app.state().await) { + authstate.authed = Some(state.clone().lock().await.authtoken == token); + Ok((req, resp, authstate)) + } else { + Err(Error::StatusCode(StatusCode::UNAUTHORIZED)) } - - let token = token.unwrap(); - - let state = app.state().await; - if state.is_none() { - return Err(Error::StatusCode(StatusCode::UNAUTHORIZED)); - } - - let state = state.unwrap(); - authstate.authed = Some(state.clone().lock().await.authtoken == token); - - return Ok((req, resp, authstate)); } +// our `hello` responder; it simply echoes the `name` parameter provided in the +// route. async fn hello( req: Request, _resp: Option>, @@ -43,25 +47,36 @@ async fn hello( _app: App, authstate: AuthedState, ) -> HTTPResult { - if authstate.authed.is_some() && !authstate.authed.unwrap() { - return Err(Error::StatusCode(StatusCode::UNAUTHORIZED)); - } - let name = params.get("name").unwrap(); let bytes = Body::from(format!("hello, {}!\n", name)); - return Ok(( - req, - Some(Response::builder().status(200).body(bytes).unwrap()), - authstate, - )); + if let Some(authed) = authstate.authed { + if authed { + return Ok(( + req, + Some(Response::builder().status(200).body(bytes).unwrap()), + authstate, + )); + } + } else if authstate.authed.is_none() { + return Ok(( + req, + Some(Response::builder().status(200).body(bytes).unwrap()), + authstate, + )); + } + + Err(Error::StatusCode(StatusCode::UNAUTHORIZED)) } +// Our global application state; must be `Clone`. #[derive(Clone)] struct State { authtoken: &'static str, } +// ServerError is a catch-all for errors returned by serving content through +// ratpack. #[tokio::main] async fn main() -> Result<(), ServerError> { let mut app = App::with_state(State {