login & logout
This commit is contained in:
@@ -8,7 +8,7 @@ use serde::Deserialize;
|
|||||||
use crate::users::{
|
use crate::users::{
|
||||||
User,
|
User,
|
||||||
auth::{
|
auth::{
|
||||||
AuthError, COOKIE_NAME, UserAuthRequired, UserAuthenticate,
|
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, UserAuthRequired,
|
||||||
implementation::authenticate_via_credentials,
|
implementation::authenticate_via_credentials,
|
||||||
},
|
},
|
||||||
sessions::Session,
|
sessions::Session,
|
||||||
@@ -38,5 +38,8 @@ pub async fn login(Json(creds): Json<LoginForm>) -> Result<Response, AuthError>
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn logout(headers: HeaderMap) -> Result<Response, AuthError> {
|
pub async fn logout(headers: HeaderMap) -> Result<Response, AuthError> {
|
||||||
todo!()
|
let mut s = Session::authenticate(&headers)?.required()?;
|
||||||
|
s.revoke(Some(&User::get_by_id(s.user_id)?))?;
|
||||||
|
let cookie = format!("{COOKIE_NAME}=revoking; Path=/; HttpOnly; Max-Age=0");
|
||||||
|
Ok(([(header::SET_COOKIE, cookie)], "Logged out!").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ use crate::{
|
|||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{
|
auth::{
|
||||||
AuthError, COOKIE_NAME, TokenSize, UserAuthDummyData, UserAuthRequired,
|
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, TokenSize,
|
||||||
UserAuthenticate, UserPasswordHashing,
|
UserAuthDummyData, UserAuthRequired, UserAuthenticate, UserPasswordHashing,
|
||||||
},
|
},
|
||||||
sessions::Session,
|
sessions::Session,
|
||||||
},
|
},
|
||||||
@@ -63,6 +63,14 @@ impl UserAuthRequired for Option<User> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl SessionAuthRequired for Option<Session> {
|
||||||
|
fn required(self) -> Result<Session, AuthError> {
|
||||||
|
match self {
|
||||||
|
Self::None => Err(AuthError::AuthRequired),
|
||||||
|
Self::Some(s) => Ok(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserPasswordHashing for User {
|
impl UserPasswordHashing for User {
|
||||||
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
|
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
|
||||||
@@ -122,6 +130,27 @@ impl<'a> AuthScheme<'a> {
|
|||||||
|
|
||||||
impl UserAuthenticate for User {
|
impl UserAuthenticate for User {
|
||||||
fn authenticate(headers: &HeaderMap) -> Result<Option<User>, AuthError> {
|
fn authenticate(headers: &HeaderMap) -> Result<Option<User>, AuthError> {
|
||||||
|
let (basic_auth, bearer_auth) = auth_common(&headers);
|
||||||
|
|
||||||
|
match (basic_auth, bearer_auth) {
|
||||||
|
(Some(creds), _) => authenticate_basic(&creds),
|
||||||
|
(None, Some(token)) => authenticate_bearer(&token),
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SessionAuthenticate for Session {
|
||||||
|
fn authenticate(headers: &HeaderMap) -> Result<Option<Session>, AuthError> {
|
||||||
|
let (_, bearer_auth) = auth_common(&headers);
|
||||||
|
if let Some(token) = bearer_auth {
|
||||||
|
authenticate_bearer_with_session(&token)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_common(headers: &HeaderMap) -> (Option<String>, Option<String>) {
|
||||||
let mut auth_values = Vec::new();
|
let mut auth_values = Vec::new();
|
||||||
for auth_header in headers.get_all(AUTHORIZATION).iter() {
|
for auth_header in headers.get_all(AUTHORIZATION).iter() {
|
||||||
if let Ok(s) = auth_header.to_str() {
|
if let Ok(s) = auth_header.to_str() {
|
||||||
@@ -138,32 +167,25 @@ impl UserAuthenticate for User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut basic_auth: Option<String> = None;
|
||||||
let mut basic_auth: Option<&str> = None;
|
let mut bearer_auth: Option<String> = None;
|
||||||
let mut bearer_auth: Option<&str> = None;
|
|
||||||
for header in &auth_values {
|
for header in &auth_values {
|
||||||
let header = header.trim();
|
let header = header.trim();
|
||||||
match AuthScheme::from_header(header) {
|
match AuthScheme::from_header(header) {
|
||||||
AuthScheme::Basic(creds) => {
|
AuthScheme::Basic(creds) => {
|
||||||
if basic_auth.is_none() {
|
if basic_auth.is_none() {
|
||||||
basic_auth = Some(creds);
|
basic_auth = Some(creds.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuthScheme::Bearer(token) => {
|
AuthScheme::Bearer(token) => {
|
||||||
if bearer_auth.is_none() {
|
if bearer_auth.is_none() {
|
||||||
bearer_auth = Some(token);
|
bearer_auth = Some(token.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuthScheme::None => {}
|
AuthScheme::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(basic_auth, bearer_auth)
|
||||||
match (basic_auth, bearer_auth) {
|
|
||||||
(Some(creds), _) => authenticate_basic(creds),
|
|
||||||
(None, Some(token)) => authenticate_bearer(token),
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn authenticate_basic(credentials: &str) -> Result<Option<User>, AuthError> {
|
fn authenticate_basic(credentials: &str) -> Result<Option<User>, AuthError> {
|
||||||
@@ -205,3 +227,11 @@ fn authenticate_bearer(token: &str) -> Result<Option<User>, AuthError> {
|
|||||||
s.prolong()?;
|
s.prolong()?;
|
||||||
Ok(Some(User::get_by_id(s.user_id)?))
|
Ok(Some(User::get_by_id(s.user_id)?))
|
||||||
}
|
}
|
||||||
|
fn authenticate_bearer_with_session(token: &str) -> Result<Option<Session>, AuthError> {
|
||||||
|
let mut s = Session::get_by_token(token)?;
|
||||||
|
if s.is_expired_or_revoked() {
|
||||||
|
return Err(AuthError::InvalidCredentials);
|
||||||
|
}
|
||||||
|
s.prolong()?;
|
||||||
|
Ok(Some(s))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use rand08::{RngCore, rngs::OsRng};
|
use rand08::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
use crate::users::{User, UserError, sessions::SessionError};
|
use crate::users::{
|
||||||
|
User, UserError,
|
||||||
|
sessions::{Session, SessionError},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod implementation;
|
pub mod implementation;
|
||||||
|
|
||||||
@@ -13,6 +16,12 @@ pub trait UserAuthenticate {
|
|||||||
pub trait UserAuthRequired {
|
pub trait UserAuthRequired {
|
||||||
fn required(self) -> Result<User, AuthError>;
|
fn required(self) -> Result<User, AuthError>;
|
||||||
}
|
}
|
||||||
|
pub trait SessionAuthenticate {
|
||||||
|
fn authenticate(headers: &HeaderMap) -> Result<Option<Session>, AuthError>;
|
||||||
|
}
|
||||||
|
pub trait SessionAuthRequired {
|
||||||
|
fn required(self) -> Result<Session, AuthError>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait UserPasswordHashing {
|
pub trait UserPasswordHashing {
|
||||||
/// Returns the hashed password as a String
|
/// Returns the hashed password as a String
|
||||||
|
|||||||
Reference in New Issue
Block a user