Temp authorization process. Templated responses

demo-mode
Wes Holland 1 year ago
parent c9ece20bd8
commit bba99009b4

12
Cargo.lock generated

@ -78,6 +78,17 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "askama_axum"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41603f7cdbf5ac4af60760f17253eb6adf6ec5b6f14a7ed830cf687d375f163"
dependencies = [
"askama",
"axum-core",
"http 1.1.0",
]
[[package]]
name = "askama_derive"
version = "0.12.5"
@ -1080,6 +1091,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"askama",
"askama_axum",
"axum",
"axum-htmx",
"dotenvy",

@ -21,6 +21,7 @@ tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
serde = { version = "1.0.213", features = ["derive"] }
reqwest = { version = "0.12.9", features = ["json"] }
askama_axum = "0.4.0"
[dev-dependencies]
httpc-test = "0.1.10"

@ -1,4 +1,4 @@
use anyhow::Context;
use anyhow::{anyhow, Context};
use axum::{async_trait, http};
use axum::extract::{FromRef, FromRequestParts, Query, State};
use axum::http::{header, StatusCode};
@ -66,6 +66,16 @@ pub async fn auth_authorized(
) -> anyhow::Result<impl IntoResponse, AppError> {
let user_info_endpoint = std::env::var("OAUTH_USER_INFO_URL")
.context("OAUTH_USER_INFO_URL not set")?;
// Validate that we have a stored csrf token that matches the one passed back to us
let stored_csrf_token = session.remove::<CsrfToken>(CSRF_TOKEN)
.await
.context("unable to access csrf token")?
.ok_or(anyhow!("session does not exist"))?;
if !query_auth.state.eq(stored_csrf_token.secret()) {
return Err(anyhow!("session csrf mismatch").into())
}
let token = oauth_client
.exchange_code(AuthorizationCode::new(query_auth.code.clone()))
@ -87,10 +97,24 @@ pub async fn auth_authorized(
.await
.context("failed to deserialize response as JSON")?;
session.insert(USER_SESSION, user_data).await?;
//TODO Check against database instead of string
let valid_users = std::env::var("AUTHORIZED_USERS")
.context("Authorized users not set")?;
let valid_users = valid_users.split(";")
.collect::<Vec<&str>>();
let is_authorized = valid_users.contains(&user_data.email.as_str());
if is_authorized {
session.insert(USER_SESSION, user_data).await?;
//TODO Redirect somewhere sane
Ok(Redirect::to("/protected"))
//TODO Redirect somewhere sane
Ok(Redirect::to("/protected").into_response())
}
else {
Ok((http::StatusCode::UNAUTHORIZED, "Unauthorized").into_response())
}
}
pub async fn auth_logout(
@ -157,13 +181,6 @@ where
.map_err(|_| UserExtractError(StatusCode::INTERNAL_SERVER_ERROR))?
.ok_or(UserExtractError(StatusCode::UNAUTHORIZED))?;
let db = SqlitePool::from_ref(state);
//TODO actual verification of users
if user.email != "whatswithwes@gmail.com" {
Err(UserExtractError(StatusCode::FORBIDDEN))?;
}
Ok(user)
}
}

@ -2,6 +2,7 @@ use crate::app_state::AppState;
use crate::auth::User;
use crate::error::AppError;
use anyhow::{anyhow, Context, Result};
use askama::Template;
use axum::extract::{FromRef, Query, State};
use axum::http::header::SET_COOKIE;
use axum::http::HeaderMap;
@ -76,17 +77,23 @@ async fn main() -> Result<()>{
.route("/auth/authorized", get(auth::auth_authorized));
let static_routes = Router::new()
.nest_service("/", ServeDir::new("static/"));
.nest_service("/css/pico.min.css", ServeFile::new("static/css/pico.min.css"))
.nest_service("/js/htmx.min.js", ServeFile::new("static/js/htmx.min.js"))
.nest_service("/favicon.ico", ServeFile::new("static/favicon.ico"));
let test_routes: Router<AppState> = Router::new()
.route("/fail", get(fail))
.route("/usertest", get(index))
.route("/protected", get(protected));
let app_routes: Router<AppState> = Router::new()
.route("/", get(index));
let router = Router::new()
.merge(auth_routes)
.merge(test_routes)
.fallback_service(static_routes)
.merge(app_routes)
.merge(static_routes)
.layer(session_layer)
.layer(TraceLayer::new_for_http())
.with_state(app_state);
@ -117,8 +124,17 @@ fn always_fails() -> Result<()> {
Err(anyhow!("I always fail"))
}
#[derive(Template)] // this will generate the code...
#[template(path = "index.html")] // using the template in this path, relative
// to the `templates` dir in the crate root
struct IndexTemplate<'a> { // the name of the struct can be anything
name: &'a str, // the field name should match the variable name
// in your template
}
async fn index(user: User) -> impl IntoResponse {
format!("Hello {}", user.email)
IndexTemplate { name: user.name.as_str() }.into_response()
//format!("Hello {}", user.email)
}
async fn protected(

@ -0,0 +1,12 @@
{% extends "main.html" %}
{% block content %}
<h1>Hello {{ name }}</h1>
<p>This is a test page</p>
<article>
<h2>Card</h2>
</article>
<a href="/auth/logout">Logout</a>
{% endblock %}

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="css/pico.min.css">
<script src="js/htmx.min.js"></script>
<title>Test Page</title>
</head>
<body>
<main class="container">
{% block content %}<p>Placeholder content</p>{% endblock %}
</main>
</body>
</html>
Loading…
Cancel
Save

Powered by TurnKey Linux.