Skip to content

Commit

Permalink
Check access_token expiration is enough
Browse files Browse the repository at this point in the history
  • Loading branch information
Timshel committed Feb 21, 2024
1 parent d019657 commit 70714f6
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 11 deletions.
12 changes: 10 additions & 2 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// JWT Handling
//
use chrono::{Duration, Utc};
use chrono::{Duration, NaiveDateTime, Utc};
use num_traits::FromPrimitive;
use once_cell::sync::Lazy;

Expand All @@ -20,6 +20,9 @@ use crate::{

const JWT_ALGORITHM: Algorithm = Algorithm::RS256;

// Limit when BitWarden consider the token as expired
pub static BW_EXPIRATION: Lazy<chrono::Duration> = Lazy::new(|| chrono::Duration::minutes(5));

pub static DEFAULT_REFRESH_VALIDITY: Lazy<Duration> = Lazy::new(|| Duration::days(7));
pub static DEFAULT_ACCESS_VALIDITY: Lazy<Duration> = Lazy::new(|| Duration::hours(2));
static JWT_HEADER: Lazy<Header> = Lazy::new(|| Header::new(JWT_ALGORITHM));
Expand Down Expand Up @@ -154,7 +157,7 @@ pub struct LoginJwtClaims {
}

impl LoginJwtClaims {
pub fn new(device: &Device, user: &User, nbf: i64, exp: i64, scope: Vec<String>) -> Self {
pub fn new(device: &Device, user: &User, nbf: i64, exp: i64, scope: Vec<String>, now: NaiveDateTime) -> Self {
// ---
// Disabled these keys to be added to the JWT since they could cause the JWT to get too large
// Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients
Expand All @@ -167,6 +170,10 @@ impl LoginJwtClaims {
// let orguser: Vec<_> = orgs.iter().filter(|o| o.atype == 2).map(|o| o.org_uuid.clone()).collect();
// let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();

if exp <= (now + *BW_EXPIRATION).timestamp() {
warn!("Raise access_token lifetime to more than 5min.")
}

// Create the JWT claims struct, to send to the client
Self {
nbf,
Expand Down Expand Up @@ -203,6 +210,7 @@ impl LoginJwtClaims {
time_now.timestamp(),
(time_now + *DEFAULT_ACCESS_VALIDITY).timestamp(),
auth_method.scope_vec(),
time_now,
)
}

Expand Down
18 changes: 9 additions & 9 deletions src/sso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use openidconnect::{
use crate::{
api::ApiResult,
auth,
auth::{AuthMethod, AuthMethodScope, AuthTokens, TokenWrapper, DEFAULT_REFRESH_VALIDITY},
auth::{AuthMethod, AuthMethodScope, AuthTokens, TokenWrapper, BW_EXPIRATION, DEFAULT_REFRESH_VALIDITY},
db::{
models::{Device, SsoNonce, User},
DbConn,
Expand All @@ -31,8 +31,6 @@ static AC_CACHE: Lazy<Cache<String, AuthenticatedUser>> =

static SSO_JWT_ISSUER: Lazy<String> = Lazy::new(|| format!("{}|sso", CONFIG.domain_origin()));

static DEFAULT_BW_EXPIRATION: Lazy<chrono::Duration> = Lazy::new(|| chrono::Duration::minutes(5));

pub static NONCE_EXPIRATION: Lazy<chrono::Duration> = Lazy::new(|| chrono::Duration::minutes(10));

trait AuthorizationRequestExt<'a> {
Expand Down Expand Up @@ -303,6 +301,7 @@ pub async fn exchange_code(wrapped_code: &str, conn: &mut DbConn) -> ApiResult<U
debug!("Id token: {}", id_token.to_string());
debug!("Access token: {}", token_response.access_token().secret().to_string());
debug!("Refresh token: {:?}", token_response.refresh_token().map(|t| t.secret().to_string()));
debug!("Expiration time: {:?}", token_response.expires_in());
}

let id_claims = match id_token.claims(&client.vw_id_token_verifier(), &oidc_nonce) {
Expand Down Expand Up @@ -370,16 +369,16 @@ pub fn create_auth_tokens(
expires_in: Option<Duration>,
) -> ApiResult<auth::AuthTokens> {
if !CONFIG.sso_auth_only_not_session() {
let now = Utc::now().naive_utc();

let (ap_nbf, ap_exp) = match (decode_token_claims("access_token", access_token), expires_in) {
(Ok(ap), _) => (ap.nbf(), ap.exp),
(Err(_), Some(exp)) => {
let time_now = Utc::now().naive_utc();
(time_now.timestamp(), (time_now + exp).timestamp())
}
(Err(_), Some(exp)) => (now.timestamp(), (now + exp).timestamp()),
_ => err!("Non jwt access_token and empty expires_in"),
};

let access_claims = auth::LoginJwtClaims::new(device, user, ap_nbf, ap_exp, auth::AuthMethod::Sso.scope_vec());
let access_claims =
auth::LoginJwtClaims::new(device, user, ap_nbf, ap_exp, auth::AuthMethod::Sso.scope_vec(), now);

_create_auth_tokens(device, refresh_token, access_claims, access_token)
} else {
Expand Down Expand Up @@ -461,7 +460,7 @@ pub async fn exchange_refresh_token(
}
Some(TokenWrapper::Access(access_token)) => {
let now = Utc::now().naive_utc();
let exp_limit = (now + *DEFAULT_BW_EXPIRATION).timestamp();
let exp_limit = (now + *BW_EXPIRATION).timestamp();

if refresh_claims.exp < exp_limit {
err_silent!("Access token is close to expiration but we have no refresh token")
Expand All @@ -479,6 +478,7 @@ pub async fn exchange_refresh_token(
now.timestamp(),
refresh_claims.exp,
auth::AuthMethod::Sso.scope_vec(),
now,
);
_create_auth_tokens(device, None, access_claims, access_token)
}
Expand Down

0 comments on commit 70714f6

Please sign in to comment.