Skip to content

Commit fa1e260

Browse files
committed
Switch debugger delivery to public ClientTokenAuth endpoint
Use the new POST /api/unstable/debugger/frontend/probes endpoint with dd-client-token header authentication instead of the same-origin /api/ui/debugger/probe-delivery route that relied on session cookies. - Build delivery URL from site config (https://api.{site}/...) - Authenticate via dd-client-token header instead of credentials: same-origin - Add proxy config for routing delivery requests through a custom origin - Move delivery mock from base server to intake server in E2E tests
1 parent b9da4f3 commit fa1e260

7 files changed

Lines changed: 115 additions & 27 deletions

File tree

packages/debugger/src/domain/deliveryApi.spec.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,61 @@ import { registerCleanupTask, mockClock, replaceMockable } from '@datadog/browse
33
import type { Clock } from '@datadog/browser-core/test'
44
import { getProbes, clearProbes } from './probes'
55
import type { Probe } from './probes'
6-
import { startDeliveryApiPolling, stopDeliveryApiPolling, clearDeliveryApiState } from './deliveryApi'
6+
import {
7+
buildDeliveryApiUrl,
8+
startDeliveryApiPolling,
9+
stopDeliveryApiPolling,
10+
clearDeliveryApiState,
11+
} from './deliveryApi'
712
import type { DeliveryApiConfiguration } from './deliveryApi'
813

14+
describe('buildDeliveryApiUrl', () => {
15+
it('should default to datadoghq.com', () => {
16+
expect(buildDeliveryApiUrl()).toBe('https://api.datadoghq.com/api/unstable/debugger/frontend/probes')
17+
})
18+
19+
it('should build URL for US1 site', () => {
20+
expect(buildDeliveryApiUrl('datadoghq.com')).toBe('https://api.datadoghq.com/api/unstable/debugger/frontend/probes')
21+
})
22+
23+
it('should build URL for EU1 site', () => {
24+
expect(buildDeliveryApiUrl('datadoghq.eu')).toBe('https://api.datadoghq.eu/api/unstable/debugger/frontend/probes')
25+
})
26+
27+
it('should build URL for US3 site', () => {
28+
expect(buildDeliveryApiUrl('us3.datadoghq.com')).toBe(
29+
'https://api.us3.datadoghq.com/api/unstable/debugger/frontend/probes'
30+
)
31+
})
32+
33+
it('should build URL for staging site', () => {
34+
expect(buildDeliveryApiUrl('datad0g.com')).toBe('https://api.datad0g.com/api/unstable/debugger/frontend/probes')
35+
})
36+
37+
it('should build URL for gov site', () => {
38+
expect(buildDeliveryApiUrl('ddog-gov.com')).toBe('https://api.ddog-gov.com/api/unstable/debugger/frontend/probes')
39+
})
40+
41+
it('should use proxy as origin when provided', () => {
42+
expect(buildDeliveryApiUrl('datadoghq.com', 'http://localhost:9000')).toBe(
43+
'http://localhost:9000/api/unstable/debugger/frontend/probes'
44+
)
45+
})
46+
47+
it('should ignore site when proxy is provided', () => {
48+
expect(buildDeliveryApiUrl('datadoghq.eu', 'http://proxy.example.com')).toBe(
49+
'http://proxy.example.com/api/unstable/debugger/frontend/probes'
50+
)
51+
})
52+
})
53+
954
describe('deliveryApi', () => {
1055
let fetchSpy: jasmine.Spy
1156
let clock: Clock
1257

1358
function makeConfig(overrides: Partial<DeliveryApiConfiguration> = {}): DeliveryApiConfiguration {
1459
return {
60+
clientToken: 'test-client-token',
1561
applicationId: 'test-app-id',
1662
env: 'staging',
1763
version: '1.0.0',
@@ -57,11 +103,19 @@ describe('deliveryApi', () => {
57103

58104
expect(fetchSpy).toHaveBeenCalledTimes(1)
59105
const [url, options] = fetchSpy.calls.mostRecent().args
60-
expect(url).toBe('/api/ui/debugger/probe-delivery')
106+
expect(url).toBe('https://api.datadoghq.com/api/unstable/debugger/frontend/probes')
61107
expect(options.method).toBe('POST')
62-
expect(options.credentials).toBe('same-origin')
108+
expect(options.credentials).toBeUndefined()
63109
expect(options.headers['Content-Type']).toBe('application/json; charset=utf-8')
64110
expect(options.headers['Accept']).toBe('application/vnd.datadog.debugger-probes+json; version=1')
111+
expect(options.headers['dd-client-token']).toBe('test-client-token')
112+
})
113+
114+
it('should use the configured site for the request URL', () => {
115+
startDeliveryApiPolling(makeConfig({ site: 'datadoghq.eu' }))
116+
117+
const [url] = fetchSpy.calls.mostRecent().args
118+
expect(url).toBe('https://api.datadoghq.eu/api/unstable/debugger/frontend/probes')
65119
})
66120

67121
it('should send the correct request body', () => {

packages/debugger/src/domain/deliveryApi.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
import type { TimeoutId } from '@datadog/browser-core'
2-
import { display, fetch, getGlobalObject, mockable, setInterval, clearInterval } from '@datadog/browser-core'
1+
import type { TimeoutId, Site } from '@datadog/browser-core'
2+
import {
3+
display,
4+
fetch,
5+
getGlobalObject,
6+
mockable,
7+
setInterval,
8+
clearInterval,
9+
INTAKE_SITE_US1,
10+
} from '@datadog/browser-core'
311
import { addProbe, removeProbe } from './probes'
412
import type { Probe } from './probes'
513

614
declare const __BUILD_ENV__SDK_VERSION__: string
715

8-
const DELIVERY_API_PATH = '/api/ui/debugger/probe-delivery'
9-
const DEFAULT_HEADERS: Record<string, string> = {
10-
'Content-Type': 'application/json; charset=utf-8',
11-
Accept: 'application/vnd.datadog.debugger-probes+json; version=1',
12-
}
16+
const DELIVERY_API_PATH = '/api/unstable/debugger/frontend/probes'
1317

1418
export interface DeliveryApiConfiguration {
19+
clientToken: string
20+
site?: Site
21+
proxy?: string
1522
applicationId: string
1623
env?: string
1724
version?: string
1825
pollInterval?: number
1926
}
2027

28+
export function buildDeliveryApiUrl(site: Site = INTAKE_SITE_US1, proxy?: string): string {
29+
if (proxy) {
30+
return `${proxy}${DELIVERY_API_PATH}`
31+
}
32+
return `https://api.${site}${DELIVERY_API_PATH}`
33+
}
34+
2135
interface DeliveryApiResponse {
2236
nextCursor: string
2337
updates: Probe[]
@@ -31,9 +45,8 @@ let knownProbeIds = new Set<string>()
3145
/**
3246
* Start polling the Datadog Delivery API for probe updates.
3347
*
34-
* This is designed for dogfooding the Live Debugger inside the Datadog web UI,
35-
* where the user is already authenticated via session cookies (ValidUser auth).
36-
* Requests are same-origin, so no explicit domain is needed.
48+
* Requests are authenticated via `dd-client-token` header (ClientTokenAuth)
49+
* against the public Smart Edge route.
3750
*/
3851
export function startDeliveryApiPolling(config: DeliveryApiConfiguration): void {
3952
if (!('location' in mockable(getGlobalObject)())) {
@@ -46,6 +59,12 @@ export function startDeliveryApiPolling(config: DeliveryApiConfiguration): void
4659
}
4760

4861
const pollInterval = config.pollInterval || 60_000
62+
const url = buildDeliveryApiUrl(config.site, config.proxy)
63+
const headers: Record<string, string> = {
64+
'Content-Type': 'application/json; charset=utf-8',
65+
Accept: 'application/vnd.datadog.debugger-probes+json; version=1',
66+
'dd-client-token': config.clientToken,
67+
}
4968

5069
const baseRequestBody = {
5170
applicationId: config.applicationId,
@@ -62,11 +81,10 @@ export function startDeliveryApiPolling(config: DeliveryApiConfiguration): void
6281
body.nextCursor = currentCursor
6382
}
6483

65-
const response = await fetch(DELIVERY_API_PATH, {
84+
const response = await fetch(url, {
6685
method: 'POST',
67-
headers: { ...DEFAULT_HEADERS },
86+
headers,
6887
body: JSON.stringify(body),
69-
credentials: 'same-origin',
7088
})
7189

7290
if (!response.ok) {

packages/debugger/src/entries/main.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ export interface DebuggerInitConfiguration {
6767
* @defaultValue 60000
6868
*/
6969
pollInterval?: number
70+
71+
/**
72+
* A proxy URL for routing SDK requests. When set, delivery API requests are
73+
* sent to `{proxy}/api/unstable/debugger/frontend/probes` instead of the
74+
* default Datadog API host derived from `site`.
75+
*
76+
* @category Transport
77+
*/
78+
proxy?: string
7079
}
7180

7281
/**
@@ -113,6 +122,9 @@ function makeDebuggerPublicApi(): DebuggerPublicApi {
113122
}
114123

115124
startDeliveryApiPolling({
125+
clientToken: initConfiguration.clientToken,
126+
site: initConfiguration.site,
127+
proxy: initConfiguration.proxy,
116128
applicationId: initConfiguration.applicationId,
117129
env: initConfiguration.env,
118130
version: initConfiguration.version,

test/e2e/lib/framework/httpServers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export type ServerApp = (req: http.IncomingMessage, res: http.ServerResponse) =>
1414

1515
export type MockServerApp = ServerApp & {
1616
getLargeResponseWroteSize(): number
17+
}
18+
19+
export type IntakeServerApp = ServerApp & {
1720
setDebuggerProbes(probes: object[]): void
1821
}
1922

@@ -26,7 +29,7 @@ export interface Server<App extends ServerApp> {
2629

2730
export interface Servers {
2831
base: Server<MockServerApp>
29-
intake: Server<ServerApp>
32+
intake: Server<IntakeServerApp>
3033
crossOrigin: Server<MockServerApp>
3134
}
3235

test/e2e/lib/framework/serverApps/intake.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@ import type { IntakeRegistry } from '../intakeRegistry'
55

66
export function createIntakeServerApp(intakeRegistry: IntakeRegistry) {
77
const app = express()
8+
let debuggerProbes: object[] = []
89

910
app.use(cors())
1011

1112
app.post('/', createIntakeProxyMiddleware({ onRequest: (request) => intakeRegistry.push(request) }))
1213

13-
return app
14+
app.post('/api/unstable/debugger/frontend/probes', (_req, res) => {
15+
res.json({ nextCursor: '', updates: debuggerProbes, deletions: [] })
16+
})
17+
18+
return Object.assign(app, {
19+
setDebuggerProbes(probes: object[]) {
20+
debuggerProbes = probes
21+
},
22+
})
1423
}

test/e2e/lib/framework/serverApps/mock.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export function createMockServerApp(servers: Servers, setup: string, setupOption
1515
const { remoteConfiguration, worker } = setupOptions ?? {}
1616
const app = express()
1717
let largeResponseBytesWritten = 0
18-
let debuggerProbes: object[] = []
1918

2019
app.use(cors())
2120
app.disable('etag') // disable automatic resource caching
@@ -228,17 +227,10 @@ export function createMockServerApp(servers: Servers, setup: string, setupOption
228227
res.send(JSON.stringify(remoteConfiguration))
229228
})
230229

231-
app.post('/api/ui/debugger/probe-delivery', (_req, res) => {
232-
res.json({ nextCursor: '', updates: debuggerProbes, deletions: [] })
233-
})
234-
235230
return Object.assign(app, {
236231
getLargeResponseWroteSize() {
237232
return largeResponseBytesWritten
238233
},
239-
setDebuggerProbes(probes: object[]) {
240-
debuggerProbes = probes
241-
},
242234
})
243235
}
244236

test/e2e/scenario/debugger.scenario.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createTest } from '../lib/framework'
55
import type { Servers } from '../lib/framework'
66

77
function setDebuggerProbes(servers: Servers, probes: object[]) {
8-
servers.base.app.setDebuggerProbes(probes)
8+
servers.intake.app.setDebuggerProbes(probes)
99
}
1010

1111
function makeProbe({

0 commit comments

Comments
 (0)