@carto/api-client 0.5.30-alpha.b195e7f.118 → 0.5.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "homepage": "https://github.com/CartoDB/carto-api-client#readme",
9
9
  "author": "Don McCurdy <donmccurdy@carto.com>",
10
10
  "packageManager": "yarn@4.3.1",
11
- "version": "0.5.30-alpha.b195e7f.118",
11
+ "version": "0.5.30",
12
12
  "license": "MIT",
13
13
  "publishConfig": {
14
14
  "access": "public"
@@ -0,0 +1,92 @@
1
+ /**
2
+ * How requests to the CARTO APIs are authenticated.
3
+ *
4
+ * - `'token'` (default): every request carries `Authorization: Bearer <accessToken>`.
5
+ * - `'session'`: requests carry NO Authorization header and are sent with
6
+ * `credentials: 'same-origin'`. Authentication is delegated to the server
7
+ * behind `apiBaseUrl` — typically a same-origin proxy that attaches the
8
+ * credential server-side from a session cookie. Used by CARTO hosted apps
9
+ * (`apiBaseUrl: '/app/_proxy/<slug>'`) and other single-origin deployments
10
+ * where the access token must never be exposed to JavaScript.
11
+ */
12
+ export type AuthMode = 'token' | 'session';
13
+
14
+ /**
15
+ * Authentication options shared by sources, queries, widget sources and
16
+ * fetchMap:
17
+ *
18
+ * - token mode (default): `accessToken` is required.
19
+ * - session mode: `authMode: 'session'` and no `accessToken`.
20
+ *
21
+ * Validated at request time by {@link buildAuthHeaders}.
22
+ */
23
+ export type AuthOptions = {
24
+ /** Carto platform access token. Required unless `authMode` is `'session'`. */
25
+ accessToken?: string;
26
+ /** @default 'token' */
27
+ authMode?: AuthMode;
28
+ };
29
+
30
+ /**
31
+ * Builds the Authorization headers for a request: a Bearer header in token
32
+ * mode, no auth headers at all in session mode (the server-side session
33
+ * middleware must see the header absent to engage). Also validates the
34
+ * options, so every request path fails fast on a misconfiguration.
35
+ */
36
+ export function buildAuthHeaders(options: AuthOptions): Record<string, string> {
37
+ if (options.authMode === 'session') {
38
+ if (options.accessToken) {
39
+ throw new Error(
40
+ `accessToken must not be provided with authMode: 'session' — ` +
41
+ `authentication is delegated to the server behind apiBaseUrl`
42
+ );
43
+ }
44
+ return {};
45
+ }
46
+ if (!options.accessToken) {
47
+ throw new Error(
48
+ `accessToken is required (or pass authMode: 'session' to authenticate ` +
49
+ `via a same-origin session instead)`
50
+ );
51
+ }
52
+ return {Authorization: `Bearer ${options.accessToken}`};
53
+ }
54
+
55
+ /**
56
+ * `fetch()` credentials for the given auth mode. Session mode must send the
57
+ * cookie explicitly; token mode preserves today's behavior (unset).
58
+ */
59
+ export function getAuthCredentials(
60
+ authMode?: AuthMode
61
+ ): RequestCredentials | undefined {
62
+ return authMode === 'session' ? 'same-origin' : undefined;
63
+ }
64
+
65
+ /**
66
+ * Rewrites an absolute CARTO API URL (e.g. a tilejson `tiles[]` template or
67
+ * the map-instantiation `dataUrl`, which always point at the tenant API host)
68
+ * onto the configured `apiBaseUrl`. In session mode the browser cannot reach
69
+ * the API host directly — every request must go through the same-origin
70
+ * proxy that `apiBaseUrl` points at. Relative URLs are returned unchanged.
71
+ */
72
+ export function rewriteUrlForSessionMode(
73
+ url: string,
74
+ apiBaseUrl: string
75
+ ): string {
76
+ let origin: string;
77
+ try {
78
+ const parsed = new URL(url);
79
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
80
+ return url;
81
+ }
82
+ origin = parsed.origin;
83
+ } catch {
84
+ // Relative URL — already same-origin, nothing to rewrite.
85
+ return url;
86
+ }
87
+ // Keep the path+query as a raw slice rather than re-serializing through
88
+ // URL: tile URL templates contain literal placeholders ({z}/{x}/{y}) that
89
+ // URL#pathname would percent-encode, breaking template substitution.
90
+ const base = apiBaseUrl.replace(/\/+$/, '');
91
+ return base + url.slice(origin.length);
92
+ }
package/src/api/index.ts CHANGED
@@ -2,6 +2,13 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ export {
6
+ buildAuthHeaders,
7
+ getAuthCredentials,
8
+ rewriteUrlForSessionMode,
9
+ type AuthMode,
10
+ type AuthOptions,
11
+ } from './auth.js';
5
12
  export {
6
13
  CartoAPIError,
7
14
  type APIErrorContext,
package/src/api/query.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  QueryResult,
10
10
  } from '../sources/types.js';
11
11
  import {buildQueryUrl} from './endpoints.js';
12
+ import {buildAuthHeaders, getAuthCredentials} from './auth.js';
12
13
  import {requestWithParameters} from './request-with-parameters.js';
13
14
  import type {APIErrorContext} from './carto-api-error.js';
14
15
  import {getClient} from '../client.js';
@@ -50,7 +51,7 @@ export const query = async function (
50
51
 
51
52
  const baseUrl = buildQueryUrl({apiBaseUrl, connectionName});
52
53
  const headers = {
53
- Authorization: `Bearer ${options.accessToken}`,
54
+ ...buildAuthHeaders(options),
54
55
  ...options.headers,
55
56
  };
56
57
  const parameters = {
@@ -74,5 +75,6 @@ export const query = async function (
74
75
  maxLengthURL,
75
76
  localCache,
76
77
  signal: options.signal,
78
+ credentials: getAuthCredentials(options.authMode),
77
79
  });
78
80
  };
@@ -24,6 +24,7 @@ export async function requestWithParameters<T = any>({
24
24
  maxLengthURL = DEFAULT_MAX_LENGTH_URL,
25
25
  localCache,
26
26
  signal,
27
+ credentials,
27
28
  }: {
28
29
  baseUrl: string;
29
30
  parameters?: Record<string, unknown>;
@@ -32,6 +33,8 @@ export async function requestWithParameters<T = any>({
32
33
  maxLengthURL?: number;
33
34
  localCache?: LocalCacheOptions;
34
35
  signal?: AbortSignal;
36
+ /** Forwarded to `fetch()`. Session auth mode passes 'same-origin'. */
37
+ credentials?: RequestCredentials;
35
38
  }): Promise<T> {
36
39
  // Parameters added to all requests issued with `requestWithParameters()`.
37
40
  // These parameters override parameters already in the base URL, but not
@@ -68,8 +71,9 @@ export async function requestWithParameters<T = any>({
68
71
  body: JSON.stringify(parameters),
69
72
  headers,
70
73
  signal,
74
+ ...(credentials && {credentials}),
71
75
  })
72
- : fetch(url, {headers, signal});
76
+ : fetch(url, {headers, signal, ...(credentials && {credentials})});
73
77
 
74
78
  let response: Response | undefined;
75
79
  let responseJson: unknown;
@@ -132,6 +136,29 @@ function createCacheKey(
132
136
  });
133
137
  }
134
138
 
139
+ /**
140
+ * Base URLs may be relative (e.g. a same-origin proxy like
141
+ * '/app/_proxy/<slug>/v3/...' used with session auth mode). `new URL()`
142
+ * requires an origin, so relative URLs are resolved against a marker origin
143
+ * that is stripped back off before returning.
144
+ */
145
+ const RELATIVE_ORIGIN = 'https://relative.invalid';
146
+
147
+ function parseBaseUrl(baseUrlString: string): {url: URL; isRelative: boolean} {
148
+ // Protocol-relative URLs ('//host/...') carry a host and must not be treated
149
+ // as relative, or the host would be lost when resolved against RELATIVE_ORIGIN.
150
+ const isRelative =
151
+ baseUrlString.startsWith('/') && !baseUrlString.startsWith('//');
152
+ return {
153
+ url: new URL(baseUrlString, isRelative ? RELATIVE_ORIGIN : undefined),
154
+ isRelative,
155
+ };
156
+ }
157
+
158
+ function serializeBaseUrl(url: URL, isRelative: boolean): string {
159
+ return isRelative ? `${url.pathname}${url.search}` : url.toString();
160
+ }
161
+
135
162
  /**
136
163
  * Appends query string parameters to a URL. Existing URL parameters are kept,
137
164
  * unless there is a conflict, in which case the new parameters override
@@ -141,7 +168,7 @@ function createURLWithParameters(
141
168
  baseUrlString: string,
142
169
  parameters: Record<string, unknown>
143
170
  ): string {
144
- const baseUrl = new URL(baseUrlString);
171
+ const {url: baseUrl, isRelative} = parseBaseUrl(baseUrlString);
145
172
  for (const [key, value] of Object.entries(parameters)) {
146
173
  if (isPureObject(value) || Array.isArray(value)) {
147
174
  baseUrl.searchParams.set(key, JSON.stringify(value));
@@ -154,20 +181,20 @@ function createURLWithParameters(
154
181
  }
155
182
  }
156
183
  }
157
- return baseUrl.toString();
184
+ return serializeBaseUrl(baseUrl, isRelative);
158
185
  }
159
186
 
160
187
  /**
161
188
  * Deletes query string parameters from a URL.
162
189
  */
163
190
  function excludeURLParameters(baseUrlString: string, parameters: string[]) {
164
- const baseUrl = new URL(baseUrlString);
191
+ const {url: baseUrl, isRelative} = parseBaseUrl(baseUrlString);
165
192
  for (const param of parameters) {
166
193
  if (baseUrl.searchParams.has(param)) {
167
194
  baseUrl.searchParams.delete(param);
168
195
  }
169
196
  }
170
- return baseUrl.toString();
197
+ return serializeBaseUrl(baseUrl, isRelative);
171
198
  }
172
199
 
173
200
  /**
@@ -754,7 +754,13 @@ function createChannelProps(
754
754
  };
755
755
  }
756
756
 
757
- function createLoadOptions(accessToken: string) {
757
+ function createLoadOptions(accessToken?: string) {
758
+ // No token (e.g. a source created with authMode: 'session', where tile URLs
759
+ // are same-origin and authenticated by a session cookie): attach no
760
+ // Authorization header rather than a literal 'Bearer undefined'.
761
+ if (!accessToken) {
762
+ return {loadOptions: {fetch: {credentials: 'same-origin'}}};
763
+ }
758
764
  return {
759
765
  loadOptions: {fetch: {headers: {Authorization: `Bearer ${accessToken}`}}},
760
766
  };
package/src/index.ts CHANGED
@@ -18,6 +18,11 @@ export * from './types.js';
18
18
  export {
19
19
  type APIErrorContext,
20
20
  type APIRequestType,
21
+ type AuthMode,
22
+ type AuthOptions,
23
+ buildAuthHeaders,
24
+ getAuthCredentials,
25
+ rewriteUrlForSessionMode,
21
26
  CartoAPIError,
22
27
  type QueryOptions,
23
28
  buildPublicMapUrl, // Internal, but required for fetchMap().
@@ -28,6 +33,7 @@ export {
28
33
  } from './api/index.js';
29
34
 
30
35
  export {_getHexagonResolution} from './spatial-index.js';
36
+ export {getPointsAggregationLevel as _getPointsAggregationLevel} from './spatial-index.js';
31
37
 
32
38
  // For unit testing only.
33
39
  export * from './filters/index.js';
@@ -1,4 +1,9 @@
1
1
  import {InvalidColumnError} from '../utils.js';
2
+ import {
3
+ buildAuthHeaders,
4
+ getAuthCredentials,
5
+ type AuthMode,
6
+ } from '../api/auth.js';
2
7
 
3
8
  /** @privateRemarks Source: @carto/react-api */
4
9
  export interface ModelRequestOptions {
@@ -55,10 +60,12 @@ export function dealWithApiError({
55
60
  export async function makeCall({
56
61
  url,
57
62
  accessToken,
63
+ authMode,
58
64
  opts,
59
65
  }: {
60
66
  url: string;
61
- accessToken: string;
67
+ accessToken?: string;
68
+ authMode?: AuthMode;
62
69
  opts: ModelRequestOptions;
63
70
  }) {
64
71
  let response;
@@ -67,10 +74,13 @@ export async function makeCall({
67
74
  try {
68
75
  response = await fetch(url.toString(), {
69
76
  headers: {
70
- Authorization: `Bearer ${accessToken}`,
77
+ ...buildAuthHeaders({accessToken, authMode}),
71
78
  ...(isPost && {'Content-Type': 'application/json'}),
72
79
  ...opts.headers,
73
80
  },
81
+ ...(getAuthCredentials(authMode) && {
82
+ credentials: getAuthCredentials(authMode),
83
+ }),
74
84
  ...(isPost && {
75
85
  method: opts?.method,
76
86
  body: opts?.body,
@@ -1,4 +1,5 @@
1
1
  import {DEFAULT_GEO_COLUMN} from '../constants-internal.js';
2
+ import type {AuthMode} from '../api/auth.js';
2
3
  import type {
3
4
  Filter,
4
5
  FilterLogicalOperator,
@@ -33,7 +34,8 @@ export interface ModelSource {
33
34
  type: MapType;
34
35
  apiVersion: ApiVersion;
35
36
  apiBaseUrl: string;
36
- accessToken: string;
37
+ accessToken?: string;
38
+ authMode?: AuthMode;
37
39
  clientId: string;
38
40
  connectionName: string;
39
41
  data: string;
@@ -72,11 +74,12 @@ export function executeModel(props: {
72
74
  );
73
75
 
74
76
  const {model, source, params, opts} = props;
75
- const {type, apiVersion, apiBaseUrl, accessToken, connectionName, clientId} =
76
- source;
77
+ const {type, apiVersion, apiBaseUrl, connectionName, clientId} = source;
77
78
 
78
79
  assert(apiBaseUrl, 'executeModel: missing apiBaseUrl');
79
- assert(accessToken, 'executeModel: missing accessToken');
80
+ // Auth (token vs session mode) is validated centrally by buildAuthHeaders,
81
+ // invoked from makeCall below — no separate accessToken assertion here, so
82
+ // that session mode (no accessToken) is not rejected before the request.
80
83
  assert(apiVersion === V3, 'executeModel: SQL Model API requires CARTO 3+');
81
84
  assert(type !== 'tileset', 'executeModel: Tilesets not supported');
82
85
 
@@ -123,6 +126,7 @@ export function executeModel(props: {
123
126
  return makeCall({
124
127
  url,
125
128
  accessToken: source.accessToken,
129
+ authMode: source.authMode,
126
130
  opts: {
127
131
  ...opts,
128
132
  method: isGet ? 'GET' : 'POST',
@@ -5,6 +5,11 @@
5
5
  import {DEFAULT_API_BASE_URL} from '../constants.js';
6
6
  import {DEFAULT_MAX_LENGTH_URL} from '../constants-internal.js';
7
7
  import {buildSourceUrl} from '../api/endpoints.js';
8
+ import {
9
+ buildAuthHeaders,
10
+ getAuthCredentials,
11
+ rewriteUrlForSessionMode,
12
+ } from '../api/auth.js';
8
13
  import {requestWithParameters} from '../api/request-with-parameters.js';
9
14
  import type {
10
15
  SourceOptionalOptions,
@@ -43,10 +48,12 @@ export async function baseSource<UrlParameters extends Record<string, unknown>>(
43
48
  }
44
49
  const baseUrl = buildSourceUrl(mergedOptions);
45
50
  const {clientId, maxLengthURL, localCache} = mergedOptions;
51
+ const sessionMode = options.authMode === 'session';
46
52
  const headers = {
47
- Authorization: `Bearer ${options.accessToken}`,
53
+ ...buildAuthHeaders(options),
48
54
  ...options.headers,
49
55
  };
56
+ const credentials = getAuthCredentials(options.authMode);
50
57
  const parameters = {client: clientId, ...options.tags, ...urlParameters};
51
58
 
52
59
  const errorContext: APIErrorContext = {
@@ -63,15 +70,22 @@ export async function baseSource<UrlParameters extends Record<string, unknown>>(
63
70
  errorContext,
64
71
  maxLengthURL,
65
72
  localCache,
73
+ credentials,
66
74
  });
67
75
 
68
- const dataUrl = tilejson.url[0];
76
+ let dataUrl = tilejson.url[0];
69
77
  if (cache) {
70
78
  cache.value = parseInt(
71
79
  new URL(dataUrl).searchParams.get('cache') || '',
72
80
  10
73
81
  );
74
82
  }
83
+ if (sessionMode) {
84
+ // The instantiation response points at the tenant API host, which the
85
+ // browser cannot reach directly in session mode — route it through the
86
+ // same-origin proxy behind apiBaseUrl.
87
+ dataUrl = rewriteUrlForSessionMode(dataUrl, mergedOptions.apiBaseUrl);
88
+ }
75
89
  errorContext.requestType = 'Map data';
76
90
 
77
91
  const json = await requestWithParameters<TilejsonResult>({
@@ -81,8 +95,17 @@ export async function baseSource<UrlParameters extends Record<string, unknown>>(
81
95
  errorContext,
82
96
  maxLengthURL,
83
97
  localCache,
98
+ credentials,
84
99
  });
85
- if (accessToken) {
100
+ if (sessionMode) {
101
+ // Tile URL templates also point at the tenant API host. Rewrite them onto
102
+ // the proxy, and deliberately leave `json.accessToken` unset: consumers
103
+ // (e.g. deck.gl tile layers) must not attach an Authorization header —
104
+ // the session credential rides on the same-origin cookie instead.
105
+ json.tiles = json.tiles?.map((template) =>
106
+ rewriteUrlForSessionMode(template, mergedOptions.apiBaseUrl)
107
+ );
108
+ } else if (accessToken) {
86
109
  json.accessToken = accessToken;
87
110
  }
88
111
  if (schema) {
@@ -3,12 +3,10 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
5
  import type {Filters, SchemaField, QueryParameters} from '../types.js';
6
+ import type {AuthOptions} from '../api/auth.js';
6
7
  import type {RasterBandColorinterp} from './constants.js';
7
8
 
8
- export type SourceRequiredOptions = {
9
- /** Carto platform access token. */
10
- accessToken: string;
11
-
9
+ export type SourceRequiredOptions = AuthOptions & {
12
10
  /** Data warehouse connection name in Carto platform. */
13
11
  connectionName: string;
14
12
  };
@@ -466,7 +464,13 @@ export type RasterMetadata = {
466
464
  };
467
465
 
468
466
  export type TilejsonResult = Tilejson & {
469
- accessToken: string;
467
+ /**
468
+ * Access token used to fetch the tiles. Absent when the source was created
469
+ * with `authMode: 'session'` — tile URLs are then rewritten onto the
470
+ * configured `apiBaseUrl` and authenticated by the server (session cookie),
471
+ * so consumers must not attach an Authorization header.
472
+ */
473
+ accessToken?: string;
470
474
  schema: SchemaField[];
471
475
  };
472
476
 
@@ -1,3 +1,5 @@
1
+ import type {TileResolution} from './sources/types.js';
2
+
1
3
  // Default tile display size in deck.gl, in viewport pixels. May differ
2
4
  // from size or resolution assumed when generating the tile data,
3
5
  const DEFAULT_TILE_SIZE = 512;
@@ -30,3 +32,42 @@ export function _getHexagonResolution(
30
32
  Math.floor(hexagonScaleFactor + latitudeScaleFactor - BIAS)
31
33
  );
32
34
  }
35
+
36
+ // Default of the maps-api setting MAPS_API_V3_DYNAMIC_TILES_POINTS_AGGREGATION_LEVEL.
37
+ const DYNAMIC_TILES_POINTS_AGGREGATION_LEVEL = 8;
38
+
39
+ // Per-tileResolution correction, identical to the maps-api dynamic tiler.
40
+ const AGG_LEVEL_CORRECTION_BY_TILE_RESOLUTION: Record<TileResolution, number> =
41
+ {
42
+ 0.25: -1,
43
+ 0.5: 0,
44
+ 1: 1,
45
+ 2: 2,
46
+ 4: 3,
47
+ };
48
+
49
+ /**
50
+ * Quadbin level at which the maps-api dynamic tiler implicitly aggregates a
51
+ * point source for a given tile zoom and resolution. Lets a client reproduce
52
+ * that level — and so the aggregation cell a point falls into — without an
53
+ * extra round-trip to the server.
54
+ *
55
+ * Ported verbatim from the maps-api dynamic tiler (`getPointsAggregationLevel`).
56
+ * The base offset mirrors the server default of
57
+ * `MAPS_API_V3_DYNAMIC_TILES_POINTS_AGGREGATION_LEVEL`; a deployment that
58
+ * overrides that env var drifts from this computation.
59
+ * @internal
60
+ */
61
+ export function getPointsAggregationLevel({
62
+ tileResolution,
63
+ zoomLevel,
64
+ }: {
65
+ tileResolution: TileResolution;
66
+ zoomLevel: number;
67
+ }): number {
68
+ return (
69
+ zoomLevel +
70
+ DYNAMIC_TILES_POINTS_AGGREGATION_LEVEL +
71
+ AGG_LEVEL_CORRECTION_BY_TILE_RESOLUTION[tileResolution]
72
+ );
73
+ }
package/src/types.ts CHANGED
@@ -116,21 +116,13 @@ export type AggregationType =
116
116
  export type GeometrySpatialFilter = Polygon | MultiPolygon;
117
117
 
118
118
  /**
119
- * Filter restricting a widget query to a set of spatial-index cells (H3 or
120
- * Quadbin). Used for point-and-click selection on cell-rendered layers. Sent to
121
- * the server-side model API only; maps-api chooses the SQL strategy from
122
- * `spatialDataType`, so the same payload works for both natively-indexed
123
- * sources and point/geometry sources dynamically aggregated into cells.
124
- *
125
- * @privateRemarks Source: @carto/query-builder (SpatialIndexFilter)
119
+ * Filter restricting a widget query to a selection of spatial-index cells,
120
+ * e.g. from point-and-click on an H3 or Quadbin layer.
126
121
  */
127
122
  export interface SpatialIndexFilter {
128
- /**
129
- * Selected cell ids. h3 = hex string; h3int = `BigInt(\`0x${id}\`)`; quadbin
130
- * = decimal or `0x`-hex string.
131
- */
123
+ /** Selected cell ids. */
132
124
  indexes: string[];
133
- /** Must match the column type; `h3` and `h3int` are interchangeable. */
125
+ /** Spatial index type of the filtered column. */
134
126
  type: 'h3' | 'h3int' | 'quadbin';
135
127
  }
136
128
 
@@ -141,7 +133,14 @@ export type SpatialFilter = GeometrySpatialFilter | SpatialIndexFilter;
141
133
  export function isSpatialIndexFilter(
142
134
  spatialFilter: SpatialFilter
143
135
  ): spatialFilter is SpatialIndexFilter {
144
- return Array.isArray((spatialFilter as SpatialIndexFilter).indexes);
136
+ const filter = spatialFilter as
137
+ | Partial<SpatialIndexFilter>
138
+ | null
139
+ | undefined;
140
+ return (
141
+ Array.isArray(filter?.indexes) &&
142
+ ['h3', 'h3int', 'quadbin'].includes(filter?.type as string)
143
+ );
145
144
  }
146
145
 
147
146
  /** @privateRemarks Source: @deck.gl/carto */
@@ -30,6 +30,7 @@ import {AggregationTypes, ApiVersion} from '../constants.js';
30
30
  import {getApplicableFilters} from '../filters.js';
31
31
  import {OTHERS_CATEGORY_NAME} from './constants.js';
32
32
  import {requestWithParameters} from '../api/request-with-parameters.js';
33
+ import {buildAuthHeaders, getAuthCredentials} from '../api/auth.js';
33
34
  import type {APIErrorContext} from '../api/carto-api-error.js';
34
35
 
35
36
  export type WidgetRemoteSourceProps = WidgetSourceProps;
@@ -88,6 +89,7 @@ export abstract class WidgetRemoteSource<
88
89
  apiBaseUrl: props.apiBaseUrl as string,
89
90
  clientId: props.clientId as string,
90
91
  accessToken: props.accessToken,
92
+ authMode: props.authMode,
91
93
  connectionName: props.connectionName,
92
94
  filters: getApplicableFilters(filterOwner, filters || props.filters),
93
95
  filtersLogicalOperator: props.filtersLogicalOperator,
@@ -486,7 +488,7 @@ export abstract class WidgetRemoteSource<
486
488
  }
487
489
 
488
490
  const headers = {
489
- Authorization: `Bearer ${this.props.accessToken}`,
491
+ ...buildAuthHeaders(this.props),
490
492
  ...this.props.headers,
491
493
  };
492
494
 
@@ -511,6 +513,7 @@ export abstract class WidgetRemoteSource<
511
513
  signal,
512
514
  errorContext,
513
515
  parameters,
516
+ credentials: getAuthCredentials(this.props.authMode),
514
517
  }).then(({extent: {xmin, ymin, xmax, ymax}}) => ({
515
518
  bbox: [xmin, ymin, xmax, ymax],
516
519
  }));