Skip to content

Commit a396f7e

Browse files
authored
Merge pull request #12530 from CesiumGS/itwin-share-key
Support iTwin share keys
2 parents 9aba485 + 43bcc41 commit a396f7e

File tree

7 files changed

+136
-27
lines changed

7 files changed

+136
-27
lines changed

Apps/Sandcastle/gallery/iModel Mesh Export Service.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@
3131
window.startup = async function (Cesium) {
3232
"use strict";
3333
//Sandcastle_Begin
34-
const serviceResponse = await fetch("https://api.cesium.com/itwin/token");
35-
const { access_token: token } = await serviceResponse.json();
34+
// Generate a share key for access to an iTwin without OAuth
35+
// https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-share/
36+
Cesium.ITwinPlatform.defaultShareKey =
37+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpVHdpbklkIjoiNTM1YTI0YTMtOWIyOS00ZTIzLWJiNWQtOWNlZGI1MjRjNzQzIiwiaWQiOiJmZTliOTgyMS0wYWI5LTQ4ZjItYmMzOC01NjQ5MTg5MDQyOTEiLCJleHAiOjE3NDQ5OTA2MjZ9.7bQIX32CGE4slLDPkOQZ4rRr4BqBSjbUOFAUvcdrFXE";
3638

37-
Cesium.ITwinPlatform.defaultAccessToken = token;
39+
// For alternative forms of authentication you can use, visit https://developer.bentley.com/apis/overview/authorization/. Then set your access token like this:
40+
// Cesium.ITwinPlatform.defaultAccessToken = 'your token'
3841

3942
// Set up viewer
4043
const viewer = new Cesium.Viewer("cesiumContainer", {

Apps/Sandcastle/gallery/iTwin Feature Service.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@
2929
window.startup = async function (Cesium) {
3030
"use strict";
3131
//Sandcastle_Begin
32-
const serviceResponse = await fetch("https://api.cesium.com/itwin/token");
33-
const { access_token: token } = await serviceResponse.json();
32+
// Generate a share key for access to an iTwin without OAuth
33+
// https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-share/
34+
Cesium.ITwinPlatform.defaultShareKey =
35+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpVHdpbklkIjoiMDRiYTcyNWYtZjNjMC00ZjMwLTgwMTQtYTQ0ODhjYmQ2MTJkIiwiaWQiOiI3ZDI2MTQ2YS1mMWE4LTRmYzYtODI5Ny05ODA4ZGRlZDdkOTciLCJleHAiOjE3NDQ5OTAzNzl9.w8B077BL6TJ8Ohit9Pzyw8DlqicKDVBMwgcE0ujLOBQ";
3436

35-
Cesium.ITwinPlatform.defaultAccessToken = token;
37+
// For alternative forms of authentication you can use, visit https://developer.bentley.com/apis/overview/authorization/. Then set your access token like this:
38+
// Cesium.ITwinPlatform.defaultAccessToken = 'your token'
3639

3740
const iTwinId = "04ba725f-f3c0-4f30-8014-a4488cbd612d";
3841

CHANGES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
#### Breaking Changes :mega:
88

9-
- `Camera.getPickRay` was erroneous returning a result in camera coordinates. It is now returned in world coordinates as stated in the documentation. The result can be transformed using `Camera.inverseViewMatrix` to achieve the previous behavior.
9+
- `Camera.getPickRay` was erroneously returning a result in camera coordinates. It is now returned in world coordinates as stated in the documentation. The result can be transformed using `Camera.inverseViewMatrix` to achieve the previous behavior.
10+
11+
#### Additions :tada:
12+
13+
- Add support for loading iTwin data using share keys as an alternative to user-based OAuth. When using a share key, set `ITwinPlatform.defaultShareKey`. [#12530](https://github.com/CesiumGS/cesium/pull/12530)
1014

1115
#### Deprecated :hourglass_flowing_sand:
1216

packages/engine/Source/Core/ITwinPlatform.js

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,51 @@ ITwinPlatform.RealityDataType = Object.freeze({
5656
/**
5757
* Gets or sets the default iTwin access token. This token should have the <code>itwin-platform</code> scope.
5858
*
59+
* This value will be ignored if {@link ITwinPlatform.defaultShareKey} is defined.
60+
*
5961
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
6062
*
6163
* @type {string|undefined}
6264
*/
6365
ITwinPlatform.defaultAccessToken = undefined;
6466

67+
/**
68+
* Gets or sets the default iTwin share key. If this value is provided it will override {@link ITwinPlatform.defaultAccessToken} in all requests.
69+
*
70+
* Share keys can be generated using the iTwin Shares api
71+
* https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-share/
72+
*
73+
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
74+
*
75+
* @type {string|undefined}
76+
*/
77+
ITwinPlatform.defaultShareKey = undefined;
78+
79+
/**
80+
* Create the necessary Authorization header based on which key/token is set.
81+
* If the {@link ITwinPlatform.defaultShareKey} is set it takes precedence and
82+
* will be used regardless if the {@link ITwinPlatform.defaultAccessToken} is set
83+
* @private
84+
* @returns {string} full auth header with basic/bearer method
85+
*/
86+
ITwinPlatform._getAuthorizationHeader = function () {
87+
//>>includeStart('debug', pragmas.debug);
88+
if (
89+
!defined(ITwinPlatform.defaultAccessToken) &&
90+
!defined(ITwinPlatform.defaultShareKey)
91+
) {
92+
throw new DeveloperError(
93+
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
94+
);
95+
}
96+
//>>includeEnd('debug');
97+
98+
if (defined(ITwinPlatform.defaultShareKey)) {
99+
return `Basic ${ITwinPlatform.defaultShareKey}`;
100+
}
101+
return `Bearer ${ITwinPlatform.defaultAccessToken}`;
102+
};
103+
65104
/**
66105
* Gets or sets the default iTwin API endpoint.
67106
*
@@ -121,15 +160,20 @@ ITwinPlatform.apiEndpoint = new Resource({
121160
ITwinPlatform.getExports = async function (iModelId) {
122161
//>>includeStart('debug', pragmas.debug);
123162
Check.typeOf.string("iModelId", iModelId);
124-
if (!defined(ITwinPlatform.defaultAccessToken)) {
125-
throw new DeveloperError("Must set ITwinPlatform.defaultAccessToken first");
163+
if (
164+
!defined(ITwinPlatform.defaultAccessToken) &&
165+
!defined(ITwinPlatform.defaultShareKey)
166+
) {
167+
throw new DeveloperError(
168+
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
169+
);
126170
}
127171
//>>includeEnd('debug');
128172

129173
const resource = new Resource({
130174
url: `${ITwinPlatform.apiEndpoint}mesh-export`,
131175
headers: {
132-
Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`,
176+
Authorization: ITwinPlatform._getAuthorizationHeader(),
133177
Accept: "application/vnd.bentley.itwin-platform.v1+json",
134178
Prefer: "return=representation",
135179
},
@@ -213,15 +257,20 @@ ITwinPlatform.getRealityDataMetadata = async function (iTwinId, realityDataId) {
213257
//>>includeStart('debug', pragmas.debug);
214258
Check.typeOf.string("iTwinId", iTwinId);
215259
Check.typeOf.string("realityDataId", realityDataId);
216-
if (!defined(ITwinPlatform.defaultAccessToken)) {
217-
throw new DeveloperError("Must set ITwinPlatform.defaultAccessToken first");
260+
if (
261+
!defined(ITwinPlatform.defaultAccessToken) &&
262+
!defined(ITwinPlatform.defaultShareKey)
263+
) {
264+
throw new DeveloperError(
265+
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
266+
);
218267
}
219268
//>>includeEnd('debug');
220269

221270
const resource = new Resource({
222271
url: `${ITwinPlatform.apiEndpoint}reality-management/reality-data/${realityDataId}`,
223272
headers: {
224-
Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`,
273+
Authorization: ITwinPlatform._getAuthorizationHeader(),
225274
Accept: "application/vnd.bentley.itwin-platform.v1+json",
226275
},
227276
queryParameters: { iTwinId: iTwinId },
@@ -275,15 +324,20 @@ ITwinPlatform.getRealityDataURL = async function (
275324
Check.typeOf.string("iTwinId", iTwinId);
276325
Check.typeOf.string("realityDataId", realityDataId);
277326
Check.typeOf.string("rootDocument", rootDocument);
278-
if (!defined(ITwinPlatform.defaultAccessToken)) {
279-
throw new DeveloperError("Must set ITwinPlatform.defaultAccessToken first");
327+
if (
328+
!defined(ITwinPlatform.defaultAccessToken) &&
329+
!defined(ITwinPlatform.defaultShareKey)
330+
) {
331+
throw new DeveloperError(
332+
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
333+
);
280334
}
281335
//>>includeEnd('debug');
282336

283337
const resource = new Resource({
284338
url: `${ITwinPlatform.apiEndpoint}reality-management/reality-data/${realityDataId}/readaccess`,
285339
headers: {
286-
Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`,
340+
Authorization: ITwinPlatform._getAuthorizationHeader(),
287341
Accept: "application/vnd.bentley.itwin-platform.v1+json",
288342
},
289343
queryParameters: { iTwinId: iTwinId },

packages/engine/Source/Scene/ITwinData.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,13 @@ ITwinData.loadGeospatialFeatures = async function (
227227
Check.typeOf.number.lessThanOrEquals("limit", limit, 10000);
228228
Check.typeOf.number.greaterThanOrEquals("limit", limit, 1);
229229
}
230-
if (!defined(ITwinPlatform.defaultAccessToken)) {
231-
throw new DeveloperError("Must set ITwinPlatform.defaultAccessToken first");
230+
if (
231+
!defined(ITwinPlatform.defaultAccessToken) &&
232+
!defined(ITwinPlatform.defaultShareKey)
233+
) {
234+
throw new DeveloperError(
235+
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
236+
);
232237
}
233238
//>>includeEnd('debug');
234239

@@ -239,7 +244,7 @@ ITwinData.loadGeospatialFeatures = async function (
239244
const resource = new Resource({
240245
url: tilesetUrl,
241246
headers: {
242-
Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`,
247+
Authorization: ITwinPlatform._getAuthorizationHeader(),
243248
Accept: "application/vnd.bentley.itwin-platform.v1+json",
244249
},
245250
queryParameters: {

packages/engine/Specs/Core/ITwinPlatformSpec.js

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,50 @@ import {
77

88
describe("ITwinPlatform", () => {
99
let previousAccessToken;
10+
let previousShareKey;
1011
beforeEach(() => {
1112
previousAccessToken = ITwinPlatform.defaultAccessToken;
1213
ITwinPlatform.defaultAccessToken = "default-access-token";
14+
previousShareKey = ITwinPlatform.defaultShareKey;
15+
ITwinPlatform.defaultShareKey = undefined;
1316
});
1417

1518
afterEach(() => {
1619
ITwinPlatform.defaultAccessToken = previousAccessToken;
20+
ITwinPlatform.defaultShareKey = previousShareKey;
21+
});
22+
23+
describe("_getAuthorizationHeader", () => {
24+
it("rejects with no default access token or default share key set", async () => {
25+
ITwinPlatform.defaultAccessToken = undefined;
26+
ITwinPlatform.defaultShareKey = undefined;
27+
expect(() =>
28+
ITwinPlatform._getAuthorizationHeader(),
29+
).toThrowDeveloperError(
30+
/Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey/,
31+
);
32+
});
33+
34+
it("uses default access token if default share key not set", () => {
35+
ITwinPlatform.defaultAccessToken = "access-token";
36+
ITwinPlatform.defaultShareKey = undefined;
37+
const header = ITwinPlatform._getAuthorizationHeader();
38+
expect(header).toEqual("Bearer access-token");
39+
});
40+
41+
it("uses default share key if default access token not set", () => {
42+
ITwinPlatform.defaultAccessToken = undefined;
43+
ITwinPlatform.defaultShareKey = "share-key";
44+
const header = ITwinPlatform._getAuthorizationHeader();
45+
expect(header).toEqual("Basic share-key");
46+
});
47+
48+
it("uses default share key even if default access token is set", () => {
49+
ITwinPlatform.defaultAccessToken = "access-token";
50+
ITwinPlatform.defaultShareKey = "share-key";
51+
const header = ITwinPlatform._getAuthorizationHeader();
52+
expect(header).toEqual("Basic share-key");
53+
});
1754
});
1855

1956
describe("getExports", () => {
@@ -30,12 +67,13 @@ describe("ITwinPlatform", () => {
3067
);
3168
});
3269

33-
it("rejects with no default access token set", async () => {
70+
it("rejects with no default access token or default share key set", async () => {
3471
ITwinPlatform.defaultAccessToken = undefined;
72+
ITwinPlatform.defaultShareKey = undefined;
3573
await expectAsync(
3674
ITwinPlatform.getExports("imodel-id-1"),
3775
).toBeRejectedWithDeveloperError(
38-
"Must set ITwinPlatform.defaultAccessToken first",
76+
/Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey/,
3977
);
4078
});
4179

@@ -142,12 +180,13 @@ describe("ITwinPlatform", () => {
142180
);
143181
});
144182

145-
it("rejects with no default access token set", async () => {
183+
it("rejects with no default access token or default share key set", async () => {
146184
ITwinPlatform.defaultAccessToken = undefined;
185+
ITwinPlatform.defaultShareKey = undefined;
147186
await expectAsync(
148187
ITwinPlatform.getRealityDataMetadata("itwin-id-1", "reality-data-id-1"),
149188
).toBeRejectedWithDeveloperError(
150-
"Must set ITwinPlatform.defaultAccessToken first",
189+
/Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey/,
151190
);
152191
});
153192

@@ -271,7 +310,7 @@ describe("ITwinPlatform", () => {
271310
);
272311
});
273312

274-
it("rejects with no default access token set", async () => {
313+
it("rejects with no default access token or default share key set", async () => {
275314
ITwinPlatform.defaultAccessToken = undefined;
276315
await expectAsync(
277316
ITwinPlatform.getRealityDataURL(
@@ -280,7 +319,7 @@ describe("ITwinPlatform", () => {
280319
"root/document/path.json",
281320
),
282321
).toBeRejectedWithDeveloperError(
283-
"Must set ITwinPlatform.defaultAccessToken first",
322+
/Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey/,
284323
);
285324
});
286325

packages/engine/Specs/Scene/ITwinDataSpec.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,13 @@ describe("ITwinData", () => {
391391
);
392392
});
393393

394-
it("rejects with no default access token set", async () => {
394+
it("rejects with no default access token or default share key set", async () => {
395395
ITwinPlatform.defaultAccessToken = undefined;
396+
ITwinPlatform.defaultShareKey = undefined;
396397
await expectAsync(
397398
ITwinData.loadGeospatialFeatures("itwin-id-1", "collection-id-1"),
398399
).toBeRejectedWithDeveloperError(
399-
"Must set ITwinPlatform.defaultAccessToken first",
400+
/Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey/,
400401
);
401402
});
402403

0 commit comments

Comments
 (0)