use crate::modules::pxutils::{decode_jwt_payload, get_value_at_path, is_truthy_json_value};
use crate::px_debug;
use crate::pxconfig::PXConfig;
use crate::pxcontext::PXContext;
use fastly::Request;
use serde_json::{Map, Value};
pub(crate) fn enrich_context_from_request(req: &Request, conf: &PXConfig, ctx: &mut PXContext) {
let (jwt_token, user_id_field_name, additional_field_names) =
match resolve_jwt_source(req, conf, ctx) {
Some(source) => source,
None => return,
};
if user_id_field_name.is_empty() {
return;
}
let payload = match decode_jwt_payload(&jwt_token) {
Some(p) => p,
None => {
px_debug!("unable to decode user identifiers JWT token");
return;
}
};
let extracted = extract_jwt_fields(&payload, user_id_field_name, additional_field_names);
if let Some(app_user_id) = extracted.app_user_id {
ctx.app_user_id = Some(app_user_id);
}
if !extracted.jwt_additional_fields.is_empty() {
ctx.jwt_additional_fields = Some(extracted.jwt_additional_fields);
}
}
fn resolve_jwt_source<'a>(
req: &Request,
conf: &'a PXConfig,
ctx: &'a PXContext,
) -> Option<(String, &'a str, &'a [String])> {
if !conf.jwt_cookie_name.is_empty() && !conf.jwt_cookie_user_id_field_name.is_empty() {
if let Some(token) = ctx.cookies.get(conf.jwt_cookie_name.as_str()) {
if !token.is_empty() {
return Some((
token.clone(),
conf.jwt_cookie_user_id_field_name.as_str(),
conf.jwt_cookie_additional_field_names.as_slice(),
));
}
}
}
if !conf.jwt_header_name.is_empty() && !conf.jwt_header_user_id_field_name.is_empty() {
if let Some(token) = req
.get_header_str_lossy(conf.jwt_header_name.as_str())
.map(|v| v.into_owned())
.filter(|t| !t.is_empty())
{
return Some((
token,
conf.jwt_header_user_id_field_name.as_str(),
conf.jwt_header_additional_field_names.as_slice(),
));
}
}
None
}
pub(crate) struct ExtractedJwtFields {
pub app_user_id: Option<String>,
pub jwt_additional_fields: Map<String, Value>,
}
pub(crate) fn extract_jwt_fields(
payload: &Value,
user_id_field_name: &str,
additional_field_names: &[String],
) -> ExtractedJwtFields {
let mut jwt_additional_fields = Map::new();
let app_user_id = get_value_at_path(payload, user_id_field_name)
.and_then(|value| value.as_str().filter(|s| !s.is_empty()).map(str::to_owned));
for field_name in additional_field_names {
if field_name.is_empty() {
continue;
}
if let Some(value) = get_value_at_path(payload, field_name) {
if is_truthy_json_value(value) {
jwt_additional_fields.insert(field_name.clone(), value.clone());
}
}
}
ExtractedJwtFields {
app_user_id,
jwt_additional_fields,
}
}