axess_core/principal/session_resolver.rs
1//! `SessionResolver`: extracts a [`HumanPrincipal`] from an
2//! authenticated [`AuthSession`].
3//!
4//! Lives in axess-core (not in the leaf [`axess_identity`] crate)
5//! because it depends on axess-core's session state machine. Implements
6//! the [`axess_identity::PrincipalResolver`] trait so call sites can
7//! treat `SessionResolver` and `CliResolver` uniformly through the same
8//! trait surface.
9
10use std::collections::BTreeMap;
11
12use axess_identity::{HumanPrincipal, IdentityError, Principal, PrincipalResolver};
13
14use crate::AuthSession;
15
16/// Resolver that extracts a [`HumanPrincipal`] from an authenticated
17/// [`AuthSession`]. Returns [`IdentityError::NotAuthenticated`] when
18/// the session is in any state other than
19/// [`Authenticated`](crate::AuthState::Authenticated).
20///
21/// Cheap to construct; clones the session handle (an `Arc`-backed
22/// shared value) so the resolver owns its own view. Per-request
23/// construction in an Axum handler is the expected use:
24///
25/// ```text
26/// async fn handler(session: AuthSession) -> Result<…, ApiError> {
27/// let principal = SessionResolver::new(session).resolve().await?;
28/// // …
29/// }
30/// ```
31#[derive(Clone)]
32pub struct SessionResolver {
33 session: AuthSession,
34}
35
36impl SessionResolver {
37 /// Construct a resolver over the given session.
38 pub fn new(session: AuthSession) -> Self {
39 Self { session }
40 }
41}
42
43impl PrincipalResolver for SessionResolver {
44 async fn resolve(&self) -> Result<Principal, IdentityError> {
45 let snap = self
46 .session
47 .snapshot()
48 .await
49 .ok_or(IdentityError::NotAuthenticated)?;
50 // The prior `from_bytes` conversion
51 // between two duplicate `SessionId` types here disappeared
52 // when `axess_core::session::id::SessionId` became a
53 // re-export of `axess_identity::SessionId`. The snapshot's
54 // `session_id` is now type-identical to the field on
55 // `HumanPrincipal`; direct assignment, no byte copy.
56 Ok(Principal::Human(HumanPrincipal {
57 user_id: snap.user_id,
58 tenant_id: snap.tenant_id,
59 session_id: Some(snap.session_id),
60 attributes: BTreeMap::new(),
61 }))
62 }
63}