diff --git a/src/lib/ae_core/ae_core__user.ts b/src/lib/ae_core/ae_core__user.ts index 8f539151..72552920 100644 --- a/src/lib/ae_core/ae_core__user.ts +++ b/src/lib/ae_core/ae_core__user.ts @@ -328,117 +328,76 @@ export async function delete_ae_obj_id__user({ } /* - * *** LEGACY AUTHENTICATION HEADER LOGIC *** + * *** V3 AUTHENTICATION FUNCTIONS *** * - * The functions in this section interact with legacy Aether API authentication endpoints - * (e.g., /user/authenticate, /user/lookup_email). + * All functions below use the V3 action endpoints: + * POST /v3/action/user/authenticate (was: GET /user/authenticate) + * GET /v3/action/user/{id}/email_auth_key_url (was: GET /user/{id}/email_auth_key_url) + * POST /v3/crud/user/search (was: GET /user/lookup_email) + * POST /v3/action/user/{id}/change_password (was: PATCH /user/{id}/change_password) * - * Unlike V3 endpoints which handle context automatically or via standard headers, - * these legacy endpoints have specific requirements: - * - * 1. They often require the `x-account-id` header to be explicitly set to the target - * account ID to find the user within that specific account context. - * 2. The standard API wrapper logic might strip `x-account-id` if `x-no-account-id` - * is present (Bootstrap Paradox logic). We must explicitly remove `x-no-account-id` - * and set `x-account-id` to ensure the request is routed correctly. - * 3. Some endpoints accept `account_id` as a query parameter, while others (like email sending) - * may crash (500 Error) if unexpected parameters are passed. + * Key differences from the legacy routes: + * - Credentials are in the POST body, not query params (safer — not logged in URLs) + * - x-account-id header still required to scope username/email lookups to the account + * - x-no-account-id must still be removed when we have a real account context */ -// Updated 2025-04-04 -// This function handles username/password authentication. -// It explicitly sets the x-account-id header to ensure the user is looked up in the correct account. +// Updated 2026-04-25 — migrated from GET /user/authenticate to POST /v3/action/user/authenticate export async function auth_ae_obj__username_password({ api_cfg, account_id, - null_account_id = false, username, password, - params = {}, - try_cache = true, log_lvl = 0 }: { api_cfg: any; account_id: string; - null_account_id?: boolean; username: string; password: string; - params?: key_val; - try_cache?: boolean; log_lvl?: number; }) { if (log_lvl) { console.log( - `*** auth_ae_obj__username_password() *** account_id=${account_id} username=${username} password=${password}` + `*** auth_ae_obj__username_password() *** account_id=${account_id} username=${username}` ); } - const endpoint = '/user/authenticate'; - - // Prepare API config with correct headers to override global guest settings + // WHY: Must set x-account-id explicitly so the backend scopes the username lookup + // to the correct account. Remove x-no-account-id which is only used during bootstrap. const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; - if (account_id) { - use_api_cfg.headers['x-account-id'] = account_id; - delete use_api_cfg.headers['x-no-account-id']; - params['account_id'] = account_id; - } - - if (null_account_id) { - params['null_account_id'] = true; - } - 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); - } + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; ae_promises.auth__username_password = await api - .get_object({ + .post_object({ api_cfg: use_api_cfg, - endpoint: endpoint, - params: params, - // data: {}, - log_lvl: log_lvl + endpoint: '/v3/action/user/authenticate', + data: { username, password }, + log_lvl }) - .then(async function (user_obj_get_result) { - if (user_obj_get_result) { - return user_obj_get_result; - } else { - console.log('No results returned.'); - return null; - } + .then(function (result: any) { + return result ?? null; }) .catch(function (error: any) { - console.log('No results returned or failed.', error); + console.log('auth_ae_obj__username_password failed:', error); + return null; }); - if (log_lvl) { - console.log( - 'ae_promises.auth__username_password:', - ae_promises.auth__username_password - ); - } return ae_promises.auth__username_password; } -// Updated 2025-04-04 -// This function handles authentication using a User ID and a one-time auth key. +// Updated 2026-04-25 — migrated from GET /user/authenticate to POST /v3/action/user/authenticate export async function auth_ae_obj__user_id_user_auth_key({ api_cfg, account_id, user_id, user_auth_key, - params = {}, - try_cache = true, log_lvl = 0 }: { api_cfg: any; account_id: string; user_id: string; user_auth_key: string; - params?: key_val; - try_cache?: boolean; log_lvl?: number; }) { if (log_lvl) { @@ -447,61 +406,36 @@ export async function auth_ae_obj__user_id_user_auth_key({ ); } - const endpoint = '/user/authenticate'; - - // Prepare API config with correct headers to override global guest settings const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; - if (account_id) { - use_api_cfg.headers['x-account-id'] = account_id; - delete use_api_cfg.headers['x-no-account-id']; - params['account_id'] = account_id; - } - - 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); - } + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; ae_promises.auth__user_id_user_key = await api - .get_object({ + .post_object({ api_cfg: use_api_cfg, - endpoint: endpoint, - params: params, - log_lvl: log_lvl + endpoint: '/v3/action/user/authenticate', + // WHY: valid_email=true marks the user's email as verified on successful magic-link auth + data: { user_id, auth_key: user_auth_key, valid_email: true }, + log_lvl }) - .then(async function (user_obj_get_result) { - if (user_obj_get_result) { - return user_obj_get_result; - } else { - console.log('No results returned.'); - return null; - } + .then(function (result: any) { + return result ?? null; }) .catch(function (error: any) { - console.log('No results returned or failed.', error); + console.log('auth_ae_obj__user_id_user_auth_key failed:', error); + return null; }); - if (log_lvl) { - console.log( - 'ae_promises.auth__user_id_user_key:', - ae_promises.auth__user_id_user_key - ); - } return ae_promises.auth__user_id_user_key; } -// Send an email to the user with a new one time use authentication key. -// Updated 2025-04-08 -// NOTE: This legacy endpoint is sensitive to extra query parameters and will 500 if account_id is passed in the URL. +// Updated 2026-04-25 — migrated from GET /user/{id}/email_auth_key_url to V3 action path export async function send_email_auth_ae_obj__user_id({ api_cfg, account_id, user_id, base_url, - key_param_name = 'user_key', // API defaults to 'auth_key' - params = {}, + key_param_name = 'user_key', log_lvl = 0 }: { api_cfg: any; @@ -509,7 +443,6 @@ export async function send_email_auth_ae_obj__user_id({ user_id: string; base_url?: string; key_param_name?: string; - params?: key_val; log_lvl?: number; }) { if (log_lvl) { @@ -518,47 +451,30 @@ export async function send_email_auth_ae_obj__user_id({ ); } - const email_auth_key_endpoint = `/user/${user_id}/email_auth_key_url`; - params = { - root_url: base_url, - key_param_name: key_param_name - }; - - // Prepare API config with correct headers const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; - if (account_id) { - use_api_cfg.headers['x-account-id'] = account_id; - delete use_api_cfg.headers['x-no-account-id']; - // WARNING: Do NOT add account_id to params here, as it causes a 500 error on the legacy backend. - } + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; ae_promises.auth_key__send_email = await api.get_object({ api_cfg: use_api_cfg, - endpoint: email_auth_key_endpoint, - params: params, - log_lvl: log_lvl + endpoint: `/v3/action/user/${user_id}/email_auth_key_url`, + params: { root_url: base_url, key_param_name }, + log_lvl }); return ae_promises.auth_key__send_email; } -// Look up user based on email address provided -// Updated 2025-04-08 +// Updated 2026-04-25 — migrated from GET /user/lookup_email to POST /v3/crud/user/search export async function qry_ae_obj_li__user_email({ api_cfg, account_id, - null_account_id = false, email, - params = {}, - try_cache = true, log_lvl = 0 }: { api_cfg: any; account_id: string; - null_account_id?: boolean; email: string; - params?: key_val; - try_cache?: boolean; log_lvl?: number; }) { if (log_lvl) { @@ -567,56 +483,39 @@ export async function qry_ae_obj_li__user_email({ ); } - const endpoint = '/user/lookup_email'; - - // Prepare API config with correct headers const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; - if (account_id) { - use_api_cfg.headers['x-account-id'] = account_id; - delete use_api_cfg.headers['x-no-account-id']; - params['account_id'] = account_id; - } + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; - params['email'] = email; // Required - params['null_account_id'] = null_account_id || false; - - ae_promises.qry__user_email = await api - .get_object({ + const results = await api + .search_ae_obj({ api_cfg: use_api_cfg, - endpoint: endpoint, - params: params, - log_lvl: log_lvl - }) - .then(async function (user_obj_get_result) { - if (user_obj_get_result) { - return user_obj_get_result; - } else { - console.log('No results returned.'); - return null; - } + obj_type: 'user', + search_query: { and: [{ field: 'email', op: 'eq', value: email }] }, + log_lvl }) .catch(function (error: any) { - console.log('No results returned or failed.', error); + console.log('qry_ae_obj_li__user_email failed:', error); + return null; }); - return ae_promises.qry__user_email; + // Return the first match to preserve the same interface callers expect + // (the old /user/lookup_email endpoint returned a single user object) + return results?.[0] ?? null; } -// Change user password -// Updated 2025-04-11 +// Updated 2026-04-25 — migrated from PATCH /user/{id}/change_password to POST /v3/action export async function auth_ae_obj__user_id_change_password({ api_cfg, account_id, user_id, password, - params = {}, log_lvl = 0 }: { api_cfg: any; account_id: string; user_id: string; password: string; - params?: key_val; log_lvl?: number; }) { if (log_lvl) { @@ -625,27 +524,19 @@ export async function auth_ae_obj__user_id_change_password({ ); } - const endpoint = `/user/${user_id}/change_password`; - - params['user_id'] = user_id; // Required ae_promises.change_password__user_id = await api - .patch_object({ - api_cfg: api_cfg, - endpoint: endpoint, - params: params, - data: { password: password }, - log_lvl: log_lvl + .post_object({ + api_cfg, + endpoint: `/v3/action/user/${user_id}/change_password`, + data: { new_password: password }, + log_lvl }) - .then(async function (change_password_result) { - if (change_password_result) { - return change_password_result; - } else { - console.log('No results returned.'); - return null; - } + .then(function (result: any) { + return result ?? null; }) .catch(function (error: any) { - console.log('No results returned or failed.', error); + console.log('auth_ae_obj__user_id_change_password failed:', error); + return null; }); return ae_promises.change_password__user_id; diff --git a/src/lib/app_components/e_app_sign_in_out.svelte b/src/lib/app_components/e_app_sign_in_out.svelte index f7e9d26c..92a3f89a 100644 --- a/src/lib/app_components/e_app_sign_in_out.svelte +++ b/src/lib/app_components/e_app_sign_in_out.svelte @@ -252,7 +252,6 @@ function handle_lookup_user_email({ email }: { email: string }) { .qry_ae_obj_li__user_email({ api_cfg: $ae_api, account_id: $slct.account_id, - null_account_id: false, email: email, log_lvl: 0 }) @@ -321,7 +320,6 @@ async function handle_change_password() { await core_func.qry_ae_obj_li__user_email({ api_cfg: $ae_api, account_id: $slct.account_id, - null_account_id: false, email: user_email, log_lvl: 0 });