Skip to content

Commit 31b688b

Browse files
authored
Add files via upload
1 parent 8901c57 commit 31b688b

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "jwt_example"
3+
version = "0.1.0"
4+
edition = "2021"
5+
author = "Paxton Smith"
6+
# https://www.linkedin.com/in/paxton21/
7+
# https://github.com/Paxton21/actix-web-jwt-example
8+
9+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
10+
11+
[dependencies]
12+
actix-web = "4.5.1"
13+
jsonwebtoken = "9.3.0"
14+
rusqlite = { version = "0.31.0", features = ["bundled"] }
15+
serde = { version = "1.0.198", features = ["derive"]}
16+
chrono = "0.4.38"
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer, Responder};
2+
use chrono::{Duration, Utc};
3+
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
4+
use rusqlite::{Connection, Result};
5+
use serde::{Deserialize, Serialize};
6+
7+
#[derive(Debug, Serialize, Deserialize)]
8+
struct User {
9+
username: String,
10+
password: String,
11+
}
12+
13+
#[derive(Debug, Serialize, Deserialize)]
14+
struct JwtGen {
15+
username: String,
16+
exp: i64,
17+
}
18+
19+
const SEC_KEY: &[u8] = b"my_very_secret_key_def_not_known";
20+
21+
async fn register(user: web::Json<User>) -> impl Responder {
22+
if let Err(err) = db_reg(&user) {
23+
return HttpResponse::InternalServerError().body(err.to_string());
24+
}
25+
HttpResponse::Ok().body("User registered successfully")
26+
}
27+
28+
async fn login(user: web::Json<User>) -> impl Responder {
29+
if let Err(_) = authenticate_user(&user).await {
30+
return HttpResponse::Unauthorized().body("Invalid username or password");
31+
}
32+
33+
match generate_token(&user.username) {
34+
Ok(token) => HttpResponse::Ok().body(token),
35+
Err(_) => HttpResponse::InternalServerError().body("Internal Server Error"),
36+
}
37+
}
38+
39+
async fn protected(req: HttpRequest) -> impl Responder {
40+
if let Some(token) = req
41+
.headers()
42+
.get("jwt")
43+
.and_then(|value| value.to_str().ok())
44+
{
45+
if let Ok(token_data) = decode::<JwtGen>(
46+
token,
47+
&DecodingKey::from_secret(SEC_KEY),
48+
&Validation::new(Algorithm::HS256),
49+
) {
50+
if token_data.claims.exp < Utc::now().timestamp() {
51+
return HttpResponse::Unauthorized().body("Token expired");
52+
}
53+
54+
return HttpResponse::Ok().body("Welcome to the protected route");
55+
}
56+
}
57+
58+
HttpResponse::Unauthorized().body("Missing or invalid JWT token in the 'jwt' header")
59+
}
60+
61+
async fn unprotected() -> impl Responder {
62+
"Unprotected endpoint (does not require authentication)"
63+
}
64+
65+
#[actix_web::main]
66+
async fn main() -> std::io::Result<()> {
67+
HttpServer::new(|| {
68+
App::new()
69+
.route("/register", web::post().to(register))
70+
.route("/login", web::post().to(login))
71+
.route("/protected", web::get().to(protected))
72+
.route("/unprotected", web::get().to(unprotected))
73+
})
74+
.bind("127.0.0.1:8080")?
75+
.run()
76+
.await
77+
}
78+
79+
fn db_reg(user: &User) -> Result<()> {
80+
let conn = Connection::open("users.db")?;
81+
82+
conn.execute(
83+
"CREATE TABLE IF NOT EXISTS users (
84+
id INTEGER PRIMARY KEY,
85+
username TEXT NOT NULL UNIQUE,
86+
password TEXT NOT NULL
87+
)",
88+
[],
89+
)?;
90+
91+
conn.execute(
92+
"INSERT INTO users (username, password) VALUES (?1, ?2)",
93+
&[&user.username, &user.password],
94+
)?;
95+
96+
Ok(())
97+
}
98+
99+
async fn authenticate_user(user: &User) -> Result<(), ()> {
100+
let conn = Connection::open("users.db").map_err(|_| ())?;
101+
let mut stmt = conn
102+
.prepare("SELECT * FROM users WHERE username = ?1")
103+
.map_err(|_| ())?;
104+
let mut rows = stmt.query(&[&user.username]).map_err(|_| ())?;
105+
106+
if let Some(row) = rows.next().map_err(|_| ())? {
107+
let stored_password: String = row.get(2).map_err(|_| ())?;
108+
if stored_password != user.password {
109+
return Err(());
110+
}
111+
} else {
112+
return Err(());
113+
}
114+
Ok(())
115+
}
116+
117+
fn generate_token(username: &str) -> Result<String, jsonwebtoken::errors::Error> {
118+
let exp = Utc::now() + Duration::hours(2); // Set expiration time to 2 hours from now
119+
120+
let claims = JwtGen {
121+
username: username.to_owned(),
122+
exp: exp.timestamp(),
123+
};
124+
125+
let token = encode(
126+
&Header::default(),
127+
&claims,
128+
&EncodingKey::from_secret(SEC_KEY),
129+
)?;
130+
131+
Ok(token)
132+
}

JWT_Authentication_with_Actix-web_and_Rusqlite/users.db

Whitespace-only changes.

0 commit comments

Comments
 (0)