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
+ }
0 commit comments