feat!: start implementing repo listing & auth middleware

This commit is contained in:
2026-01-15 00:20:14 -08:00
parent 92d409f812
commit 6f2f64fd73
18 changed files with 1982 additions and 21 deletions

74
api/src/auth.rs Normal file
View File

@@ -0,0 +1,74 @@
use std::{env, sync::Arc};
use crate::error::Result;
use jsonwebtoken::{
Algorithm, DecodingKey, Validation, decode, decode_header, errors::ErrorKind, jwk::JwkSet,
};
use serde::Deserialize;
pub struct UserId(pub String);
pub struct JWT {
reqwest_client: reqwest::Client,
}
pub trait AuthImpl {
async fn for_protected(&self, token: &str) -> Result<UserId>;
}
pub enum Auth {
JWT(JWT),
}
#[derive(Deserialize)]
struct Claims {
sub: String,
}
impl JWT {
pub fn new(reqwest_client: reqwest::Client) -> Self {
Self { reqwest_client }
}
}
impl AuthImpl for JWT {
async fn for_protected(&self, token: &str) -> Result<UserId> {
let frontend_url =
env::var("FRONTEND_BASE_URL").unwrap_or_else(|_| "http://localhost:5173".to_string());
let jwks = get_jwks(
&self.reqwest_client,
&format!("{frontend_url}/api/auth/jwks"),
)
.await?;
let header = decode_header(token)?;
let kid = header.kid.ok_or(crate::error::Error::Unauthorized)?;
let jwk = jwks.find(&kid).ok_or(crate::error::Error::Unauthorized)?;
let decoding_key = DecodingKey::from_jwk(jwk)?;
let mut validation = Validation::new(Algorithm::EdDSA);
validation.set_issuer(&[&frontend_url]);
validation.set_audience(&[&frontend_url]);
let token_data =
decode::<Claims>(token, &decoding_key, &validation).map_err(|e| match e.kind() {
ErrorKind::ExpiredSignature => crate::error::Error::TokenExpired,
_ => crate::error::Error::Unauthorized,
})?;
Ok(UserId(token_data.claims.sub))
}
}
impl AuthImpl for Auth {
async fn for_protected(&self, token: &str) -> Result<UserId> {
match self {
Auth::JWT(jwt) => jwt.for_protected(token).await,
}
}
}
async fn get_jwks(client: &reqwest::Client, jwks_url: &str) -> Result<JwkSet> {
Ok(client.get(jwks_url).send().await?.json::<JwkSet>().await?)
}