added signin endpoint

This commit is contained in:
Mitchell Marino 2023-04-04 20:13:30 -05:00
parent e0e58811a8
commit 2856b1bbd8

View File

@ -1,16 +1,28 @@
use axum::{
extract::{Query, State},
extract::State,
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use jsonwebtoken::{EncodingKey, Header};
use serde::{Deserialize, Serialize};
use serde_json::json;
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;
use std::net::SocketAddr;
#[derive(Clone, Serialize, Deserialize, Debug, Hash)]
struct Claims {
username: String,
}
#[derive(Clone)]
struct AppState {
db_pool: PgPool,
jwt_key: EncodingKey,
}
#[tokio::main]
async fn main() {
// initialize tracing
@ -21,6 +33,8 @@ async fn main() {
let db_url = std::env::var("DATABASE_URL")
.expect("Set `DATABASE_URL` to the url of the postgres database.");
let jwt_secret = std::env::var("SAAPI_JWT_SECRET").unwrap_or("Sdb\\y5PP`,hmG+98".into());
let pool = PgPoolOptions::new()
.max_connections(50)
.connect(&db_url)
@ -33,7 +47,11 @@ async fn main() {
.route("/", get(root))
// `POST /users` goes to `create_user`
.route("/user/signup", post(signup))
.with_state(pool);
.route("/user/signin", post(signin))
.with_state(AppState {
db_pool: pool,
jwt_key: EncodingKey::from_secret(jwt_secret.as_bytes()),
});
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
@ -50,7 +68,10 @@ async fn root() -> &'static str {
"Hello, World!"
}
async fn signup(State(pool): State<PgPool>, Json(signup): Json<Signup>) -> impl IntoResponse {
async fn signup(
State(app_state): State<AppState>,
Json(signup): Json<Signup>,
) -> impl IntoResponse {
// insert your application logic here
let pass_hash = sha256::digest(&*signup.password);
@ -63,7 +84,7 @@ RETURNING id;
signup.username,
pass_hash.as_bytes(),
)
.fetch_one(&pool)
.fetch_one(&app_state.db_pool)
.await;
match result {
@ -92,13 +113,75 @@ RETURNING id;
}
}
// the input to our `create_user` handler
async fn signin(
State(app_state): State<AppState>,
Json(signin): Json<Signin>,
) -> impl IntoResponse {
let pass_hash = sha256::digest(&*signin.password);
let result = sqlx::query!(
r#"
SELECT username
FROM users
WHERE username = $1 AND password = $2
"#,
signin.username,
pass_hash.as_bytes(),
)
.fetch_optional(&app_state.db_pool)
.await;
match result {
Ok(Some(user)) => {
let claims = Claims {
username: user.username,
};
let token = match jsonwebtoken::encode(&Header::default(), &claims, &app_state.jwt_key)
{
Ok(token) => token,
Err(err) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": format!("Unknown error signing in when creating JWT: {}", err)
})),
)
}
};
return (StatusCode::OK, Json(json!({ "data": token })));
}
Ok(None) => {
return (
StatusCode::UNAUTHORIZED,
Json(json!({
"error": format!("Incorrect username or password")
})),
)
}
Err(err) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({
"error": format!("Unknown error signing in: {}", err)
})),
)
}
}
}
#[derive(Clone, Debug, Deserialize)]
struct Signup {
username: String,
password: String,
}
#[derive(Clone, Debug, Deserialize)]
struct Signin {
username: String,
password: String,
}
// the output to our `create_user` handler
#[derive(Serialize, sqlx::FromRow)]
struct User {