From 50008423203592bbcd6184f178b673bc0756bdbe Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Tue, 23 Jun 2026 04:36:19 +0800 Subject: [PATCH] fix(auth): add user billing dependency spans (#1948) --- internal/cloud/access-control.ts | 67 +++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/internal/cloud/access-control.ts b/internal/cloud/access-control.ts index 8ddecaee..f78d7636 100644 --- a/internal/cloud/access-control.ts +++ b/internal/cloud/access-control.ts @@ -599,8 +599,56 @@ class AccessController { return { ok: true, actor: session.user, session: publicSession(session), authMethod: AUTH_METHOD_WEB_SESSION }; } - async authenticateUserBillingToken(secret) { - const result = await this.userBilling.introspect(secret); + async withAuthOtelDependencySpan(name, otelContext, parentSpanId, attributes, fn, classifyResult = null) { + const spanId = otelContext ? newOtelSpanId() : ""; + const startedAtMs = Date.now(); + let statusCode = 0; + let errorCode = ""; + try { + const result = await fn(); + const classified = typeof classifyResult === "function" ? classifyResult(result) : null; + statusCode = Number(classified?.statusCode ?? 0); + errorCode = textOr(classified?.errorCode, ""); + return result; + } catch (error) { + statusCode = Number(error?.statusCode ?? error?.status ?? 503); + errorCode = textOr(error?.code, `${name.replace(/[^A-Za-z0-9]+/g, "_")}_exception`); + throw error; + } finally { + if (otelContext) { + void emitAuthOtelSpan(name, otelContext, this.env, { + spanId, + parentSpanId: parentSpanId || otelContext.rootSpanId, + kind: 3, + startTimeMs: startedAtMs, + endTimeMs: Date.now(), + status: errorCode ? "error" : "ok", + error: errorCode ? new Error(errorCode) : null, + attributes: { + ...attributes, + ...(statusCode > 0 ? { "http.status_code": statusCode } : {}), + ...(errorCode ? { "error.code": errorCode } : {}), + "auth.trace_visible": true, + "auth.values_redacted": true + } + }); + } + } + } + + async authenticateUserBillingToken(secret, otel = null) { + const otelContext = otel?.context ?? null; + const parentSpanId = textOr(otel?.parentSpanId, ""); + const result = await this.withAuthOtelDependencySpan("auth.user_billing.introspect", otelContext, parentSpanId, { + "http.method": "POST", + "http.route": "/internal/auth/introspect", + "server.address": "hwlab-user-billing", + "user_billing.service_id": "hwlab-user-billing", + "user_billing.operation": "introspect" + }, () => this.userBilling.introspect(secret), (item) => ({ + statusCode: Number(item?.status ?? 0), + errorCode: item?.ok ? "" : textOr(item?.error?.code, "user_billing_introspect_failed") + })); if (!result.ok) { return errorPayload(result.error?.code ?? "user_billing_introspect_failed", result.error?.message ?? "user-billing introspection failed", result.status ?? 503); } @@ -609,7 +657,11 @@ class AccessController { } const principal = result.body.principal; const actor = userBillingActor(principal); - const synced = await this.store.upsertExternalUser?.({ ...actor, authProvider: "user-billing", email: principal.email, now: this.now() }) ?? actor; + const synced = await this.withAuthOtelDependencySpan("auth.local.upsert_external_user", otelContext, parentSpanId, { + "db.system": "postgresql", + "db.sql.table": "users", + "auth.operation": "upsert_external_user" + }, async () => await this.store.upsertExternalUser?.({ ...actor, authProvider: "user-billing", email: principal.email, now: this.now() }) ?? actor); const authMethod = principal.authType === "session" ? AUTH_METHOD_USER_BILLING_SESSION : AUTH_METHOD_USER_BILLING_API_KEY; return { ok: true, @@ -944,11 +996,16 @@ class AccessController { }; } const upstreamToken = textOr(login.body.token, ""); - const auth = await this.authenticateUserBillingToken(upstreamToken); + const auth = await this.authenticateUserBillingToken(upstreamToken, otelContext ? { context: otelContext, parentSpanId: spanId } : null); if (!auth.ok) { + thrownCode = textOr(auth?.error?.code, "user_billing_introspect_failed"); return { ok: false, status: auth.status, error: auth.error }; } - const localSession = await this.issueWebSessionForActor(auth.actor); + const localSession = await this.withAuthOtelDependencySpan("auth.local.issue_web_session", otelContext, spanId, { + "db.system": "postgresql", + "db.sql.table": "sessions", + "auth.operation": "issue_web_session" + }, () => this.issueWebSessionForActor(auth.actor)); const localAuth = { ok: true, actor: auth.actor,