Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): Add sentry_client to auth headers #5413

Merged
merged 3 commits into from Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/browser/src/client.ts
Expand Up @@ -161,7 +161,7 @@ export class BrowserClient extends BaseClient<BrowserClientOptions> {

__DEBUG_BUILD__ && logger.log('Sending outcomes:', outcomes);

const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, this._options.tunnel);
const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, this._options);
const envelope = createClientReportEnvelope(outcomes, this._options.tunnel && dsnToString(this._dsn));

try {
Expand Down
22 changes: 18 additions & 4 deletions packages/core/src/api.ts
@@ -1,4 +1,4 @@
import { DsnComponents, DsnLike } from '@sentry/types';
import { ClientOptions, DsnComponents, DsnLike, SdkInfo } from '@sentry/types';
import { dsnToString, makeDsn, urlEncode } from '@sentry/utils';

const SENTRY_API_VERSION = '7';
Expand All @@ -16,12 +16,13 @@ function _getIngestEndpoint(dsn: DsnComponents): string {
}

/** Returns a URL-encoded string with auth config suitable for a query string. */
function _encodedAuth(dsn: DsnComponents): string {
function _encodedAuth(dsn: DsnComponents, sdkInfo: SdkInfo | undefined): string {
return urlEncode({
// We send only the minimum set of required information. See
// https://github.com/getsentry/sentry-javascript/issues/2572.
sentry_key: dsn.publicKey,
sentry_version: SENTRY_API_VERSION,
...(sdkInfo && { sentry_client: `${sdkInfo.name}/${sdkInfo.version}` }),
});
}

Expand All @@ -30,8 +31,21 @@ function _encodedAuth(dsn: DsnComponents): string {
*
* Sending auth as part of the query string and not as custom HTTP headers avoids CORS preflight requests.
*/
export function getEnvelopeEndpointWithUrlEncodedAuth(dsn: DsnComponents, tunnel?: string): string {
return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn)}`;
export function getEnvelopeEndpointWithUrlEncodedAuth(
dsn: DsnComponents,
// TODO (v8): Remove `tunnelOrOptions` in favor of `options`, and use the substitute code below
// options: ClientOptions = {} as ClientOptions,
tunnelOrOptions: string | ClientOptions = {} as ClientOptions,
): string {
// TODO (v8): Use this code instead
// const { tunnel, _metadata = {} } = options;
// return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn, _metadata.sdk)}`;

const tunnel = typeof tunnelOrOptions === 'string' ? tunnelOrOptions : tunnelOrOptions.tunnel;
const sdkInfo =
typeof tunnelOrOptions === 'string' || !tunnelOrOptions._metadata ? undefined : tunnelOrOptions._metadata.sdk;

return tunnel ? tunnel : `${_getIngestEndpoint(dsn)}?${_encodedAuth(dsn, sdkInfo)}`;
}

/** Returns the url to the report dialog endpoint. */
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/baseclient.ts
Expand Up @@ -104,7 +104,7 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
this._options = options;
if (options.dsn) {
this._dsn = makeDsn(options.dsn);
const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel);
const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options);
this._transport = options.transport({
recordDroppedEvent: this.recordDroppedEvent.bind(this),
...options.transportOptions,
Expand Down
43 changes: 39 additions & 4 deletions packages/core/test/lib/api.test.ts
@@ -1,20 +1,55 @@
/* eslint-disable deprecation/deprecation */
import { ClientOptions, DsnComponents } from '@sentry/types';
import { makeDsn } from '@sentry/utils';

import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from '../../src/api';

const ingestDsn = 'https://abc@xxxx.ingest.sentry.io:1234/subpath/123';
const dsnPublic = 'https://abc@sentry.io:1234/subpath/123';
const tunnel = 'https://hello.com/world';
const _metadata = { sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } } as ClientOptions['_metadata'];

const dsnPublicComponents = makeDsn(dsnPublic);

describe('API', () => {
test('getEnvelopeEndpoint', () => {
expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicComponents)).toEqual(
'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7',
describe('getEnvelopeEndpointWithUrlEncodedAuth', () => {
it.each([
[
"doesn't include `sentry_client` when called with only DSN",
dsnPublicComponents,
undefined,
'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7',
],
['uses `tunnel` value when called with `tunnel` as string', dsnPublicComponents, tunnel, tunnel],
[
'uses `tunnel` value when called with `tunnel` in options',
dsnPublicComponents,
{ tunnel } as ClientOptions,
tunnel,
],
[
'uses `tunnel` value when called with `tunnel` and `_metadata` in options',
dsnPublicComponents,
{ tunnel, _metadata } as ClientOptions,
tunnel,
],
[
'includes `sentry_client` when called with `_metadata` in options and no tunnel',
dsnPublicComponents,
{ _metadata } as ClientOptions,
'https://sentry.io:1234/subpath/api/123/envelope/?sentry_key=abc&sentry_version=7&sentry_client=sentry.javascript.browser%2F12.31.12',
],
])(
'%s',
(
_testName: string,
dsnComponents: DsnComponents,
tunnelOrOptions: string | ClientOptions | undefined,
expected: string,
) => {
expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnComponents, tunnelOrOptions)).toBe(expected);
},
);
expect(getEnvelopeEndpointWithUrlEncodedAuth(dsnPublicComponents, tunnel)).toEqual(tunnel);
});

describe('getReportDialogEndpoint', () => {
Expand Down