pub struct Sessions<St, S = Uuid> { /* private fields */ }axum only.Expand description
Login, logout and step-up for handlers: the guard, the request’s ClientContext and the
session cookie it carries, in one extractor.
It takes the guard from the router state (anything with FromRef for
Guard<St, S>, including the guard itself) and needs
ConnectInfo<SocketAddr> like the layer; without it the request fails closed with
MissingConnectInfo. It does not authenticate anything, so put step-up routes outside the
GuardLayer: the layer would answer step_up_required before the handler runs.
Every result converts into a response: NewSession and IssuedToken set the cookie,
LoggedOut deletes it, and GuardError and StepUpError become the matching error.
Verifying credentials, and rate limiting the routes that do, stays with the app.
use std::net::SocketAddr;
use axum::{Router, http::StatusCode, routing::post};
use session_guard::{
Guard, GuardError, IssuedToken, MemoryStore, NewSession,
axum::{LoggedOut, Sessions, StepUpError},
};
use uuid::Uuid;
// Stands in for the app's own credential check.
async fn verify() -> Uuid {
Uuid::nil()
}
async fn login(sessions: Sessions<MemoryStore>) -> Result<(NewSession, StatusCode), GuardError> {
let user = verify().await;
Ok((sessions.login(&user).await?, StatusCode::NO_CONTENT))
}
async fn step_up(sessions: Sessions<MemoryStore>) -> Result<(IssuedToken, StatusCode), StepUpError> {
let user = verify().await;
Ok((sessions.complete_step_up(&user).await?, StatusCode::NO_CONTENT))
}
async fn logout(sessions: Sessions<MemoryStore>) -> Result<(LoggedOut, StatusCode), GuardError> {
Ok((sessions.logout().await?, StatusCode::NO_CONTENT))
}
let guard = Guard::new(MemoryStore::new());
let app = Router::new()
.route("/login", post(login))
.route("/step-up", post(step_up))
.route("/logout", post(logout))
.with_state(guard);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).awaitImplementations§
Source§impl<St: SessionStore, S: Subject> Sessions<St, S>
impl<St: SessionStore, S: Subject> Sessions<St, S>
Sourcepub async fn login(&self, subject: &S) -> Result<NewSession, GuardError>
pub async fn login(&self, subject: &S) -> Result<NewSession, GuardError>
Guard::login for subject, revoking the session the request presented, if any.
Call it only after verifying the subject’s credentials.
§Errors
As Guard::login.
Sourcepub async fn logout(&self) -> Result<LoggedOut, GuardError>
pub async fn logout(&self) -> Result<LoggedOut, GuardError>
Guard::logout for the presented session. The result deletes the cookie even when
there was no session to revoke.
§Errors
As Guard::logout.
Sourcepub async fn complete_step_up(
&self,
verified_subject: &S,
) -> Result<IssuedToken, StepUpError>
pub async fn complete_step_up( &self, verified_subject: &S, ) -> Result<IssuedToken, StepUpError>
Guard::complete_step_up for the presented session, once the app has verified
verified_subject’s credentials again.
§Errors
A StepUpError: the subject does not own the session, there is no live session
(including no cookie at all), or the guard failed.
Sourcepub fn guard(&self) -> &Guard<St, S>
pub fn guard(&self) -> &Guard<St, S>
The guard, for anything else (listing or revoking sessions).
Sourcepub fn client(&self) -> &ClientContext
pub fn client(&self) -> &ClientContext
The request’s client context.
Trait Implementations§
Source§impl<T, St, S> FromRequestParts<T> for Sessions<St, S>
impl<T, St, S> FromRequestParts<T> for Sessions<St, S>
Source§type Rejection = MissingConnectInfo
type Rejection = MissingConnectInfo
Source§impl<St, S> OperationInput for Sessions<St, S>
Available on crate feature aide only.
impl<St, S> OperationInput for Sessions<St, S>
aide only.Source§fn operation_input(ctx: &mut GenContext, operation: &mut Operation)
fn operation_input(ctx: &mut GenContext, operation: &mut Operation)
Source§fn inferred_early_responses(
ctx: &mut GenContext,
operation: &mut Operation,
) -> Vec<(Option<u16>, Response)>
fn inferred_early_responses( ctx: &mut GenContext, operation: &mut Operation, ) -> Vec<(Option<u16>, Response)>
Auto Trait Implementations§
impl<St, S = Uuid> !RefUnwindSafe for Sessions<St, S>
impl<St, S = Uuid> !UnwindSafe for Sessions<St, S>
impl<St, S> Freeze for Sessions<St, S>
impl<St, S> Send for Sessions<St, S>
impl<St, S> Sync for Sessions<St, S>
impl<St, S> Unpin for Sessions<St, S>
impl<St, S> UnsafeUnpin for Sessions<St, S>where
Guard<St, S>: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<S, T> FromRequest<S, ViaParts> for T
impl<S, T> FromRequest<S, ViaParts> for T
Source§type Rejection = <T as FromRequestParts<S>>::Rejection
type Rejection = <T as FromRequestParts<S>>::Rejection
Source§fn from_request(
req: Request<Body>,
state: &S,
) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>
fn from_request( req: Request<Body>, state: &S, ) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more