added signin endpoint
This commit is contained in:
parent
e0e58811a8
commit
2856b1bbd8
99
src/main.rs
99
src/main.rs
@ -1,16 +1,28 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
extract::{Query, State},
|
extract::State,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
|
use jsonwebtoken::{EncodingKey, Header};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::net::SocketAddr;
|
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]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// initialize tracing
|
// initialize tracing
|
||||||
@ -21,6 +33,8 @@ async fn main() {
|
|||||||
let db_url = std::env::var("DATABASE_URL")
|
let db_url = std::env::var("DATABASE_URL")
|
||||||
.expect("Set `DATABASE_URL` to the url of the postgres database.");
|
.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()
|
let pool = PgPoolOptions::new()
|
||||||
.max_connections(50)
|
.max_connections(50)
|
||||||
.connect(&db_url)
|
.connect(&db_url)
|
||||||
@ -33,7 +47,11 @@ async fn main() {
|
|||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
// `POST /users` goes to `create_user`
|
// `POST /users` goes to `create_user`
|
||||||
.route("/user/signup", post(signup))
|
.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
|
// run our app with hyper
|
||||||
// `axum::Server` is a re-export of `hyper::Server`
|
// `axum::Server` is a re-export of `hyper::Server`
|
||||||
@ -50,20 +68,23 @@ async fn root() -> &'static str {
|
|||||||
"Hello, World!"
|
"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
|
// insert your application logic here
|
||||||
let pass_hash = sha256::digest(&*signup.password);
|
let pass_hash = sha256::digest(&*signup.password);
|
||||||
|
|
||||||
let result = sqlx::query!(
|
let result = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO users (username, password)
|
INSERT INTO users (username, password)
|
||||||
VALUES ( $1, $2 )
|
VALUES ( $1, $2 )
|
||||||
RETURNING id;
|
RETURNING id;
|
||||||
"#,
|
"#,
|
||||||
signup.username,
|
signup.username,
|
||||||
pass_hash.as_bytes(),
|
pass_hash.as_bytes(),
|
||||||
)
|
)
|
||||||
.fetch_one(&pool)
|
.fetch_one(&app_state.db_pool)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match result {
|
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)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
struct Signup {
|
struct Signup {
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
struct Signin {
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
// the output to our `create_user` handler
|
// the output to our `create_user` handler
|
||||||
#[derive(Serialize, sqlx::FromRow)]
|
#[derive(Serialize, sqlx::FromRow)]
|
||||||
struct User {
|
struct User {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user