Auth api response tells you who you are now

This commit is contained in:
Jeremy Wall 2022-12-19 15:42:35 -05:00
parent dfe5d668a4
commit 066fa8648d
2 changed files with 38 additions and 35 deletions

View File

@ -1,22 +0,0 @@
// Copyright 2022 Jeremy Wall (Jeremy@marzhilsltudios.com)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sqlx::{migrate, SqlitePool};
use std::sync::Arc;
pub async fn run_migration(pool: Arc<SqlitePool>) {
sqlx::migrate!("./migrations")
.run(pool.as_ref())
.await
.expect("Unable to run migratins");
}

View File

@ -18,20 +18,43 @@ use async_session::{Session, SessionStore};
use axum::{ use axum::{
extract::Extension, extract::Extension,
http::{header, HeaderMap, StatusCode}, http::{header, HeaderMap, StatusCode},
response::IntoResponse,
}; };
use axum_auth::AuthBasic; use axum_auth::AuthBasic;
use cookie::{Cookie, SameSite}; use cookie::{Cookie, SameSite};
use secrecy::Secret; use secrecy::Secret;
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, instrument}; use tracing::{debug, error, info, instrument};
use super::storage::{self, AuthStore}; use super::storage::{self, AuthStore, UserCreds};
// FIXME(jwall): This needs to live in a client integration library.
#[derive(Serialize, Deserialize)]
pub enum AccountResponse {
Success { user_id: String },
Err { message: String },
}
impl From<UserCreds> for AccountResponse {
fn from(auth: UserCreds) -> Self {
Self::Success {
user_id: auth.user_id().to_owned(),
}
}
}
impl<'a> From<&'a str> for AccountResponse {
fn from(msg: &'a str) -> Self {
Self::Err {
message: msg.to_string(),
}
}
}
#[instrument(skip_all, fields(user=%auth.0.0))] #[instrument(skip_all, fields(user=%auth.0.0))]
pub async fn handler( pub async fn handler(
auth: AuthBasic, auth: AuthBasic,
Extension(session_store): Extension<Arc<storage::SqliteStore>>, Extension(session_store): Extension<Arc<storage::SqliteStore>>,
) -> impl IntoResponse { ) -> (StatusCode, HeaderMap, axum::Json<AccountResponse>) {
// NOTE(jwall): It is very important that you do **not** log the password // NOTE(jwall): It is very important that you do **not** log the password
// here. We convert the AuthBasic into UserCreds immediately to help prevent // here. We convert the AuthBasic into UserCreds immediately to help prevent
// that. Do not circumvent that protection. // that. Do not circumvent that protection.
@ -44,28 +67,31 @@ pub async fn handler(
let mut session = Session::new(); let mut session = Session::new();
if let Err(err) = session.insert("user_id", auth.user_id()) { if let Err(err) = session.insert("user_id", auth.user_id()) {
error!(?err, "Unable to insert user id into session"); error!(?err, "Unable to insert user id into session");
let resp: AccountResponse = "Unable to insert user id into session".into();
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
headers, headers,
"Unable to insert user id into session", axum::Json::from(resp),
); );
} }
// 2. Store the session in the store. // 2. Store the session in the store.
let cookie_value = match session_store.store_session(session).await { let cookie_value = match session_store.store_session(session).await {
Err(err) => { Err(err) => {
error!(?err, "Unable to store session in session store"); error!(?err, "Unable to store session in session store");
let resp: AccountResponse = "Unable to store session in session store".into();
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
headers, headers,
"Unable to store session in session store", axum::Json::from(resp),
); );
} }
Ok(None) => { Ok(None) => {
error!("Unable to create session cookie"); error!("Unable to create session cookie");
let resp: AccountResponse = "Unable to create session cookie".into();
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
headers, headers,
"Unable to create session cookie", axum::Json::from(resp),
); );
} }
Ok(Some(value)) => value, Ok(Some(value)) => value,
@ -79,25 +105,24 @@ pub async fn handler(
let parsed_cookie = match cookie.to_string().parse() { let parsed_cookie = match cookie.to_string().parse() {
Err(err) => { Err(err) => {
error!(?err, "Unable to parse session cookie"); error!(?err, "Unable to parse session cookie");
let resp: AccountResponse = "Unable to parse session cookie".into();
return ( return (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
headers, headers,
"Unable to parse session cookie", axum::Json::from(resp),
); );
} }
Ok(parsed_cookie) => parsed_cookie, Ok(parsed_cookie) => parsed_cookie,
}; };
headers.insert(header::SET_COOKIE, parsed_cookie); headers.insert(header::SET_COOKIE, parsed_cookie);
// Respond with 200 OK // Respond with 200 OK
(StatusCode::OK, headers, "Login Successful") let resp: AccountResponse = auth.into();
(StatusCode::OK, headers, axum::Json::from(resp))
} else { } else {
debug!("Invalid credentials"); debug!("Invalid credentials");
let headers = HeaderMap::new(); let headers = HeaderMap::new();
( let resp: AccountResponse = "Invalid user id or password".into();
StatusCode::UNAUTHORIZED, (StatusCode::UNAUTHORIZED, headers, axum::Json::from(resp))
headers,
"Invalid user id or password",
)
} }
} }