10 Commits

Author SHA1 Message Date
0482e1f0cc chore(deps): update rust crate clap to 4.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-13 00:24:58 +00:00
6fcfacfab2 Merge pull request 'chore(deps): update rust crate mailchecker to 6.0.20' (#481) from renovate/mailchecker-6.x into master
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-08 00:14:12 +00:00
ddaf74739d chore(deps): update rust crate mailchecker to 6.0.20
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-03-07 00:15:11 +00:00
c538f01148 Merge pull request 'chore(deps): update rust crate uuid to 1.22.0' (#480) from renovate/uuid-1.x into master
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-07 00:15:05 +00:00
1c2cd39305 chore(deps): update rust crate uuid to 1.22.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-03-06 00:14:55 +00:00
316e2bfaa5 Merge pull request 'chore(deps): update rust crate bcrypt to 0.19.0' (#479) from renovate/bcrypt-0.x into master
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-05 00:13:50 +00:00
1a92085d70 chore(deps): update rust crate bcrypt to 0.19.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-03-04 00:18:09 +00:00
2268e7cbff Merge pull request 'chore(deps): update rust crate chrono to 0.4.44' (#478) from renovate/chrono-0.x into master
All checks were successful
continuous-integration/drone/push Build is passing
2026-02-27 00:11:31 +00:00
4801ed7cf9 chore(deps): update rust crate chrono to 0.4.44
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2026-02-26 00:12:15 +00:00
Pierre HUBERT
7391a4f488 feat: can add custom claims to access token
All checks were successful
continuous-integration/drone/push Build is passing
2026-02-24 15:17:40 +01:00
7 changed files with 113 additions and 43 deletions

68
Cargo.lock generated
View File

@@ -368,7 +368,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse 0.2.7",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse 1.0.0",
"anstyle-query", "anstyle-query",
"anstyle-wincon", "anstyle-wincon",
"colorchoice", "colorchoice",
@@ -391,6 +406,15 @@ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.1.5" version = "1.1.5"
@@ -654,13 +678,13 @@ dependencies = [
[[package]] [[package]]
name = "bcrypt" name = "bcrypt"
version = "0.18.0" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a0f5948f30df5f43ac29d310b7476793be97c50787e6ef4a63d960a0d0be827" checksum = "523ab528ce3a7ada6597f8ccf5bd8d85ebe26d5edf311cad4d1d3cfb2d357ac6"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"blowfish", "blowfish",
"getrandom 0.3.4", "getrandom 0.4.1",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
@@ -843,9 +867,9 @@ dependencies = [
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.43" version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
@@ -876,9 +900,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.60" version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -886,11 +910,11 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.60" version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [ dependencies = [
"anstream", "anstream 1.0.0",
"anstyle", "anstyle",
"clap_lex", "clap_lex",
"strsim", "strsim",
@@ -898,9 +922,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.55" version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -1284,7 +1308,7 @@ version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d"
dependencies = [ dependencies = [
"anstream", "anstream 0.6.21",
"anstyle", "anstyle",
"env_filter", "env_filter",
"jiff", "jiff",
@@ -2231,9 +2255,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]] [[package]]
name = "mailchecker" name = "mailchecker"
version = "6.0.19" version = "6.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abad4bc63045f04cfc55aa4c55d4ec0a890c377ce56463bfc2adc2bc059c4b84" checksum = "e32897aec7dd28fa9a39f02a0a08e434f50e2761214e40cec745eb7c411cdb48"
dependencies = [ dependencies = [
"fast_chemail", "fast_chemail",
"once_cell", "once_cell",
@@ -2726,9 +2750,9 @@ checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.44" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@@ -3390,9 +3414,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.116" version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3777,9 +3801,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.21.0" version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37"
dependencies = [ dependencies = [
"getrandom 0.4.1", "getrandom 0.4.1",
"js-sys", "js-sys",

View File

@@ -11,15 +11,15 @@ actix-identity = "0.9.0"
actix-web = "4.13.0" actix-web = "4.13.0"
actix-session = { version = "0.11.0", features = ["cookie-session", "redis-session"] } actix-session = { version = "0.11.0", features = ["cookie-session", "redis-session"] }
actix-remote-ip = "0.1.0" actix-remote-ip = "0.1.0"
clap = { version = "4.5.60", features = ["derive", "env"] } clap = { version = "4.6.0", features = ["derive", "env"] }
include_dir = "0.7.4" include_dir = "0.7.4"
log = "0.4.29" log = "0.4.29"
serde_json = "1.0.149" serde_json = "1.0.149"
serde_yml = "0.0.12" serde_yml = "0.0.12"
env_logger = "0.11.9" env_logger = "0.11.9"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
bcrypt = "0.18.0" bcrypt = "0.19.0"
uuid = { version = "1.21.0", features = ["v4"] } uuid = { version = "1.22.0", features = ["v4"] }
mime_guess = "2.0.5" mime_guess = "2.0.5"
askama = "0.15.4" askama = "0.15.4"
urlencoding = "2.1.3" urlencoding = "2.1.3"
@@ -35,8 +35,8 @@ webauthn-rs = { version = "0.5.4", features = ["danger-allow-state-serialisation
url = "2.5.8" url = "2.5.8"
light-openid = { version = "1.1.0", features = ["crypto-wrapper"] } light-openid = { version = "1.1.0", features = ["crypto-wrapper"] }
rkyv = "0.8.15" rkyv = "0.8.15"
chrono = "0.4.43" chrono = "0.4.44"
mailchecker = "6.0.19" mailchecker = "6.0.20"
httpdate = "1.0.3" httpdate = "1.0.3"
build-time = "0.1.3" build-time = "0.1.3"
hex = "0.4.3" hex = "0.4.3"

View File

@@ -31,7 +31,7 @@ You can configure a list of clients (Relying Parties) in a `clients.yaml` file w
enforce_2fa_auth: true enforce_2fa_auth: true
# Optional, claims to be added to the ID token payload. # Optional, claims to be added to the ID token payload.
# The following placeholders can be set, they will the replaced when the token is created: # The following placeholders can be set, they will the replaced when the token is created:
# * {username}: user name of the user # * {username}: username of the user
# * {mail}: email address of the user # * {mail}: email address of the user
# * {first_name}: first name of the user # * {first_name}: first name of the user
# * {last_name}: last name of the user # * {last_name}: last name of the user
@@ -39,6 +39,11 @@ You can configure a list of clients (Relying Parties) in a `clients.yaml` file w
claims_id_token: claims_id_token:
groups: ["group_{user}"] groups: ["group_{user}"]
service: "auth" service: "auth"
# Optional, claims to be added to the access token payload
# The placeholders of `claims_id_token` can also be used here
claims_access_token:
groups: ["group_{user}"]
service: "auth"
# Optional, claims to be added to the user info endpoint response # Optional, claims to be added to the user info endpoint response
# The placeholders of `claims_id_token` can also be used here # The placeholders of `claims_id_token` can also be used here
claims_user_info: claims_user_info:

View File

@@ -4,10 +4,10 @@ use actix::{Actor, AsyncContext, Context, Handler};
use crate::constants::*; use crate::constants::*;
use crate::data::access_token::AccessToken; use crate::data::access_token::AccessToken;
use crate::data::app_config::AppConfig; use crate::data::app_config::AppConfig;
use crate::data::client::ClientID; use crate::data::client::{Client, ClientID};
use crate::data::code_challenge::CodeChallenge; use crate::data::code_challenge::CodeChallenge;
use crate::data::jwt_signer::JWTSigner; use crate::data::jwt_signer::JWTSigner;
use crate::data::user::UserID; use crate::data::user::{User, UserID};
use crate::utils::err::Res; use crate::utils::err::Res;
use crate::utils::string_utils::rand_str; use crate::utils::string_utils::rand_str;
use crate::utils::time_utils::time; use crate::utils::time_utils::time;
@@ -50,10 +50,13 @@ impl Session {
&mut self, &mut self,
app_config: &AppConfig, app_config: &AppConfig,
jwt_signer: &JWTSigner, jwt_signer: &JWTSigner,
user: &User,
client: &Client,
) -> Res { ) -> Res {
let access_token = AccessToken { let access_token = AccessToken {
issuer: app_config.website_origin.to_string(), issuer: app_config.website_origin.to_string(),
subject_identifier: self.user.clone().0, user,
client,
issued_at: time(), issued_at: time(),
exp_time: time() + OPEN_ID_ACCESS_TOKEN_TIMEOUT, exp_time: time() + OPEN_ID_ACCESS_TOKEN_TIMEOUT,
rand_val: rand_str(OPEN_ID_ACCESS_TOKEN_LEN), rand_val: rand_str(OPEN_ID_ACCESS_TOKEN_LEN),

View File

@@ -501,13 +501,7 @@ pub async fn token(
)); ));
} }
session.regenerate_access_and_refresh_tokens(AppConfig::get(), &jwt_signer)?; // Get user information
sessions
.send(openid_sessions_actor::UpdateSession(session.clone()))
.await
.unwrap();
let user: Option<User> = users let user: Option<User> = users
.send(users_actor::GetUserRequest(session.user.clone())) .send(users_actor::GetUserRequest(session.user.clone()))
.await .await
@@ -518,6 +512,18 @@ pub async fn token(
Some(u) => u, Some(u) => u,
}; };
// Refresh access and refresh tokens
session.regenerate_access_and_refresh_tokens(
AppConfig::get(),
&jwt_signer,
&user,
&client,
)?;
sessions
.send(openid_sessions_actor::UpdateSession(session.clone()))
.await
.unwrap();
// Generate id token // Generate id token
let id_token = IdToken { let id_token = IdToken {
issuer: AppConfig::get().website_origin.to_string(), issuer: AppConfig::get().website_origin.to_string(),
@@ -574,8 +580,24 @@ pub async fn token(
)); ));
} }
session.regenerate_access_and_refresh_tokens(AppConfig::get(), &jwt_signer)?; // Get user information
let user: Option<User> = users
.send(users_actor::GetUserRequest(session.user.clone()))
.await
.unwrap()
.0;
let user = match user {
None => return Ok(error_response(&query, "invalid_request", "User not found!")),
Some(u) => u,
};
// Regenerate user session
session.regenerate_access_and_refresh_tokens(
AppConfig::get(),
&jwt_signer,
&user,
&client,
)?;
sessions sessions
.send(openid_sessions_actor::UpdateSession(session.clone())) .send(openid_sessions_actor::UpdateSession(session.clone()))
.await .await

View File

@@ -1,9 +1,12 @@
use crate::data::client::{AdditionalClaims, Client};
use crate::data::user::User;
use jwt_simple::claims::JWTClaims; use jwt_simple::claims::JWTClaims;
use jwt_simple::prelude::Duration; use jwt_simple::prelude::Duration;
pub struct AccessToken { pub struct AccessToken<'a> {
pub issuer: String, pub issuer: String,
pub subject_identifier: String, pub user: &'a User,
pub client: &'a Client,
pub issued_at: u64, pub issued_at: u64,
pub exp_time: u64, pub exp_time: u64,
pub rand_val: String, pub rand_val: String,
@@ -13,21 +16,26 @@ pub struct AccessToken {
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
pub struct CustomAccessTokenClaims { pub struct CustomAccessTokenClaims {
rand_val: String, rand_val: String,
/// Additional claims
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub additional_claims: Option<AdditionalClaims>,
} }
impl AccessToken { impl<'a> AccessToken<'a> {
pub fn to_jwt_claims(self) -> JWTClaims<CustomAccessTokenClaims> { pub fn to_jwt_claims(self) -> JWTClaims<CustomAccessTokenClaims> {
JWTClaims { JWTClaims {
issued_at: Some(Duration::from_secs(self.issued_at)), issued_at: Some(Duration::from_secs(self.issued_at)),
expires_at: Some(Duration::from_secs(self.exp_time)), expires_at: Some(Duration::from_secs(self.exp_time)),
invalid_before: None, invalid_before: None,
issuer: Some(self.issuer), issuer: Some(self.issuer),
subject: Some(self.subject_identifier), subject: Some(self.user.uid.0.to_string()),
audiences: None, audiences: None,
jwt_id: None, jwt_id: None,
nonce: self.nonce, nonce: self.nonce,
custom: CustomAccessTokenClaims { custom: CustomAccessTokenClaims {
rand_val: self.rand_val, rand_val: self.rand_val,
additional_claims: self.client.claims_access_token(self.user),
}, },
} }
} }

View File

@@ -42,6 +42,9 @@ pub struct Client {
/// Additional claims to return with the id token /// Additional claims to return with the id token
claims_id_token: Option<AdditionalClaims>, claims_id_token: Option<AdditionalClaims>,
/// Additional claims to return with the access token
claims_access_token: Option<AdditionalClaims>,
/// Additional claims to return through the user info endpoint /// Additional claims to return through the user info endpoint
claims_user_info: Option<AdditionalClaims>, claims_user_info: Option<AdditionalClaims>,
} }
@@ -117,6 +120,11 @@ impl Client {
self.process_additional_claims(user, &self.claims_id_token) self.process_additional_claims(user, &self.claims_id_token)
} }
/// Get additional claims for access_token for a successful authentication
pub fn claims_access_token(&self, user: &User) -> Option<AdditionalClaims> {
self.process_additional_claims(user, &self.claims_access_token)
}
/// Get additional claims for user info for a successful authentication /// Get additional claims for user info for a successful authentication
pub fn claims_user_info(&self, user: &User) -> Option<AdditionalClaims> { pub fn claims_user_info(&self, user: &User) -> Option<AdditionalClaims> {
self.process_additional_claims(user, &self.claims_user_info) self.process_additional_claims(user, &self.claims_user_info)