proper JWT making on login

This commit is contained in:
Cadey Ratio 2020-10-28 12:20:00 -04:00
parent 75c9823e76
commit c19d3f125e
5 changed files with 220 additions and 51 deletions

127
Cargo.lock generated
View File

@ -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",

View File

@ -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"] }

View File

@ -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";
}

61
src/jwt.rs Normal file
View File

@ -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)
}

View File

@ -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())