Content-Length: 504809 | pFad | https://github.com/sebadob/rauthy/commit/9ca697091eba2a6297c289162f426a92b28385f4

F0 Merge pull request #242 from sebadob/max_age+auth_time · sebadob/rauthy@9ca6970 · GitHub
Skip to content

Commit

Permalink
Merge pull request #242 from sebadob/max_age+auth_time
Browse files Browse the repository at this point in the history
Implement `max_age`  + `auth_time` during the auth flow
  • Loading branch information
sebadob authored Jan 16, 2024
2 parents 8375587 + 4017bf2 commit 9ca6970
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 37 deletions.
6 changes: 3 additions & 3 deletions frontend/src/components/account/AccInfo.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script>
import CheckIcon from "$lib/CheckIcon.svelte";
import {buidlWebIdUri, formatDateFromTs} from "../../utils/helpers.js";
import {buildWebIdUri, formatDateFromTs} from "../../utils/helpers.js";
export let t;
export let user = {};
Expand Down Expand Up @@ -90,8 +90,8 @@
<div class={classRow}>
<div class={classLabel}><b>WebID:</b></div>
<span class="value">
<a href={buidlWebIdUri(user.id)} target="_blank">
{@html buidlWebIdUri(user.id).replace('/auth/', '/auth/<wbr/>')}
<a href={buildWebIdUri(user.id)} target="_blank">
{@html buildWebIdUri(user.id).replace('/auth/', '/auth/<wbr/>')}
</a>
</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/account/AccWebId.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import {fade, slide} from 'svelte/transition';
import {putUserWebIdData} from "../../utils/dataFetching.js";
import Switch from "$lib/Switch.svelte";
import {buidlWebIdUri} from "../../utils/helpers.js";
import {buildWebIdUri} from "../../utils/helpers.js";
export let t;
export let webIdData;
Expand All @@ -13,7 +13,7 @@
let err = '';
let success = false;
let expertMode = !!webIdData.custom_triples;
let webIdLink = buidlWebIdUri(webIdData.user_id);
let webIdLink = buildWebIdUri(webIdData.user_id);
// do not set any value here - will be bound to validate function in <AccWebIdEntries/>
let getData;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "./constants.js";
import { decode, encode } from "base64-arraybuffer";

export function buidlWebIdUri(userId) {
export function buildWebIdUri(userId) {
return `${window.location.origen}/auth/${userId}/profile#me`
}

Expand Down
60 changes: 35 additions & 25 deletions rauthy-handlers/src/oidc.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::ops::Add;
use std::time::{SystemTime, UNIX_EPOCH};

use crate::{map_auth_step, real_ip_from_req, ReqPrincipal};
use actix_web::cookie::time::OffsetDateTime;
use actix_web::http::header::{HeaderValue, CONTENT_TYPE};
use actix_web::http::{header, StatusCode};
use actix_web::{get, post, web, HttpRequest, HttpResponse, HttpResponseBuilder, ResponseError};
use tracing::debug;

use chrono::Utc;
use rauthy_common::constants::{APPLICATION_JSON, COOKIE_MFA, HEADER_HTML, SESSION_LIFETIME};
use rauthy_common::error_response::ErrorResponse;
use rauthy_models::app_state::AppState;
Expand All @@ -28,8 +25,9 @@ use rauthy_models::templates::{
};
use rauthy_models::JwtCommonClaims;
use rauthy_service::auth;

use crate::{map_auth_step, real_ip_from_req, ReqPrincipal};
use std::ops::Add;
use std::time::{SystemTime, UNIX_EPOCH};
use tracing::debug;

//github.com/ OIDC Authorization HTML
//github.com/
Expand Down Expand Up @@ -75,12 +73,40 @@ pub async fn get_authorize(
}
};

if principal.validate_session_auth().is_ok() {
// check max_age and possibly force a new session
let mut force_new_session = if let Some(max_age) = req_data.max_age {
if let Some(session) = &principal.session {
let session_created = session.exp - *SESSION_LIFETIME as i64;
Utc::now().timestamp() > session_created + max_age
} else {
true
}
} else {
false
};

// check if the user needs to do the Webauthn login each time
let mut action = FrontendAction::None;
if let Ok(mfa_cookie) = WebauthnCookie::parse_validate(&req.cookie(COOKIE_MFA)) {
if let Ok(user) = User::find_by_email(&data, mfa_cookie.email.clone()).await {
// we need to check this, because a user could deactivate MFA in another browser or
// be deleted while still having existing mfa cookies somewhere else
if user.has_webauthn_enabled() {
action = FrontendAction::MfaLogin(mfa_cookie.email);

// if the user must do another MFA login anyway, we do never force a new session creation,
// because the authentication happens each time anyway
force_new_session = false;
}
}
}

// if the user is still authenticated and everything is valid -> immediate refresh
if !force_new_session && principal.validate_session_auth().is_ok() {
let csrf = principal.get_session_csrf_token()?;
let body =
AuthorizeHtml::build(&client.name, csrf, FrontendAction::Refresh, &colors, &lang);

// TODO make this prettier - append header conditionally easier?
if let Some(o) = origen_header {
return Ok(HttpResponse::Ok()
.insert_header(o)
Expand All @@ -97,22 +123,6 @@ pub async fn get_authorize(
return Ok(ErrorHtml::response(body, status));
}

let mut action = FrontendAction::None;

if let Ok(mfa_cookie) = WebauthnCookie::parse_validate(&req.cookie(COOKIE_MFA)) {
if let Ok(user) = User::find_by_email(&data, mfa_cookie.email.clone()).await {
// we need to check this, because a user could deactivate MFA in another browser or
// be deleted while still having existing mfa cookies somewhere else
if user.has_webauthn_enabled() {
action = FrontendAction::MfaLogin(mfa_cookie.email);
}
}
}

// if let Some(mfa_cookie) = req.cookie(COOKIE_MFA) {
// action = FrontendAction::MfaLogin(mfa_cookie.value().to_string())
// }

let body = AuthorizeHtml::build(&client.name, &session.csrf_token, action, &colors, &lang);

let cookie = session.client_cookie();
Expand Down
1 change: 1 addition & 0 deletions rauthy-models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ pub struct JwtIdClaims {
pub azp: String,
pub typ: JwtTokenType,
pub amr: Vec<String>,
pub auth_time: i64,
pub preferred_username: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
Expand Down
2 changes: 2 additions & 0 deletions rauthy-models/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub struct AuthRequest {
//github.com/ Validation: `[a-zA-Z0-9]`
#[validate(regex(path = "RE_ALNUM", code = "[a-zA-Z0-9]"))]
pub code_challenge_method: Option<String>,
#[validate(range(min = 0))]
pub max_age: Option<i64>,
}

#[derive(Debug, Deserialize, Validate, ToSchema)]
Expand Down
33 changes: 27 additions & 6 deletions rauthy-service/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use jwt_simple::claims;
use jwt_simple::prelude::*;
use rauthy_common::constants::{
CACHE_NAME_12HR, CACHE_NAME_LOGIN_DELAY, COOKIE_MFA, ENABLE_SOLID_AUD, ENABLE_WEB_ID,
HEADER_DPOP_NONCE, IDX_JWKS, IDX_JWK_LATEST, IDX_LOGIN_TIME, SESSION_RENEW_MFA, TOKEN_BEARER,
WEBAUTHN_REQ_EXP,
HEADER_DPOP_NONCE, IDX_JWKS, IDX_JWK_LATEST, IDX_LOGIN_TIME, SESSION_LIFETIME,
SESSION_RENEW_MFA, TOKEN_BEARER, WEBAUTHN_REQ_EXP,
};
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::password_hasher::HashPassword;
Expand Down Expand Up @@ -459,15 +459,35 @@ pub async fn build_id_token(
scope_customs: Option<(Vec<&Scope>, &Option<HashMap<String, Vec<u8>>>)>,
is_auth_code_flow: bool,
) -> Result<String, ErrorResponse> {
let amr = match user.has_webauthn_enabled() {
let now_ts = Utc::now().timestamp();
let (amr, auth_time) = match user.has_webauthn_enabled() {
true => {
if is_auth_code_flow {
JwtAmrValue::Mfa.to_string()
// With active MFA, the auth_time is always 'now', because it must be re-validated each time
(JwtAmrValue::Mfa.to_string(), now_ts)
} else {
JwtAmrValue::Pwd.to_string()
// TODO to get this 100% correct, we would need to do a DB migration with a new version and update
// something like a `last_auth` field in the DB each time. Not implemented now to not being forced into
// a new minor version just because of this small thing -> do in the future.
(
JwtAmrValue::Pwd.to_string(),
now_ts - *SESSION_LIFETIME as i64,
)
}
}
false => {
if is_auth_code_flow {
// TODO this is a bit inaccurate at this time as well -> improve with DB migration in future minor version
// it might be the case, that this was initiated with a direct refresh
(JwtAmrValue::Pwd.to_string(), now_ts)
} else {
// TODO make more accurate with future DB migration
(
JwtAmrValue::Pwd.to_string(),
now_ts - *SESSION_LIFETIME as i64,
)
}
}
false => JwtAmrValue::Pwd.to_string(),
};

let webid =
Expand All @@ -477,6 +497,7 @@ pub async fn build_id_token(
azp: client.id.clone(),
typ: JwtTokenType::Id,
amr: vec![amr],
auth_time,
preferred_username: user.email.clone(),
email: None,
email_verified: None,
Expand Down

0 comments on commit 9ca6970

Please sign in to comment.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/sebadob/rauthy/commit/9ca697091eba2a6297c289162f426a92b28385f4

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy