From 43b037177518ccb80df78157759d54cab175f9e3 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 21 Sep 2021 15:18:14 +0200 Subject: [PATCH] move claims.rs back to utils --- Cargo.lock | 3 +- crates/api/src/lib.rs | 25 ++-- crates/api/src/local_user.rs | 16 ++- crates/api_common/Cargo.toml | 2 - crates/api_common/src/claims.rs | 63 ---------- crates/api_common/src/lib.rs | 14 ++- crates/api_crud/src/user/create.rs | 9 +- crates/db_queries/src/source/secrets.rs | 26 ++++- crates/routes/src/feeds.rs | 146 +++++++++++------------- crates/routes/src/images.rs | 11 +- crates/utils/Cargo.toml | 1 + crates/utils/src/claims.rs | 37 ++++++ crates/utils/src/lib.rs | 1 + 13 files changed, 170 insertions(+), 184 deletions(-) delete mode 100644 crates/api_common/src/claims.rs create mode 100644 crates/utils/src/claims.rs diff --git a/Cargo.lock b/Cargo.lock index 60173813f..8cd935abb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1628,8 +1628,6 @@ dependencies = [ "actix-web", "chrono", "diesel", - "jsonwebtoken", - "lazy_static", "lemmy_db_queries", "lemmy_db_schema", "lemmy_db_views", @@ -1909,6 +1907,7 @@ dependencies = [ "futures", "http", "itertools", + "jsonwebtoken", "lazy_static", "lettre", "log", diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 9b32c412b..3969570f9 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -187,30 +187,22 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String { #[cfg(test)] mod tests { - use diesel::{ - r2d2::{ConnectionManager, Pool}, - PgConnection, - }; - use lemmy_api_common::{check_validator_time, claims::Claims}; + use lemmy_api_common::check_validator_time; use lemmy_db_queries::{ establish_unpooled_connection, - get_database_url_from_env, - source::local_user::LocalUser_, + source::{local_user::LocalUser_, secrets::Secrets_}, Crud, }; use lemmy_db_schema::source::{ local_user::{LocalUser, LocalUserForm}, person::{Person, PersonForm}, + secrets::Secrets, }; - use lemmy_utils::settings::structs::Settings; + use lemmy_utils::claims::Claims; - #[actix_rt::test] - async fn test_should_not_validate_user_token_after_password_change() { + #[test] + fn test_should_not_validate_user_token_after_password_change() { let conn = establish_unpooled_connection(); - let db_url = get_database_url_from_env().unwrap_or_else(|_| Settings::get().get_database_url()); - let pool = Pool::builder() - .build(ConnectionManager::::new(&db_url)) - .unwrap(); let new_person = PersonForm { name: "Gerry9812".into(), @@ -227,8 +219,9 @@ mod tests { let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap(); - let jwt = Claims::jwt(inserted_local_user.id.0, &pool).await.unwrap(); - let claims = Claims::decode(&jwt, &pool).await.unwrap().claims; + let jwt_secret = Secrets::read_jwt_secret(&conn).unwrap(); + let jwt = Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref()).unwrap(); + let claims = Claims::decode(&jwt, jwt_secret.as_ref()).unwrap().claims; let check = check_validator_time(&inserted_local_user.validator_time, &claims); assert!(check.is_ok()); diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs index dc60424bd..03522301a 100644 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@ -6,7 +6,6 @@ use captcha::{gen, Difficulty}; use chrono::Duration; use lemmy_api_common::{ blocking, - claims::Claims, collect_moderated_communities, get_local_user_view_from_jwt, is_admin, @@ -26,6 +25,7 @@ use lemmy_db_queries::{ person_mention::PersonMention_, post::Post_, private_message::PrivateMessage_, + secrets::Secrets_, }, Blockable, Crud, @@ -44,6 +44,7 @@ use lemmy_db_schema::{ person_mention::*, post::Post, private_message::PrivateMessage, + secrets::Secrets, site::*, }, }; @@ -59,6 +60,7 @@ use lemmy_db_views_actor::{ person_view::PersonViewSafe, }; use lemmy_utils::{ + claims::Claims, email::send_email, location_info, settings::structs::Settings, @@ -103,8 +105,9 @@ impl Perform for Login { } // Return the jwt + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; Ok(LoginResponse { - jwt: Claims::jwt(local_user_view.local_user.id.0, context.pool()).await?, + jwt: Claims::jwt(local_user_view.local_user.id.0, jwt_secret.as_ref())?, }) } } @@ -268,8 +271,9 @@ impl Perform for SaveUserSettings { }; // Return the jwt + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, + jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, }) } } @@ -311,8 +315,9 @@ impl Perform for ChangePassword { .await??; // Return the jwt + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, + jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, }) } } @@ -770,8 +775,9 @@ impl Perform for PasswordChange { .map_err(|_| ApiError::err("couldnt_update_user"))?; // Return the jwt + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, + jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, }) } } diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index 19dfb652f..8e8cdb7fd 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -24,5 +24,3 @@ actix-web = { version = "4.0.0-beta.8", default-features = false, features = ["c chrono = { version = "0.4.19", features = ["serde"] } serde_json = { version = "1.0.66", features = ["preserve_order"] } url = "2.2.2" -jsonwebtoken = "7.2.0" -lazy_static = "1.4.0" diff --git a/crates/api_common/src/claims.rs b/crates/api_common/src/claims.rs deleted file mode 100644 index a30ee0b6e..000000000 --- a/crates/api_common/src/claims.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::blocking; -use chrono::Utc; -use diesel::PgConnection; -use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; -use lazy_static::lazy_static; -use lemmy_db_queries::{source::secrets::Secrets_, DbPool}; -use lemmy_db_schema::source::secrets::Secrets; -use lemmy_utils::{settings::structs::Settings, LemmyError}; -use serde::{Deserialize, Serialize}; -use std::{ops::Deref, sync::RwLock}; - -type Jwt = String; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Claims { - /// local_user_id, standard claim by RFC 7519. - pub sub: i32, - pub iss: String, - /// Time when this token was issued as UNIX-timestamp in seconds - pub iat: i64, -} - -impl Claims { - pub async fn decode(jwt: &str, pool: &DbPool) -> Result, LemmyError> { - let v = Validation { - validate_exp: false, - ..Validation::default() - }; - let secret = blocking(pool, move |conn| get_jwt_secret(conn)).await??; - let key = DecodingKey::from_secret(secret.as_ref()); - Ok(decode::(jwt, &key, &v)?) - } - - pub async fn jwt(local_user_id: i32, pool: &DbPool) -> Result { - let my_claims = Claims { - sub: local_user_id, - iss: Settings::get().hostname, - iat: Utc::now().timestamp(), - }; - - let secret = blocking(pool, move |conn| get_jwt_secret(conn)).await??; - let key = EncodingKey::from_secret(secret.as_ref()); - Ok(encode(&Header::default(), &my_claims, &key)?) - } -} - -lazy_static! { - static ref JWT_SECRET: RwLock> = RwLock::new(None); -} - -fn get_jwt_secret(conn: &PgConnection) -> Result { - let jwt_option: Option = JWT_SECRET.read().unwrap().deref().clone(); - match jwt_option { - Some(j) => Ok(j), - None => { - let jwt = Secrets::read(conn)?; - let jwt_static = JWT_SECRET.write(); - let mut jwt_static = jwt_static.unwrap(); - *jwt_static = Some(jwt.clone()); - Ok(jwt) - } - } -} diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index 72e301bad..309cd0fba 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -1,4 +1,3 @@ -pub mod claims; pub mod comment; pub mod community; pub mod person; @@ -6,12 +5,13 @@ pub mod post; pub mod site; pub mod websocket; -use crate::{claims::Claims, site::FederatedInstances}; +use crate::site::FederatedInstances; use diesel::PgConnection; use lemmy_db_queries::{ source::{ community::{CommunityModerator_, Community_}, person_block::PersonBlock_, + secrets::Secrets_, site::Site_, }, Crud, @@ -26,6 +26,7 @@ use lemmy_db_schema::{ person_block::PersonBlock, person_mention::{PersonMention, PersonMentionForm}, post::{Post, PostRead, PostReadForm}, + secrets::Secrets, site::Site, }, CommunityId, @@ -39,6 +40,7 @@ use lemmy_db_views_actor::{ community_view::CommunityView, }; use lemmy_utils::{ + claims::Claims, email::send_email, settings::structs::Settings, utils::MentionData, @@ -245,8 +247,8 @@ pub async fn get_local_user_view_from_jwt( jwt: &str, pool: &DbPool, ) -> Result { - let claims = Claims::decode(jwt, pool) - .await + let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??; + let claims = Claims::decode(jwt, jwt_secret.as_ref()) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); @@ -294,8 +296,8 @@ pub async fn get_local_user_settings_view_from_jwt( jwt: &str, pool: &DbPool, ) -> Result { - let claims = Claims::decode(jwt, pool) - .await + let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??; + let claims = Claims::decode(jwt, jwt_secret.as_ref()) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 5d8f45132..b5c2866e7 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -1,6 +1,6 @@ use crate::PerformCrud; use actix_web::web::Data; -use lemmy_api_common::{blocking, claims::Claims, password_length_check, person::*}; +use lemmy_api_common::{blocking, password_length_check, person::*}; use lemmy_apub::{ generate_apub_endpoint, generate_followers_url, @@ -9,7 +9,7 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_queries::{ - source::{local_user::LocalUser_, site::Site_}, + source::{local_user::LocalUser_, secrets::Secrets_, site::Site_}, Crud, Followable, Joinable, @@ -21,6 +21,7 @@ use lemmy_db_schema::{ community::*, local_user::{LocalUser, LocalUserForm}, person::*, + secrets::Secrets, site::*, }, CommunityId, @@ -28,6 +29,7 @@ use lemmy_db_schema::{ use lemmy_db_views_actor::person_view::PersonViewSafe; use lemmy_utils::{ apub::generate_actor_keypair, + claims::Claims, settings::structs::Settings, utils::{check_slurs, is_valid_actor_name}, ApiError, @@ -217,8 +219,9 @@ impl PerformCrud for Register { } // Return the jwt + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; Ok(LoginResponse { - jwt: Claims::jwt(inserted_local_user.id.0, context.pool()).await?, + jwt: Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref())?, }) } } diff --git a/crates/db_queries/src/source/secrets.rs b/crates/db_queries/src/source/secrets.rs index 0d54cf58a..471ccd196 100644 --- a/crates/db_queries/src/source/secrets.rs +++ b/crates/db_queries/src/source/secrets.rs @@ -1,13 +1,31 @@ -use diesel::{result::Error, *}; +use crate::{diesel::RunQueryDsl, lazy_static::__Deref}; +use diesel::PgConnection; use lemmy_db_schema::source::secrets::Secrets; +use lemmy_utils::LemmyError; +use std::sync::RwLock; pub trait Secrets_ { - fn read(conn: &PgConnection) -> Result; + fn read_jwt_secret(conn: &PgConnection) -> Result; +} + +// TODO: thread_local! might be better in terms of performance, but i couldnt get it to work +lazy_static! { + static ref JWT_SECRET: RwLock> = RwLock::new(None); } impl Secrets_ for Secrets { - fn read(conn: &PgConnection) -> Result { + fn read_jwt_secret(conn: &PgConnection) -> Result { use lemmy_db_schema::schema::secrets::dsl::*; - secrets.first::(conn).map(|s| s.jwt_secret) + let jwt_option: Option = JWT_SECRET.read().unwrap().deref().clone(); + match jwt_option { + Some(j) => Ok(j), + None => { + let jwt = secrets.first::(conn).map(|s| s.jwt_secret)?; + let jwt_static = JWT_SECRET.write(); + let mut jwt_static = jwt_static.unwrap(); + *jwt_static = Some(jwt.clone()); + Ok(jwt) + } + } } } diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 13b646808..5b90373a0 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -1,16 +1,16 @@ use actix_web::{error::ErrorBadRequest, *}; use anyhow::anyhow; use chrono::{DateTime, NaiveDateTime, Utc}; -use lemmy_api_common::{blocking, claims::Claims}; +use diesel::PgConnection; +use lemmy_api_common::blocking; use lemmy_db_queries::{ - source::{community::Community_, person::Person_}, + source::{community::Community_, person::Person_, secrets::Secrets_}, Crud, - DbPool, ListingType, SortType, }; use lemmy_db_schema::{ - source::{community::Community, local_user::LocalUser, person::Person}, + source::{community::Community, local_user::LocalUser, person::Person, secrets::Secrets}, LocalUserId, }; use lemmy_db_views::{ @@ -19,7 +19,12 @@ use lemmy_db_views::{ site_view::SiteView, }; use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView}; -use lemmy_utils::{settings::structs::Settings, utils::markdown_to_html, LemmyError}; +use lemmy_utils::{ + claims::Claims, + settings::structs::Settings, + utils::markdown_to_html, + LemmyError, +}; use lemmy_websocket::LemmyContext; use rss::{ extension::dublincore::DublinCoreExtensionBuilder, @@ -136,12 +141,13 @@ async fn get_feed( _ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))), }; - let builder = match request_type { - RequestType::User => get_feed_user(context.pool(), sort_type, param).await, - RequestType::Community => get_feed_community(context.pool(), sort_type, param).await, - RequestType::Front => get_feed_front(context.pool(), sort_type, param).await, - RequestType::Inbox => get_feed_inbox(context.pool(), param).await, - } + let builder = blocking(context.pool(), move |conn| match request_type { + RequestType::User => get_feed_user(conn, &sort_type, param), + RequestType::Community => get_feed_community(conn, &sort_type, param), + RequestType::Front => get_feed_front(conn, &sort_type, param), + RequestType::Inbox => get_feed_inbox(conn, param), + }) + .await? .map_err(ErrorBadRequest)?; let rss = builder.build().map_err(ErrorBadRequest)?.to_string(); @@ -161,23 +167,19 @@ fn get_sort_type(info: web::Query) -> Result { SortType::from_str(&sort_query) } -async fn get_feed_user( - pool: &DbPool, - sort_type: SortType, +fn get_feed_user( + conn: &PgConnection, + sort_type: &SortType, user_name: String, ) -> Result { - let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; - let person = blocking(pool, move |conn| Person::find_by_name(conn, &user_name)).await??; + let site_view = SiteView::read(conn)?; + let person = Person::find_by_name(conn, &user_name)?; - let person_id = person.id; - let posts = blocking(pool, move |conn| { - PostQueryBuilder::create(conn) - .listing_type(ListingType::All) - .sort(sort_type) - .creator_id(person_id) - .list() - }) - .await??; + let posts = PostQueryBuilder::create(conn) + .listing_type(ListingType::All) + .sort(*sort_type) + .creator_id(person.id) + .list()?; let items = create_post_items(posts)?; @@ -191,26 +193,19 @@ async fn get_feed_user( Ok(channel_builder) } -async fn get_feed_community( - pool: &DbPool, - sort_type: SortType, +fn get_feed_community( + conn: &PgConnection, + sort_type: &SortType, community_name: String, ) -> Result { - let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; - let community = blocking(pool, move |conn| { - Community::read_from_name(conn, &community_name) - }) - .await??; + let site_view = SiteView::read(conn)?; + let community = Community::read_from_name(conn, &community_name)?; - let community_id = community.id; - let posts = blocking(pool, move |conn| { - PostQueryBuilder::create(conn) - .listing_type(ListingType::All) - .sort(sort_type) - .community_id(community_id) - .list() - }) - .await??; + let posts = PostQueryBuilder::create(conn) + .listing_type(ListingType::All) + .sort(*sort_type) + .community_id(community.id) + .list()?; let items = create_post_items(posts)?; @@ -228,25 +223,23 @@ async fn get_feed_community( Ok(channel_builder) } -async fn get_feed_front( - pool: &DbPool, - sort_type: SortType, +fn get_feed_front( + conn: &PgConnection, + sort_type: &SortType, jwt: String, ) -> Result { - let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; - let local_user_id = LocalUserId(Claims::decode(&jwt, pool).await?.claims.sub); + let site_view = SiteView::read(conn)?; + let jwt_secret = Secrets::read_jwt_secret(conn)?; + let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub); + let local_user = LocalUser::read(conn, local_user_id)?; - let posts = blocking(pool, move |conn| { - let local_user = LocalUser::read(conn, local_user_id)?; - PostQueryBuilder::create(conn) - .listing_type(ListingType::Subscribed) - .my_person_id(local_user.person_id) - .show_bot_accounts(local_user.show_bot_accounts) - .show_read_posts(local_user.show_read_posts) - .sort(sort_type) - .list() - }) - .await??; + let posts = PostQueryBuilder::create(conn) + .listing_type(ListingType::Subscribed) + .my_person_id(local_user.person_id) + .show_bot_accounts(local_user.show_bot_accounts) + .show_read_posts(local_user.show_read_posts) + .sort(*sort_type) + .list()?; let items = create_post_items(posts)?; @@ -264,33 +257,28 @@ async fn get_feed_front( Ok(channel_builder) } -async fn get_feed_inbox(pool: &DbPool, jwt: String) -> Result { - let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; - let local_user_id = LocalUserId(Claims::decode(&jwt, pool).await?.claims.sub); - let local_user = blocking(pool, move |conn| LocalUser::read(conn, local_user_id)).await??; +fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { + let site_view = SiteView::read(conn)?; + let jwt_secret = Secrets::read_jwt_secret(conn)?; + let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub); + let local_user = LocalUser::read(conn, local_user_id)?; let person_id = local_user.person_id; let show_bot_accounts = local_user.show_bot_accounts; let sort = SortType::New; - let replies = blocking(pool, move |conn| { - CommentQueryBuilder::create(conn) - .recipient_id(person_id) - .my_person_id(person_id) - .show_bot_accounts(show_bot_accounts) - .sort(sort) - .list() - }) - .await??; + let replies = CommentQueryBuilder::create(conn) + .recipient_id(person_id) + .my_person_id(person_id) + .show_bot_accounts(show_bot_accounts) + .sort(sort) + .list()?; - let mentions = blocking(pool, move |conn| { - PersonMentionQueryBuilder::create(conn) - .recipient_id(person_id) - .my_person_id(person_id) - .sort(sort) - .list() - }) - .await??; + let mentions = PersonMentionQueryBuilder::create(conn) + .recipient_id(person_id) + .my_person_id(person_id) + .sort(sort) + .list()?; let items = create_reply_and_mention_items(replies, mentions)?; diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs index 1c2288a9a..a10d79566 100644 --- a/crates/routes/src/images.rs +++ b/crates/routes/src/images.rs @@ -2,8 +2,10 @@ use actix_http::http::header::ACCEPT_ENCODING; use actix_web::{body::BodyStream, http::StatusCode, web::Data, *}; use anyhow::anyhow; use awc::Client; -use lemmy_api_common::claims::Claims; -use lemmy_utils::{rate_limit::RateLimit, settings::structs::Settings, LemmyError}; +use lemmy_api_common::blocking; +use lemmy_db_queries::source::secrets::Secrets_; +use lemmy_db_schema::source::secrets::Secrets; +use lemmy_utils::{claims::Claims, rate_limit::RateLimit, settings::structs::Settings, LemmyError}; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -47,15 +49,16 @@ struct PictrsParams { async fn upload( req: HttpRequest, body: web::Payload, - client: web::Data, context: web::Data, + client: web::Data, ) -> Result { // TODO: check rate limit here let jwt = req .cookie("jwt") .expect("No auth header for picture upload"); - if Claims::decode(jwt.value(), context.pool()).await.is_err() { + let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + if Claims::decode(jwt.value(), jwt_secret.as_ref()).is_err() { return Ok(HttpResponse::Unauthorized().finish()); }; diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index fa59e7ce6..477833583 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -38,3 +38,4 @@ http = "0.2.4" deser-hjson = "1.0.2" smart-default = "0.6.0" webpage = { version = "1.3.0", default-features = false, features = ["serde"] } +jsonwebtoken = "7.2.0" diff --git a/crates/utils/src/claims.rs b/crates/utils/src/claims.rs new file mode 100644 index 000000000..7922eb367 --- /dev/null +++ b/crates/utils/src/claims.rs @@ -0,0 +1,37 @@ +use crate::{settings::structs::Settings, LemmyError}; +use chrono::Utc; +use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation}; +use serde::{Deserialize, Serialize}; + +type Jwt = String; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + /// local_user_id, standard claim by RFC 7519. + pub sub: i32, + pub iss: String, + /// Time when this token was issued as UNIX-timestamp in seconds + pub iat: i64, +} + +impl Claims { + pub fn decode(jwt: &str, jwt_secret: &[u8]) -> Result, LemmyError> { + let v = Validation { + validate_exp: false, + ..Validation::default() + }; + let key = DecodingKey::from_secret(jwt_secret); + Ok(decode::(jwt, &key, &v)?) + } + + pub fn jwt(local_user_id: i32, jwt_secret: &[u8]) -> Result { + let my_claims = Claims { + sub: local_user_id, + iss: Settings::get().hostname, + iat: Utc::now().timestamp(), + }; + + let key = EncodingKey::from_secret(jwt_secret); + Ok(encode(&Header::default(), &my_claims, &key)?) + } +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 8916e3e2d..f1093fd58 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -11,6 +11,7 @@ pub mod rate_limit; pub mod request; pub mod settings; +pub mod claims; #[cfg(test)] mod test; pub mod utils;