From a92fb07d23fb2268a6f4e650c5cbd00ad993e760 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 21 Aug 2024 13:25:55 +0200 Subject: Add login Fields sent are ``` { "query": "mutation Login($input: LoginCredentials!) { login(input: $input) { accessToken tokenType } }", "variables": { "input": { "email": "....", "password": "..." } } } ``` --- src/graphql/types/jwt.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ src/graphql/types/mod.rs | 1 + 2 files changed, 80 insertions(+) create mode 100644 src/graphql/types/jwt.rs (limited to 'src/graphql/types') diff --git a/src/graphql/types/jwt.rs b/src/graphql/types/jwt.rs new file mode 100644 index 0000000..932f7fd --- /dev/null +++ b/src/graphql/types/jwt.rs @@ -0,0 +1,79 @@ +use crate::errors::AppError; +use async_graphql::{InputObject, SimpleObject}; +use chrono::{Duration, Local}; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; + +struct Keys { + encoding: EncodingKey, + _decoding: DecodingKey, +} + +static KEYS: Lazy = Lazy::new(|| { + let secret = &crate::config::CONFIG.jwt_secret; + Keys::new(secret.as_bytes()) +}); + +impl Keys { + fn new(secret: &[u8]) -> Self { + Self { + encoding: EncodingKey::from_secret(secret), + _decoding: DecodingKey::from_secret(secret), + } + } +} + +/// Claims struct +#[derive(Serialize, Deserialize)] +pub struct Claims { + /// ID from the user model + pub user_id: i32, + /// Expiration timestamp + exp: usize, +} + +impl Claims { + /// Create a new Claim using the `user_id` and the current timestamp + 2 days + pub fn new(user_id: i32) -> Self { + let expiration = Local::now() + Duration::days(2); + + Self { + user_id, + exp: expiration.timestamp() as usize, + } + } + + /// Returns the token as a string. If a token is not encoded, raises an + /// `AppError::TokenCreation` + pub fn get_token(&self) -> Result { + let token = encode(&Header::default(), &self, &KEYS.encoding) + .map_err(|_| AppError::TokenCreation)?; + + Ok(token) + } +} + +#[derive(InputObject, Debug)] +pub struct LoginCredentials { + pub email: String, + pub password: String, +} + +/// Body used as response to login +#[derive(Serialize, SimpleObject)] +pub struct AuthBody { + /// Access token string + access_token: String, + /// "Bearer" string + token_type: String, +} + +impl AuthBody { + pub fn new(access_token: String) -> Self { + Self { + access_token, + token_type: "Bearer".to_string(), + } + } +} diff --git a/src/graphql/types/mod.rs b/src/graphql/types/mod.rs index 22d12a3..d7cdece 100644 --- a/src/graphql/types/mod.rs +++ b/src/graphql/types/mod.rs @@ -1 +1,2 @@ +pub mod jwt; pub mod user; -- cgit v1.2.3-71-g8e6c