WIP: new-activity notification #231

Draft
trentlarson wants to merge 80 commits from notify-api into master
7 changed files with 59 additions and 11 deletions
Showing only changes of commit 0d7586865c - Show all commits

View File

@@ -30,7 +30,6 @@ import {
import {
getNotificationApiHeaders,
httpAuthErrorMessage,
logNotificationRequestAuthenticated,
logSkippingRefreshDueToMissingAuth,
} from "./notificationApiAuth";
import { logNotification } from "./NotificationDebugEvents";
@@ -591,7 +590,7 @@ export async function refreshNotificationsWithDiagnostics(options?: {
}
try {
const auth = await getNotificationApiHeaders();
const auth = await getNotificationApiHeaders("refresh");
if (!auth.ok) {
logSkippingRefreshDueToMissingAuth();
logRefreshFailure(startedAt, auth.message);
@@ -601,7 +600,6 @@ export async function refreshNotificationsWithDiagnostics(options?: {
errorMessage: auth.message,
};
}
logNotificationRequestAuthenticated("refresh");
const baseUrl = getNotificationApiBaseUrl();
const res = await fetch(`${baseUrl}/notifications/refresh`, {

View File

@@ -24,7 +24,6 @@ import {
getNotificationApiHeaders,
httpAuthErrorMessage,
logNotificationAuthFailure,
logNotificationRequestAuthenticated,
} from "./notificationApiAuth";
import {
logTokenRegistrationFailure,
@@ -42,12 +41,11 @@ export async function registerToken(fcmToken: string): Promise<void> {
const deviceId = await getOrCreateDeviceId();
const baseUrl = getNotificationApiBaseUrl();
try {
const auth = await getNotificationApiHeaders();
const auth = await getNotificationApiHeaders("register");
if (!auth.ok) {
logNotificationAuthFailure("register", auth.message);
throw new Error(`registerToken auth unavailable: ${auth.message}`);
}
logNotificationRequestAuthenticated("register");
const res = await fetch(`${baseUrl}/notifications/register`, {
method: "POST",

View File

@@ -54,7 +54,7 @@ async function registerRetrievedToken(
return;
}
const auth = await getNotificationApiHeaders();
const auth = await getNotificationApiHeaders("register");
if (!auth.ok) {
if (options?.force) {
throw new Error(`FCM registration auth unavailable: ${auth.message}`);

View File

@@ -21,6 +21,7 @@ export {
setBackendBaseUrl,
setTestMode,
} from "./NotificationDebugConfig";
export { shouldBypassNotificationAuth } from "./notificationApiDebugMode";
export {
appendLog,
clearNotificationDebugLogs,

View File

@@ -1,23 +1,33 @@
/**
* Authenticated headers for notification backend API calls (`/notifications/*`).
* Uses the same `getHeaders` + active DID flow as the rest of the app.
* Debug/local config can bypass auth for ngrok and panel testing.
*/
import { getHeaders } from "@/libs/endorserServer";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { logger } from "@/utils/logger";
import { shouldBypassNotificationAuth } from "./notificationApiDebugMode";
import { logNotification } from "./NotificationDebugEvents";
export type NotificationRequestKind = "register" | "refresh";
export type NotificationApiHeadersResult =
| { ok: true; headers: { "Content-Type": string; Authorization: string } }
| {
ok: true;
authenticated: boolean;
headers: Record<string, string>;
}
| {
ok: false;
reason: "no_active_did" | "missing_token";
message: string;
};
const DEBUG_HEADERS: Record<string, string> = {
"Content-Type": "application/json",
};
async function resolveActiveDid(): Promise<string | null> {
try {
const service = PlatformServiceFactory.getInstance();
@@ -46,8 +56,33 @@ function hasBearerToken(headers: {
);
}
/** Resolve `Authorization: Bearer …` headers for notification API requests. */
export async function getNotificationApiHeaders(): Promise<NotificationApiHeadersResult> {
function logAuthBypassEnabled(): void {
logNotification("Auth bypass enabled for debug/testing");
}
function logAuthenticatedNotificationRequest(): void {
logNotification("Using authenticated notification request");
}
function logDebugUnauthenticatedNotificationRequest(): void {
logNotification("Using debug unauthenticated notification request");
}
/**
* Resolve headers for notification API requests.
* @param kind Optional request kind for structured logs.
*/
export async function getNotificationApiHeaders(
kind?: NotificationRequestKind,
): Promise<NotificationApiHeadersResult> {
if (shouldBypassNotificationAuth()) {
logAuthBypassEnabled();
if (kind) {
logDebugUnauthenticatedNotificationRequest();
}
return { ok: true, authenticated: false, headers: { ...DEBUG_HEADERS } };
}
const did = await resolveActiveDid();
if (!did) {
return {
@@ -66,8 +101,13 @@ export async function getNotificationApiHeaders(): Promise<NotificationApiHeader
};
}
if (kind) {
logAuthenticatedNotificationRequest();
}
return {
ok: true,
authenticated: true,
headers: {
"Content-Type": headers["Content-Type"],
Authorization: headers.Authorization,

View File

@@ -0,0 +1,11 @@
/**
* Debug/local notification API auth bypass (ngrok, Notification Debug Panel).
* Production paths leave bypass off unless test mode or backend override is set.
*/
import { getBackendBaseUrl, getTestMode } from "./NotificationDebugConfig";
/** True when local notification debug config allows unauthenticated API calls. */
export function shouldBypassNotificationAuth(): boolean {
return getTestMode() || getBackendBaseUrl() !== null;
}

View File

@@ -78,7 +78,7 @@ export async function flushDeferredFcmRegistration(
return;
}
const auth = await getNotificationApiHeaders();
const auth = await getNotificationApiHeaders("register");
if (!auth.ok) {
scheduleDeferredRegistrationRetry();
return;