End-to-end web-based signup, login, and logout flows work.

This commit is contained in:
Joe Ardent 2023-05-29 14:25:50 -07:00
parent 113982ba27
commit d2613b0ef9
8 changed files with 95 additions and 15 deletions

View file

@ -1,15 +1,19 @@
use axum::response::{IntoResponse, Redirect}; use axum::response::{IntoResponse, Redirect};
use crate::AuthContext; use crate::{templates::Index, AuthContext};
pub async fn handle_slash_redir() -> impl IntoResponse { pub async fn handle_slash_redir() -> impl IntoResponse {
Redirect::temporary("/") Redirect::temporary("/")
} }
pub async fn handle_slash(auth: AuthContext) -> impl IntoResponse { pub async fn handle_slash(auth: AuthContext) -> impl IntoResponse {
if let Some(user) = auth.current_user { if let Some(ref user) = auth.current_user {
tracing::debug!("Logged in as: {user}"); let name = &user.username;
tracing::debug!("Logged in as: {name}");
} else { } else {
tracing::debug!("Not logged in.") tracing::debug!("Not logged in.");
}
Index {
user: auth.current_user,
} }
} }

View file

@ -11,7 +11,7 @@ use axum::{
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::{ use crate::{
templates::{LoginGet, LoginPost}, templates::{LoginGet, LoginPost, LogoutGet, LogoutPost},
util::form_decode, util::form_decode,
AuthContext, User, AuthContext, User,
}; };
@ -41,12 +41,12 @@ pub enum LoginErrorKind {
impl IntoResponse for LoginError { impl IntoResponse for LoginError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
match self.0 { match self.0 {
LoginErrorKind::Unknown => ( LoginErrorKind::Unknown | LoginErrorKind::Internal => (
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
"An unknown error occurred; you cursed, brah?", "An unknown error occurred; you cursed, brah?",
) )
.into_response(), .into_response(),
_ => (StatusCode::BAD_REQUEST, format!("{self}")).into_response(), _ => (StatusCode::OK, format!("{self}")).into_response(),
} }
} }
} }
@ -99,9 +99,12 @@ pub async fn get_login() -> impl IntoResponse {
} }
pub async fn get_logout() -> impl IntoResponse { pub async fn get_logout() -> impl IntoResponse {
todo!() LogoutGet
} }
pub async fn post_logout() -> impl IntoResponse { pub async fn post_logout(mut auth: AuthContext) -> impl IntoResponse {
todo!() if auth.current_user.is_some() {
auth.logout().await;
}
LogoutPost
} }

View file

@ -1,7 +1,10 @@
use std::net::SocketAddr; use std::{net::SocketAddr, time::Duration};
use axum::{routing::get, Router}; use axum::{routing::get, Router};
use axum_login::{axum_sessions::SessionLayer, AuthLayer, SqliteStore}; use axum_login::{
axum_sessions::{PersistencePolicy, SessionLayer},
AuthLayer, SqliteStore,
};
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use witch_watch::{ use witch_watch::{
@ -26,7 +29,7 @@ async fn main() {
let pool = db::get_pool().await; let pool = db::get_pool().await;
let secret = { let secret = {
let mut bytes = [0u8; 128]; let mut bytes = [0u8; 64];
let mut rng = OsRng; let mut rng = OsRng;
rng.fill_bytes(&mut bytes); rng.fill_bytes(&mut bytes);
bytes bytes
@ -35,7 +38,10 @@ async fn main() {
let session_layer = { let session_layer = {
let store = SqliteSessionStore::from_client(pool.clone()); let store = SqliteSessionStore::from_client(pool.clone());
store.migrate().await.expect("Could not migrate session DB"); store.migrate().await.expect("Could not migrate session DB");
SessionLayer::new(store, &secret).with_secure(true) SessionLayer::new(store, &secret)
.with_secure(true)
.with_persistence_policy(PersistencePolicy::ExistingOnly)
.with_session_ttl(Some(Duration::from_secs(3600 * 24 * 366)))
}; };
let auth_layer = { let auth_layer = {

View file

@ -1,6 +1,8 @@
use askama::Template; use askama::Template;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::User;
#[derive(Debug, Default, Template, Deserialize, Serialize)] #[derive(Debug, Default, Template, Deserialize, Serialize)]
#[template(path = "signup.html")] #[template(path = "signup.html")]
pub struct CreateUser { pub struct CreateUser {
@ -24,3 +26,17 @@ pub struct LoginGet {
pub username: String, pub username: String,
pub password: String, pub password: String,
} }
#[derive(Debug, Default, Template, Deserialize, Serialize)]
#[template(path = "logout_get.html")]
pub struct LogoutGet;
#[derive(Debug, Default, Template, Deserialize, Serialize)]
#[template(path = "logout_post.html")]
pub struct LogoutPost;
#[derive(Debug, Default, Template, Deserialize, Serialize)]
#[template(path = "index.html")]
pub struct Index {
pub user: Option<User>,
}

View file

@ -1,12 +1,13 @@
use std::fmt::Display; use std::fmt::Display;
use axum_login::{secrecy::SecretVec, AuthUser}; use axum_login::{secrecy::SecretVec, AuthUser};
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use uuid::Uuid; use uuid::Uuid;
const USERNAME_QUERY: &str = "select * from witches where username = $1"; const USERNAME_QUERY: &str = "select * from witches where username = $1";
#[derive(Debug, Default, Clone, PartialEq, Eq, sqlx::FromRow)] #[derive(Debug, Default, Clone, PartialEq, Eq, sqlx::FromRow, Serialize, Deserialize)]
pub struct User { pub struct User {
pub id: Uuid, pub id: Uuid,
pub username: String, pub username: String,

26
templates/index.html Normal file
View file

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block title %}Welcome to Witch Watch, Bish{% endblock %}
{% block content %}
<h1>Welcome to Witch Watch</h1>
{% match user %}
{% when Some with (usr) %}
<p>
Hello, {{ usr.username }}! It's nice to see you.
</p>
</br>
<p>
<form action="/logout" enctype="application/x-www-form-urlencoded" method="post">
<input type="submit" value="sign out?">
</form>
</p>
{% else %}
<p>
Heya, why don't you <a href="/login">log in</a> or <a href="/signup">sign up</a>?
</p>
{% endmatch %}
{% endblock %}

13
templates/logout_get.html Normal file
View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Logout of Witch Watch, Bish{% endblock %}
{% block content %}
<p>
<form action="/logout" enctype="application/x-www-form-urlencoded" method="post">
<input type="submit" value="Sign out">
</form>
</p>
{% endblock %}

View file

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block title %}Thanks for Signing Up for Witch Watch, Bish{% endblock %}
{% block content %}
<h1>Goodbye</h1>
<p>Good bye! May we suggest checking out <a href="/">our home page</a>?</p>
{% endblock %}