full backend rewrite.
calling this v0.4.0
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
use crate::error::ApiResult;
|
||||
use crate::model::auth::{AccessTokenForm, AuthResponse, LoginCredentials, SignupCredentials};
|
||||
use crate::svc::auth_svc::AuthService;
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{FromRequest, Outcome};
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
use rocket::{Request, State};
|
||||
use std::sync::LazyLock;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use crate::svc::access_token_svc::AccessTokenService;
|
||||
|
||||
#[post("/signup", data = "<cred>")]
|
||||
pub async fn signup(
|
||||
cred: Json<SignupCredentials>,
|
||||
svc: &State<AuthService>
|
||||
) -> ApiResult<Json<AuthResponse>> {
|
||||
let response = svc
|
||||
.signup(
|
||||
&cred.email, &cred.username, &cred.password, &cred.access_token,
|
||||
).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[post("/login", data = "<cred>")]
|
||||
pub async fn login(
|
||||
cred: Json<LoginCredentials>,
|
||||
svc: &State<AuthService>
|
||||
) -> ApiResult<Json<AuthResponse>> {
|
||||
Ok(Json(svc.login(&cred.username, &cred.password).await?))
|
||||
}
|
||||
|
||||
#[post("/invite", data = "<form>")]
|
||||
pub async fn generate_invite(
|
||||
session: Session,
|
||||
form: Json<AccessTokenForm>,
|
||||
svc: &State<AccessTokenService>
|
||||
) -> ApiResult<String> {
|
||||
svc.create(
|
||||
session.uid, &form.name, form.max_uses,
|
||||
form.start_date, form.expiry_date).await
|
||||
}
|
||||
|
||||
static JWT_SECRET: LazyLock<String> = LazyLock::new(|| std::env::var("JWT_SECRET").unwrap());
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TokenScope {
|
||||
Full,
|
||||
TotpPending,
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
pub uid: i64,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for Session {
|
||||
type Error = ();
|
||||
|
||||
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
match Claims::from_request(req).await {
|
||||
Outcome::Success(user) if user.scope == TokenScope::Full => Outcome::Success(Session {
|
||||
uid: user.sub as i64,
|
||||
}),
|
||||
Outcome::Success(_) => {
|
||||
eprintln!("warning: user with scope other than Full attempted to access session");
|
||||
Outcome::Error((Status::Forbidden, ()))
|
||||
}
|
||||
Outcome::Error(err) => {
|
||||
eprintln!("Session request guard failed: {:?}", err);
|
||||
Outcome::Error(err)
|
||||
}
|
||||
_ => unreachable!("forward should never be called"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub sub: i32,
|
||||
pub exp: usize,
|
||||
pub scope: TokenScope,
|
||||
}
|
||||
|
||||
impl Claims {
|
||||
pub fn new(user_id: usize, scope: TokenScope) -> Self {
|
||||
Self {
|
||||
sub: user_id as i32,
|
||||
exp: (SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
+ 3600) as usize,
|
||||
scope,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> String {
|
||||
encode(
|
||||
&Header::default(),
|
||||
self,
|
||||
&EncodingKey::from_secret(JWT_SECRET.as_bytes()),
|
||||
)
|
||||
.expect("unable to encode jwt")
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for Claims {
|
||||
type Error = ();
|
||||
|
||||
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
let token = req
|
||||
.headers()
|
||||
.get_one("Authorization")
|
||||
.and_then(|v| v.strip_prefix("Bearer "));
|
||||
|
||||
match token {
|
||||
None => Outcome::Error((Status::Unauthorized, ())),
|
||||
Some(t) => {
|
||||
match decode::<Claims>(
|
||||
t,
|
||||
&DecodingKey::from_secret(JWT_SECRET.as_bytes()),
|
||||
&Validation::default(),
|
||||
) {
|
||||
Ok(data) => Outcome::Success(data.claims),
|
||||
Err(_) => Outcome::Error((Status::Unauthorized, ())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user