proper JWT making on login
This commit is contained in:
parent
75c9823e76
commit
c19d3f125e
|
@ -21,7 +21,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -57,7 +57,7 @@ checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
|
|||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"byteorder",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -67,7 +67,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
|
||||
dependencies = [
|
||||
"block-cipher-trait",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -174,7 +174,16 @@ dependencies = [
|
|||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -183,7 +192,7 @@ version = "0.6.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -310,10 +319,10 @@ dependencies = [
|
|||
"aes-gcm",
|
||||
"base64 0.12.3",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.7.1",
|
||||
"percent-encoding 2.1.0",
|
||||
"rand 0.7.3",
|
||||
"sha2",
|
||||
"sha2 0.8.2",
|
||||
"time 0.1.44",
|
||||
]
|
||||
|
||||
|
@ -344,16 +353,32 @@ dependencies = [
|
|||
"url 2.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpuid-bool"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
"subtle 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"subtle 2.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "devise"
|
||||
version = "0.2.0"
|
||||
|
@ -418,7 +443,16 @@ version = "0.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -591,6 +625,16 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.15"
|
||||
|
@ -644,8 +688,8 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
"digest 0.8.1",
|
||||
"hmac 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -654,8 +698,18 @@ version = "0.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
"crypto-mac 0.7.0",
|
||||
"digest 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff"
|
||||
dependencies = [
|
||||
"crypto-mac 0.9.1",
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -786,6 +840,21 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jwt"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddab2a3df24e9694bde32a01b4f7f3a3693715db5b0f259228b171b2a6526752"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"crypto-mac 0.9.1",
|
||||
"digest 0.9.0",
|
||||
"hmac 0.9.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
|
@ -1019,6 +1088,12 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "1.1.3"
|
||||
|
@ -1643,10 +1718,23 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if 0.1.10",
|
||||
"cpuid-bool",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1996,7 +2084,7 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.3",
|
||||
"subtle 2.3.0",
|
||||
]
|
||||
|
||||
|
@ -2162,12 +2250,15 @@ dependencies = [
|
|||
"chrono",
|
||||
"color-eyre",
|
||||
"diesel",
|
||||
"log 0.4.11",
|
||||
"hmac 0.9.0",
|
||||
"jwt",
|
||||
"lazy_static",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"rocket_oauth2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.1",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
|
|
|
@ -10,7 +10,10 @@ edition = "2018"
|
|||
chrono = { version = "0.4", features = ["serde"] }
|
||||
color-eyre = "0.5"
|
||||
diesel = { version = "1", features = ["postgres", "r2d2", "uuidv07", "chrono"] }
|
||||
log = "0"
|
||||
lazy_static = "1.4"
|
||||
jwt = "0.11"
|
||||
hmac = "0.9"
|
||||
sha2 = "0.9"
|
||||
rocket = "0.4"
|
||||
rocket_oauth2 = "0.4"
|
||||
serde = { version = "^1", features = ["derive"] }
|
||||
|
|
|
@ -8,4 +8,5 @@ in pkgs.mkShell rec {
|
|||
DATABASE_URL = "postgresql://postgres:hunter2@localhost:5432/wasmcloud";
|
||||
ROCKET_DATABASES = ''
|
||||
{ main_data = { url = "${DATABASE_URL}" } }'';
|
||||
JWT_SECRET = "hunter2";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use crate::{MainDatabase, models, schema};
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use diesel::prelude::*;
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use lazy_static::lazy_static;
|
||||
use std::env;
|
||||
use hmac::{Hmac, NewMac};
|
||||
use sha2::Sha256;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SECRET: String = env::var("JWT_SECRET")
|
||||
.expect("JWT_SECRET to be populated")
|
||||
.to_string();
|
||||
}
|
||||
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn make(user_id: uuid::Uuid, token_id: uuid::Uuid) -> Result<String> {
|
||||
let key: Hmac<Sha256> = Hmac::new_varkey(&*SECRET.as_bytes()).unwrap();
|
||||
let mut claims = BTreeMap::new();
|
||||
claims.insert("sub", user_id.to_string());
|
||||
claims.insert("jti", token_id.to_string());
|
||||
|
||||
let token_str = claims.sign_with_key(&key)?;
|
||||
tracing::debug!("token: {}", token_str);
|
||||
Ok(token_str)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(token, conn))]
|
||||
pub fn verify(token: String, conn: MainDatabase) -> Result<models::User> {
|
||||
use schema::{tokens::dsl::tokens, users::dsl::users};
|
||||
let key: Hmac<Sha256> = Hmac::new_varkey(&*SECRET.as_bytes()).unwrap();
|
||||
|
||||
let claims: BTreeMap<String, String> = token.verify_with_key(&key)?;
|
||||
let uid = uuid::Uuid::parse_str(
|
||||
&claims
|
||||
.get("sub")
|
||||
.ok_or(eyre!("can't get subscriber from JWT"))?,
|
||||
)?;
|
||||
let jti = claims
|
||||
.get("jti")
|
||||
.ok_or(eyre!("can't get token ID from JWT"))?;
|
||||
|
||||
let tok = tokens
|
||||
.find(uuid::Uuid::parse_str(&jti)?)
|
||||
.get_result::<models::Token>(&*conn)?;
|
||||
|
||||
if tok.deleted_at.is_none() {
|
||||
return Err(eyre!("token was deleted"));
|
||||
}
|
||||
|
||||
if tok.user_id != uid {
|
||||
return Err(eyre!("token and user mismatch"));
|
||||
}
|
||||
|
||||
let user = users.find(uid).get_result::<models::User>(&*conn)?;
|
||||
|
||||
Ok(user)
|
||||
}
|
77
src/main.rs
77
src/main.rs
|
@ -14,18 +14,19 @@ use rocket::{
|
|||
http::{Cookie, Cookies, SameSite},
|
||||
response::Redirect,
|
||||
};
|
||||
use rocket_contrib::{helmet::SpaceHelmet};
|
||||
use rocket_contrib::helmet::SpaceHelmet;
|
||||
use rocket_oauth2::{OAuth2, TokenResponse};
|
||||
|
||||
pub mod api;
|
||||
pub mod gitea;
|
||||
pub mod jwt;
|
||||
pub mod models;
|
||||
pub mod schema;
|
||||
|
||||
#[database("main_data")]
|
||||
pub struct MainDatabase(PgConnection);
|
||||
|
||||
struct Gitea;
|
||||
pub struct Gitea;
|
||||
|
||||
#[tracing::instrument(skip(oauth2, cookies))]
|
||||
#[get("/login/gitea")]
|
||||
|
@ -39,14 +40,14 @@ fn gitea_callback(
|
|||
conn: MainDatabase,
|
||||
token: TokenResponse<Gitea>,
|
||||
mut cookies: Cookies<'_>,
|
||||
) -> Redirect {
|
||||
) -> String {
|
||||
let tok = token.access_token().to_string();
|
||||
let refresh = token.refresh_token().unwrap().to_string();
|
||||
|
||||
let gitea_user = gitea::user(tok.clone()).expect("gitea api call to work");
|
||||
|
||||
use schema::{
|
||||
gitea_tokens,
|
||||
gitea_tokens, tokens,
|
||||
users::{
|
||||
dsl::{email, users},
|
||||
table as users_table,
|
||||
|
@ -57,56 +58,68 @@ fn gitea_callback(
|
|||
.limit(1)
|
||||
.load::<models::User>(&*conn)
|
||||
{
|
||||
Ok(u) => if u.len() == 0 {
|
||||
let u = models::NewUser {
|
||||
salutation: gitea_user.full_name,
|
||||
email: gitea_user.email,
|
||||
is_admin: gitea_user.is_admin,
|
||||
is_locked: false,
|
||||
tier: 0,
|
||||
};
|
||||
Ok(u) => {
|
||||
if u.len() == 0 {
|
||||
let u = models::NewUser {
|
||||
salutation: gitea_user.full_name,
|
||||
email: gitea_user.email,
|
||||
is_admin: gitea_user.is_admin,
|
||||
is_locked: false,
|
||||
tier: 0,
|
||||
};
|
||||
|
||||
let u: models::User = diesel::insert_into(users_table)
|
||||
.values(&u)
|
||||
.get_result(&*conn)
|
||||
.expect("able to insert user");
|
||||
let u: models::User = diesel::insert_into(users_table)
|
||||
.values(&u)
|
||||
.get_result(&*conn)
|
||||
.expect("able to insert user");
|
||||
|
||||
let tok = models::NewGiteaToken {
|
||||
user_id: u.id.clone(),
|
||||
access_token: tok,
|
||||
refresh_token: refresh,
|
||||
};
|
||||
let tok = models::NewGiteaToken {
|
||||
user_id: u.id.clone(),
|
||||
access_token: tok,
|
||||
refresh_token: refresh,
|
||||
};
|
||||
|
||||
let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table)
|
||||
.values(&tok)
|
||||
.get_result(&*conn)
|
||||
.expect("able to insert token");
|
||||
let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table)
|
||||
.values(&tok)
|
||||
.get_result(&*conn)
|
||||
.expect("able to insert token");
|
||||
|
||||
u
|
||||
} else {
|
||||
tracing::info!("{} {:?} logged in", u[0].id, u[0].salutation);
|
||||
u[0].clone()
|
||||
},
|
||||
u
|
||||
} else {
|
||||
tracing::info!("{} {:?} logged in", u[0].id, u[0].salutation);
|
||||
u[0].clone()
|
||||
}
|
||||
}
|
||||
Err(why) => {
|
||||
tracing::error!("error reading from database: {}", why);
|
||||
todo!("error response")
|
||||
}
|
||||
};
|
||||
|
||||
let tok: models::Token = diesel::insert_into(tokens::table)
|
||||
.values(&models::NewToken {
|
||||
user_id: user.id.clone(),
|
||||
})
|
||||
.get_result(&*conn)
|
||||
.expect("create token information");
|
||||
tracing::info!("created new token for {} with id {}", user.id, tok.id);
|
||||
|
||||
let tok = jwt::make(user.id, tok.id).expect("to sign JWT");
|
||||
// Set a private cookie with the access token
|
||||
cookies.add_private(
|
||||
Cookie::build("token", token.access_token().to_string())
|
||||
Cookie::build("token", tok.clone())
|
||||
.same_site(SameSite::Lax)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
Redirect::to("/")
|
||||
tok
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
tracing::trace!("JWT secret: {:?}", *jwt::SECRET);
|
||||
rocket::ignite()
|
||||
.attach(OAuth2::<Gitea>::fairing("gitea"))
|
||||
.attach(MainDatabase::fairing())
|
||||
|
|
Loading…
Reference in New Issue