Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Stipulations authored Apr 30, 2024
1 parent 8901c57 commit 31b688b
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 0 deletions.
16 changes: 16 additions & 0 deletions JWT_Authentication_with_Actix-web_and_Rusqlite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "jwt_example"
version = "0.1.0"
edition = "2021"
author = "Paxton Smith"
# https://www.linkedin.com/in/paxton21/
# https://github.com/Paxton21/actix-web-jwt-example

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-web = "4.5.1"
jsonwebtoken = "9.3.0"
rusqlite = { version = "0.31.0", features = ["bundled"] }
serde = { version = "1.0.198", features = ["derive"]}
chrono = "0.4.38"
132 changes: 132 additions & 0 deletions JWT_Authentication_with_Actix-web_and_Rusqlite/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use chrono::{Duration, Utc};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use rusqlite::{Connection, Result};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
username: String,
password: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct JwtGen {
username: String,
exp: i64,
}

const SEC_KEY: &[u8] = b"my_very_secret_key_def_not_known";

async fn register(user: web::Json<User>) -> impl Responder {
if let Err(err) = db_reg(&user) {
return HttpResponse::InternalServerError().body(err.to_string());
}
HttpResponse::Ok().body("User registered successfully")
}

async fn login(user: web::Json<User>) -> impl Responder {
if let Err(_) = authenticate_user(&user).await {
return HttpResponse::Unauthorized().body("Invalid username or password");
}

match generate_token(&user.username) {
Ok(token) => HttpResponse::Ok().body(token),
Err(_) => HttpResponse::InternalServerError().body("Internal Server Error"),
}
}

async fn protected(req: HttpRequest) -> impl Responder {
if let Some(token) = req
.headers()
.get("jwt")
.and_then(|value| value.to_str().ok())
{
if let Ok(token_data) = decode::<JwtGen>(
token,
&DecodingKey::from_secret(SEC_KEY),
&Validation::new(Algorithm::HS256),
) {
if token_data.claims.exp < Utc::now().timestamp() {
return HttpResponse::Unauthorized().body("Token expired");
}

return HttpResponse::Ok().body("Welcome to the protected route");
}
}

HttpResponse::Unauthorized().body("Missing or invalid JWT token in the 'jwt' header")
}

async fn unprotected() -> impl Responder {
"Unprotected endpoint (does not require authentication)"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/register", web::post().to(register))
.route("/login", web::post().to(login))
.route("/protected", web::get().to(protected))
.route("/unprotected", web::get().to(unprotected))
})
.bind("127.0.0.1:8080")?
.run()
.await
}

fn db_reg(user: &User) -> Result<()> {
let conn = Connection::open("users.db")?;

conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
)",
[],
)?;

conn.execute(
"INSERT INTO users (username, password) VALUES (?1, ?2)",
&[&user.username, &user.password],
)?;

Ok(())
}

async fn authenticate_user(user: &User) -> Result<(), ()> {
let conn = Connection::open("users.db").map_err(|_| ())?;
let mut stmt = conn
.prepare("SELECT * FROM users WHERE username = ?1")
.map_err(|_| ())?;
let mut rows = stmt.query(&[&user.username]).map_err(|_| ())?;

if let Some(row) = rows.next().map_err(|_| ())? {
let stored_password: String = row.get(2).map_err(|_| ())?;
if stored_password != user.password {
return Err(());
}
} else {
return Err(());
}
Ok(())
}

fn generate_token(username: &str) -> Result<String, jsonwebtoken::errors::Error> {
let exp = Utc::now() + Duration::hours(2); // Set expiration time to 2 hours from now

let claims = JwtGen {
username: username.to_owned(),
exp: exp.timestamp(),
};

let token = encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(SEC_KEY),
)?;

Ok(token)
}
Empty file.

0 comments on commit 31b688b

Please sign in to comment.