feat: improve authentication flow
authentication now works either with sessionid or basic auth, with basic auth not creating a session
This commit is contained in:
parent
2f974fd1ff
commit
648921837b
6 changed files with 69 additions and 39 deletions
|
|
@ -44,44 +44,57 @@ pub fn router(ctx: Context) -> Router<Context> {
|
|||
|
||||
/// This middleware accepts
|
||||
pub async fn auth_middleware(State(ctx): State<Context>, mut req: Request, next: Next) -> Response {
|
||||
// SAFETY: this extractor's error type is Infallible
|
||||
let jar: CookieJar = req.extract_parts().await.unwrap();
|
||||
let mut auth_user = None;
|
||||
let mut new_session_id = None;
|
||||
tracing::debug!("{:?}", req.headers());
|
||||
|
||||
// SAFETY: this extractor's error type is Infallible
|
||||
let mut jar: CookieJar = req.extract_parts().await.unwrap();
|
||||
let mut auth_user = None;
|
||||
|
||||
// First try to validate the session
|
||||
if let Some(session_id) = jar
|
||||
.get(SESSION_ID_COOKIE)
|
||||
.and_then(|c| c.value().parse::<i64>().ok())
|
||||
{
|
||||
match tokio::task::spawn_blocking(move || ctx.repo.validate_session(session_id))
|
||||
let ctx_clone = ctx.clone();
|
||||
match tokio::task::spawn_blocking(move || ctx_clone.repo.validate_session(session_id))
|
||||
.await
|
||||
.unwrap()
|
||||
.map_err(AppError::from)
|
||||
{
|
||||
Ok(user) => {
|
||||
auth_user = Some(user);
|
||||
}
|
||||
Err(gpodder::AuthErr::UnknownSession) => {
|
||||
jar = jar.add(
|
||||
Cookie::build((SESSION_ID_COOKIE, String::new()))
|
||||
.max_age(cookie::time::Duration::ZERO),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
return err.into_response();
|
||||
return AppError::from(err).into_response();
|
||||
}
|
||||
};
|
||||
} else if let Ok(auth) = req
|
||||
.extract_parts::<TypedHeader<Authorization<Basic>>>()
|
||||
.await
|
||||
{
|
||||
match tokio::task::spawn_blocking(move || {
|
||||
ctx.repo.create_session(auth.username(), auth.password())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.map_err(AppError::from)
|
||||
}
|
||||
|
||||
// Only if the sessionid wasn't present or valid do we check the credentials.
|
||||
if auth_user.is_none() {
|
||||
if let Ok(auth) = req
|
||||
.extract_parts::<TypedHeader<Authorization<Basic>>>()
|
||||
.await
|
||||
{
|
||||
Ok((session_id, user)) => {
|
||||
auth_user = Some(user);
|
||||
new_session_id = Some(session_id);
|
||||
}
|
||||
Err(err) => {
|
||||
return err.into_response();
|
||||
match tokio::task::spawn_blocking(move || {
|
||||
ctx.repo
|
||||
.validate_credentials(auth.username(), auth.password())
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.map_err(AppError::from)
|
||||
{
|
||||
Ok(user) => {
|
||||
auth_user = Some(user);
|
||||
}
|
||||
Err(err) => {
|
||||
return err.into_response();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -89,22 +102,9 @@ pub async fn auth_middleware(State(ctx): State<Context>, mut req: Request, next:
|
|||
if let Some(user) = auth_user {
|
||||
req.extensions_mut().insert(user);
|
||||
|
||||
let res = next.run(req).await;
|
||||
|
||||
if let Some(session_id) = new_session_id {
|
||||
(
|
||||
jar.add(
|
||||
Cookie::build((SESSION_ID_COOKIE, session_id.to_string()))
|
||||
.expires(Expiration::Session),
|
||||
),
|
||||
res,
|
||||
)
|
||||
.into_response()
|
||||
} else {
|
||||
res
|
||||
}
|
||||
(jar, next.run(req).await).into_response()
|
||||
} else {
|
||||
let mut res = StatusCode::UNAUTHORIZED.into_response();
|
||||
let mut res = (jar, StatusCode::UNAUTHORIZED).into_response();
|
||||
|
||||
// This is what the gpodder.net service returns, and some clients seem to depend on it
|
||||
res.headers_mut().insert(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct SubscriptionDelta {
|
||||
pub add: Vec<String>,
|
||||
pub remove: Vec<String>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue