Standardize JWT authentication and finalize Activity Log V3 migration
This commit is contained in:
@@ -65,29 +65,35 @@ export const get_object = async function get_object({
|
||||
delete api_cfg['headers']['x-no-account-id'];
|
||||
}
|
||||
|
||||
// Clean the headers
|
||||
// Clean and merge headers
|
||||
const headers_cleaned: key_val = {};
|
||||
for (const prop in headers) {
|
||||
const merged_headers = { ...api_cfg['headers'], ...headers };
|
||||
|
||||
// Standardize all headers to kebab-case and ensure string values
|
||||
for (const prop in merged_headers) {
|
||||
const prop_cleaned = prop.replaceAll('_', '-');
|
||||
if (typeof headers[prop] != 'string') {
|
||||
headers[prop] = JSON.stringify(headers[prop]);
|
||||
}
|
||||
headers_cleaned[prop_cleaned] = headers[prop];
|
||||
if (log_lvl > 1) {
|
||||
console.log(`${prop_cleaned}: ${headers_cleaned[prop_cleaned]}`);
|
||||
let value = merged_headers[prop];
|
||||
if (value === null || value === undefined) continue;
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
headers_cleaned[prop_cleaned] = value;
|
||||
}
|
||||
headers = headers_cleaned;
|
||||
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log('All headers cleaned:', headers);
|
||||
console.log('Final cleaned headers:', headers_cleaned);
|
||||
}
|
||||
|
||||
const fetchOptions: RequestInit = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...api_cfg['headers'],
|
||||
...headers
|
||||
},
|
||||
headers: headers_cleaned,
|
||||
signal: controller.signal
|
||||
};
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ export const post_blob_percent_completed = temp_post_blob_percent_completed;
|
||||
export const temp_post_object_percent_completed = 0;
|
||||
export const post_object_percent_completed = temp_post_object_percent_completed;
|
||||
|
||||
// Updated 2024-05-23
|
||||
// Updated 2026-01-07
|
||||
export const post_object = async function post_object({
|
||||
api_cfg = null,
|
||||
endpoint = '',
|
||||
headers = {},
|
||||
params = {},
|
||||
data = {},
|
||||
form_data = null,
|
||||
@@ -23,6 +24,7 @@ export const post_object = async function post_object({
|
||||
}: {
|
||||
api_cfg: any;
|
||||
endpoint: string;
|
||||
headers?: any;
|
||||
params?: any;
|
||||
data?: any;
|
||||
form_data?: any;
|
||||
@@ -68,27 +70,38 @@ export const post_object = async function post_object({
|
||||
|
||||
// console.log('HERE!! API POST 2');
|
||||
|
||||
// Clean the headers
|
||||
// Clean and merge headers
|
||||
const headers_cleaned: Record<string, string> = {};
|
||||
for (const prop in api_cfg['headers']) {
|
||||
const merged_headers = { ...api_cfg['headers'], ...headers };
|
||||
|
||||
for (const prop in merged_headers) {
|
||||
const prop_cleaned = prop.replaceAll('_', '-');
|
||||
headers_cleaned[prop_cleaned] = api_cfg['headers'][prop];
|
||||
let value = merged_headers[prop];
|
||||
if (value === null || value === undefined) continue;
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
headers_cleaned[prop_cleaned] = value;
|
||||
}
|
||||
|
||||
// console.log('HERE!! API POST 3');
|
||||
// Auto-inject Authorization header if JWT is present but header is missing
|
||||
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
|
||||
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
|
||||
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
|
||||
}
|
||||
|
||||
if (form_data) {
|
||||
// headers_cleaned['Content-Type'] = 'multipart/form-data';
|
||||
delete headers_cleaned['content-type'];
|
||||
delete headers_cleaned['Content-Type'];
|
||||
delete headers_cleaned['content-type']; // Just in case
|
||||
delete headers_cleaned['Content-type']; // Just in case
|
||||
console.log('Form Data:', form_data);
|
||||
} else {
|
||||
headers_cleaned['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log('Cleaned Headers:', headers_cleaned);
|
||||
console.log('Final cleaned headers:', headers_cleaned);
|
||||
}
|
||||
|
||||
// console.log('HERE!! API POST 4');
|
||||
@@ -108,6 +121,8 @@ export const post_object = async function post_object({
|
||||
signal: controller.signal
|
||||
};
|
||||
|
||||
console.log('Final fetch options for post_object:', fetchOptions);
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log('Fetch Options:', fetchOptions);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,40 @@ export interface Site_Domain {
|
||||
* --- SITE CRUD ---
|
||||
*/
|
||||
|
||||
export async function lookup_site_domain({
|
||||
api_cfg,
|
||||
fqdn,
|
||||
view = 'default',
|
||||
log_lvl = 0
|
||||
}: {
|
||||
api_cfg: any;
|
||||
fqdn: string;
|
||||
view?: string;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
if (log_lvl) {
|
||||
console.log(`*** lookup_site_domain() *** fqdn=${fqdn}`);
|
||||
}
|
||||
|
||||
// We use get_ae_obj_id_crud because we are looking up by a unique field (fqdn) rather than ID.
|
||||
// This is the older method that uses the /crud/site/domain/:id endpoint.
|
||||
const result = await api.get_ae_obj_id_crud({
|
||||
api_cfg,
|
||||
no_account_id: true,
|
||||
obj_type: 'site_domain',
|
||||
obj_id: fqdn,
|
||||
use_alt_table: true,
|
||||
use_alt_base: true,
|
||||
log_lvl
|
||||
});
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Updated 2026-01-06
|
||||
// Updated 2026-01-06
|
||||
export async function lookup_site_domain_v3({
|
||||
@@ -94,7 +128,7 @@ export async function lookup_site_domain_v3({
|
||||
}
|
||||
|
||||
const search_query = {
|
||||
and: [{ field: 'fqdn', op: 'eq', value: fqdn }]
|
||||
q: fqdn
|
||||
};
|
||||
|
||||
// We use search because we are looking up by a unique field (fqdn) rather than ID.
|
||||
|
||||
@@ -38,6 +38,7 @@ export async function auth_ae_obj__username_password({
|
||||
}
|
||||
params['username'] = username; // Required
|
||||
params['password'] = password; // Required
|
||||
params['inc_jwt'] = true; // Request a JWT in the response
|
||||
if (log_lvl > 1) {
|
||||
console.log(`auth_ae_obj__username_password() - params:`, params);
|
||||
}
|
||||
@@ -104,6 +105,7 @@ export async function auth_ae_obj__user_id_user_auth_key({
|
||||
|
||||
params['user_id'] = user_id; // Required
|
||||
params['auth_key'] = user_auth_key; // Required
|
||||
params['inc_jwt'] = true; // Request a JWT in the response
|
||||
if (log_lvl > 1) {
|
||||
console.log(`auth_ae_obj__user_id_user_auth_key() - params:`, params);
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ export const delete_ae_obj_id_crud = async function delete_ae_obj_id_crud({
|
||||
|
||||
/* BEGIN: Hosted File Related */
|
||||
|
||||
// Updated 2023-08-17
|
||||
// Updated 2026-01-07
|
||||
export const download_hosted_file = async function download_hosted_file({
|
||||
api_cfg,
|
||||
hosted_file_id,
|
||||
@@ -693,6 +693,17 @@ export const download_hosted_file = async function download_hosted_file({
|
||||
}
|
||||
params['return_file'] = true;
|
||||
|
||||
// Inject JWT into URL parameters if available
|
||||
if (api_cfg.jwt) {
|
||||
params['jwt'] = api_cfg.jwt;
|
||||
} else if (api_cfg.headers?.['authorization']) {
|
||||
// Fallback: extract from header if present
|
||||
const auth_header = api_cfg.headers['authorization'];
|
||||
if (auth_header.startsWith('Bearer ')) {
|
||||
params['jwt'] = auth_header.substring(7);
|
||||
}
|
||||
}
|
||||
|
||||
const hosted_file_download_get_promise = await api.get_object({
|
||||
api_cfg: api_cfg,
|
||||
endpoint: endpoint,
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
let show_password_text = $state('password'); // password or text
|
||||
|
||||
function sign_in() {
|
||||
$ae_loc.jwt = user_obj.jwt; // Store the JSON Web Token
|
||||
$ae_loc.person_id = person_id; // Set the person_id in the ae_loc store
|
||||
$ae_loc.person = person_obj; // Store the full person object for reference
|
||||
$ae_loc.user_id = user_id; // Set the user_id in the ae_loc store
|
||||
@@ -112,6 +113,7 @@
|
||||
|
||||
function sign_out() {
|
||||
// Clear the session information
|
||||
$ae_loc.jwt = null;
|
||||
$ae_loc.person_id = null;
|
||||
$ae_loc.person = {};
|
||||
$ae_loc.user_id = null;
|
||||
|
||||
@@ -131,6 +131,8 @@ const ae_app_local_data_defaults: key_val = {
|
||||
user_email: null, // Currently used with Sponsorships only?
|
||||
user_access_type: null, // Used to revert back to the user's access type after quick access (temporarily escalate permissions) turned off.
|
||||
|
||||
jwt: null, // JSON Web Token for authenticated API requests
|
||||
|
||||
// Added 2025-04-04
|
||||
person_id: null, // The current person_id of the logged-in user (if any)
|
||||
person: {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// console.log(`ae_root +layout.ts: start`);
|
||||
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { lookup_site_domain_v3 } from '$lib/ae_core/ae_core__site';
|
||||
import { lookup_site_domain } from '$lib/ae_core/ae_core__site';
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import {
|
||||
@@ -31,8 +31,8 @@ const ae_api_init: key_val = {
|
||||
ver: '2024-08-11_11',
|
||||
base_url: api_base_url,
|
||||
base_url_bak: api_base_url_bak,
|
||||
api_secret_key: api_secret_key,
|
||||
api_secret_key_bak: api_secret_key,
|
||||
api_secret_key: api_secret_key,
|
||||
api_secret_key_bak: api_secret_key,
|
||||
api_crud_super_key: api_crud_super_key,
|
||||
headers: {},
|
||||
account_id: ae_account_id
|
||||
@@ -83,7 +83,7 @@ export async function load({ fetch, params, parent, route, url }) {
|
||||
ae_loc: {},
|
||||
ae_api: ae_api_init,
|
||||
ae_ds: {},
|
||||
ae_hub: {},
|
||||
ae_hub: {},
|
||||
ae_m_sponsorships: {},
|
||||
ae_m_events: {},
|
||||
ae_m_events_speakers: {},
|
||||
@@ -102,11 +102,11 @@ export async function load({ fetch, params, parent, route, url }) {
|
||||
};
|
||||
|
||||
const fqdn = url.host;
|
||||
|
||||
const result = await lookup_site_domain_v3({
|
||||
|
||||
const result = await lookup_site_domain({
|
||||
api_cfg: ae_api_init,
|
||||
fqdn,
|
||||
view: 'base',
|
||||
view: 'base',
|
||||
log_lvl
|
||||
}).catch((err) => {
|
||||
console.log('Site lookup failed in root layout.', err);
|
||||
@@ -128,11 +128,13 @@ export async function load({ fetch, params, parent, route, url }) {
|
||||
|
||||
ae_api_init['account_id'] = json_data.account_id_random;
|
||||
ae_api_init['headers']['x-account-id'] = json_data.account_id_random;
|
||||
ae_api_init['headers']['x-no-account-id'] = null;
|
||||
// ae_api_init['headers']['x-no-account-id'] = null;
|
||||
|
||||
ae_api_headers['x-account-id'] = ae_account_id;
|
||||
|
||||
ae_loc_init['account_id'] = json_data.account_id_random;
|
||||
ae_loc_init['account_code'] = json_data.account_code;
|
||||
ae_loc_init['account_name'] = json_data.account_name;
|
||||
ae_loc_init['account_code'] = json_data.account_code;
|
||||
ae_loc_init['account_name'] = json_data.account_name;
|
||||
|
||||
ae_loc_init['site_id'] = json_data.site_id_random;
|
||||
ae_loc_init['site_domain_id'] = json_data.site_domain_id_random;
|
||||
@@ -142,17 +144,17 @@ export async function load({ fetch, params, parent, route, url }) {
|
||||
ae_loc_init['site_google_tracking_id'] = json_data.google_tracking_id;
|
||||
ae_loc_init['site_access_code_kv'] = json_data.access_code_kv_json;
|
||||
ae_loc_init['site_cfg_json'] = json_data.cfg_json;
|
||||
ae_loc_init['site_access_key'] = json_data.access_key;
|
||||
ae_loc_init['site_domain_access_key'] = json_data.site_domain_access_key;
|
||||
ae_loc_init['site_access_key'] = json_data.access_key;
|
||||
ae_loc_init['site_domain_access_key'] = json_data.site_domain_access_key;
|
||||
|
||||
ae_loc_init['base_url'] = url.origin;
|
||||
ae_loc_init['hostname'] = url.hostname;
|
||||
|
||||
if (!ae_loc_init['site_access_key'] && !ae_loc_init['site_domain_access_key']) {
|
||||
ae_loc_init['key_checked'] = true;
|
||||
ae_loc_init['allow_access'] = true;
|
||||
ae_loc_init['key_checked'] = true;
|
||||
ae_loc_init['allow_access'] = true;
|
||||
} else {
|
||||
const access_key = url.searchParams.get('key');
|
||||
const access_key = url.searchParams.get('key');
|
||||
|
||||
if (access_key) {
|
||||
if (log_lvl) {
|
||||
|
||||
Reference in New Issue
Block a user