Feat: JWT handling

This commit is contained in:
Jett Chen 2024-11-21 19:31:37 +08:00
parent e1fa45e39b
commit 820f619aeb
7 changed files with 832 additions and 59 deletions

768
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -68,6 +68,9 @@ tower = "0.4.13"
hyper-util = "0.1.6" hyper-util = "0.1.6"
toml = "0.8.15" toml = "0.8.15"
rust-ini = "0.21.0" rust-ini = "0.21.0"
jwt-simple = "0.12.10"
config = "0.14.1"
dotenvy = "0.15.7"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
caps = "0.5" caps = "0.5"

View file

@ -84,9 +84,11 @@ pub fn store_device(
) -> Result<()> { ) -> Result<()> {
log::debug!("Storing openid user {:#?}", openid_user); log::debug!("Storing openid user {:#?}", openid_user);
let conn = rusqlite::Connection::open(PATH)?; let conn = rusqlite::Connection::open(PATH)?;
todo!();
// TODO conn.execute(
"INSERT INTO device (name, public_key, apns_token, user_id, ipv4, ipv6, access_token, refresh_token)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
())?;
Ok(()) Ok(())
} }

View file

@ -1,6 +1,8 @@
use std::sync::Arc;
use tonic::{Request, Response, Status}; use tonic::{Request, Response, Status};
use crate::auth::server::providers::OpenIdUser; use crate::auth::server::providers::{KeypairT, OpenIdUser};
use super::{ use super::{
grpc_defs::{ grpc_defs::{
@ -9,10 +11,13 @@ use super::{
SlackAuthRequest, SlackAuthRequest,
}, },
providers::slack::auth, providers::slack::auth,
settings::BurrowAuthServerConfig,
}; };
#[derive(Debug)] struct BurrowGrpcServer {
struct BurrowGrpcServer; config: Arc<BurrowAuthServerConfig>,
jwt_keypair: Arc<KeypairT>,
}
#[tonic::async_trait] #[tonic::async_trait]
impl BurrowWeb for BurrowGrpcServer { impl BurrowWeb for BurrowGrpcServer {
@ -31,19 +36,19 @@ impl BurrowWeb for BurrowGrpcServer {
let jwt = req let jwt = req
.jwt .jwt
.ok_or(Status::invalid_argument("JWT Not existent!"))?; .ok_or(Status::invalid_argument("JWT Not existent!"))?;
let oid_user = let oid_user = OpenIdUser::try_from_jwt(&jwt, &self.jwt_keypair)
OpenIdUser::try_from(&jwt).map_err(|e| Status::invalid_argument(e.to_string()))?; .map_err(|e| Status::invalid_argument(e.to_string()))?;
unimplemented!() todo!()
} }
async fn delete_device(&self, request: Request<JwtInfo>) -> Result<Response<Empty>, Status> { async fn delete_device(&self, request: Request<JwtInfo>) -> Result<Response<Empty>, Status> {
unimplemented!() todo!()
} }
async fn list_devices( async fn list_devices(
&self, &self,
request: Request<JwtInfo>, request: Request<JwtInfo>,
) -> Result<Response<ListDevicesResponse>, Status> { ) -> Result<Response<ListDevicesResponse>, Status> {
unimplemented!() todo!()
} }
} }

View file

@ -2,6 +2,7 @@ pub mod db;
pub mod grpc_defs; pub mod grpc_defs;
mod grpc_server; mod grpc_server;
pub mod providers; pub mod providers;
pub mod settings;
use anyhow::Result; use anyhow::Result;
use axum::{http::StatusCode, routing::post, Router}; use axum::{http::StatusCode, routing::post, Router};
@ -11,9 +12,7 @@ use tokio::signal;
pub async fn serve() -> Result<()> { pub async fn serve() -> Result<()> {
db::init_db()?; db::init_db()?;
let app = Router::new() let app = Router::new().route("/device/new", post(device_new));
.route("/slack-auth", post(auth))
.route("/device/new", post(device_new));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap(); let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
log::info!("Starting auth server on port 8080"); log::info!("Starting auth server on port 8080");

View file

@ -1,18 +1,65 @@
pub mod slack; pub mod slack;
pub use super::{db, grpc_defs}; use self::grpc_defs::JwtInfo;
use anyhow::Result;
use grpc_defs::JwtInfo;
#[derive(serde::Deserialize, Default, Debug)] pub use super::{db, grpc_defs, settings::BurrowAuthServerConfig};
use anyhow::{anyhow, Result};
use jwt_simple::{
claims::{Claims, NoCustomClaims},
prelude::{Duration, Ed25519KeyPair, EdDSAKeyPairLike, EdDSAPublicKeyLike},
};
use serde::{Deserialize, Serialize};
pub type KeypairT = Ed25519KeyPair;
#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
pub struct OpenIdUser { pub struct OpenIdUser {
pub sub: String, pub sub: String,
pub name: String, pub name: String,
} }
impl TryFrom<&JwtInfo> for OpenIdUser { #[derive(Serialize, Deserialize, Debug)]
type Error = anyhow::Error; struct OpenIDCustomField {
pub name: String,
}
fn try_from(jwt_info: &JwtInfo) -> Result<Self> { impl OpenIdUser {
todo!() pub fn try_from_jwt(jwt_info: &JwtInfo, keypair: &KeypairT) -> Result<Self> {
let claims = keypair
.public_key()
.verify_token::<OpenIDCustomField>(&jwt_info.jwt, None)?;
Ok(Self {
sub: claims.subject.ok_or(anyhow!("No Subject!"))?,
name: claims.custom.name,
})
}
}
impl JwtInfo {
fn try_from_oid(oid_user: OpenIdUser, keypair: &KeypairT) -> Result<Self> {
let claims = Claims::with_custom_claims(
OpenIDCustomField { name: oid_user.name },
Duration::from_days(10),
)
.with_subject(oid_user.sub);
let jwt = keypair.sign(claims)?;
Ok(Self { jwt })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_jwt() -> Result<()> {
let key_pair = Ed25519KeyPair::generate();
let sample_usr = OpenIdUser {
sub: "Spanish".into(),
name: "Inquisition".into(),
};
let encoded = JwtInfo::try_from_oid(sample_usr.clone(), &key_pair)?;
let decoded = OpenIdUser::try_from_jwt(&encoded, &key_pair)?;
assert_eq!(decoded, sample_usr);
Ok(())
} }
} }

View file

@ -0,0 +1,23 @@
use config::{Config, ConfigError, Environment};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct BurrowAuthServerConfig {
jwt_secret_key: String,
jwt_public_key: String,
}
impl BurrowAuthServerConfig {
pub fn new() -> Result<Self, ConfigError> {
let s = Config::builder()
.add_source(Environment::default())
.build()?;
s.try_deserialize()
}
/// Creates a new config that includes the dotenv
pub fn new_dotenv() -> Result<Self, ConfigError> {
dotenvy::dotenv().ok();
Self::new()
}
}