@fluidframework/odsp-driver 2.0.0 → 2.0.2

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.
Files changed (138) hide show
  1. package/api-report/odsp-driver.alpha.api.md +1 -1
  2. package/dist/createFile.d.ts +3 -3
  3. package/dist/createFile.d.ts.map +1 -1
  4. package/dist/createFile.js +11 -9
  5. package/dist/createFile.js.map +1 -1
  6. package/dist/createNewContainerOnExistingFile.d.ts +1 -1
  7. package/dist/createNewContainerOnExistingFile.d.ts.map +1 -1
  8. package/dist/createNewContainerOnExistingFile.js +2 -2
  9. package/dist/createNewContainerOnExistingFile.js.map +1 -1
  10. package/dist/createNewUtils.d.ts +1 -1
  11. package/dist/createNewUtils.d.ts.map +1 -1
  12. package/dist/createNewUtils.js +22 -17
  13. package/dist/createNewUtils.js.map +1 -1
  14. package/dist/fetchSnapshot.d.ts +6 -9
  15. package/dist/fetchSnapshot.d.ts.map +1 -1
  16. package/dist/fetchSnapshot.js +22 -21
  17. package/dist/fetchSnapshot.js.map +1 -1
  18. package/dist/getFileLink.js +13 -8
  19. package/dist/getFileLink.js.map +1 -1
  20. package/dist/getUrlAndHeadersWithAuth.d.ts +2 -5
  21. package/dist/getUrlAndHeadersWithAuth.d.ts.map +1 -1
  22. package/dist/getUrlAndHeadersWithAuth.js +7 -28
  23. package/dist/getUrlAndHeadersWithAuth.js.map +1 -1
  24. package/dist/odspDelayLoadedDeltaStream.d.ts +3 -4
  25. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  26. package/dist/odspDelayLoadedDeltaStream.js +4 -5
  27. package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
  28. package/dist/odspDeltaStorageService.d.ts +2 -2
  29. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  30. package/dist/odspDeltaStorageService.js +9 -8
  31. package/dist/odspDeltaStorageService.js.map +1 -1
  32. package/dist/odspDocumentService.d.ts +4 -4
  33. package/dist/odspDocumentService.d.ts.map +1 -1
  34. package/dist/odspDocumentService.js +9 -9
  35. package/dist/odspDocumentService.js.map +1 -1
  36. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  37. package/dist/odspDocumentServiceFactoryCore.js +6 -4
  38. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  39. package/dist/odspDocumentStorageManager.d.ts +2 -2
  40. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  41. package/dist/odspDocumentStorageManager.js +25 -19
  42. package/dist/odspDocumentStorageManager.js.map +1 -1
  43. package/dist/odspSummaryUploadManager.d.ts +2 -3
  44. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  45. package/dist/odspSummaryUploadManager.js +6 -5
  46. package/dist/odspSummaryUploadManager.js.map +1 -1
  47. package/dist/odspUtils.d.ts +11 -3
  48. package/dist/odspUtils.d.ts.map +1 -1
  49. package/dist/odspUtils.js +10 -6
  50. package/dist/odspUtils.js.map +1 -1
  51. package/dist/packageVersion.d.ts +1 -1
  52. package/dist/packageVersion.js +1 -1
  53. package/dist/packageVersion.js.map +1 -1
  54. package/dist/prefetchLatestSnapshot.d.ts +1 -1
  55. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  56. package/dist/prefetchLatestSnapshot.js +5 -5
  57. package/dist/prefetchLatestSnapshot.js.map +1 -1
  58. package/dist/vroom.d.ts +2 -2
  59. package/dist/vroom.d.ts.map +1 -1
  60. package/dist/vroom.js +7 -9
  61. package/dist/vroom.js.map +1 -1
  62. package/lib/createFile.d.ts +3 -3
  63. package/lib/createFile.d.ts.map +1 -1
  64. package/lib/createFile.js +12 -10
  65. package/lib/createFile.js.map +1 -1
  66. package/lib/createNewContainerOnExistingFile.d.ts +1 -1
  67. package/lib/createNewContainerOnExistingFile.d.ts.map +1 -1
  68. package/lib/createNewContainerOnExistingFile.js +2 -2
  69. package/lib/createNewContainerOnExistingFile.js.map +1 -1
  70. package/lib/createNewUtils.d.ts +1 -1
  71. package/lib/createNewUtils.d.ts.map +1 -1
  72. package/lib/createNewUtils.js +23 -18
  73. package/lib/createNewUtils.js.map +1 -1
  74. package/lib/fetchSnapshot.d.ts +6 -9
  75. package/lib/fetchSnapshot.d.ts.map +1 -1
  76. package/lib/fetchSnapshot.js +23 -22
  77. package/lib/fetchSnapshot.js.map +1 -1
  78. package/lib/getFileLink.js +14 -9
  79. package/lib/getFileLink.js.map +1 -1
  80. package/lib/getUrlAndHeadersWithAuth.d.ts +2 -5
  81. package/lib/getUrlAndHeadersWithAuth.d.ts.map +1 -1
  82. package/lib/getUrlAndHeadersWithAuth.js +5 -26
  83. package/lib/getUrlAndHeadersWithAuth.js.map +1 -1
  84. package/lib/odspDelayLoadedDeltaStream.d.ts +3 -4
  85. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  86. package/lib/odspDelayLoadedDeltaStream.js +4 -5
  87. package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
  88. package/lib/odspDeltaStorageService.d.ts +2 -2
  89. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  90. package/lib/odspDeltaStorageService.js +9 -8
  91. package/lib/odspDeltaStorageService.js.map +1 -1
  92. package/lib/odspDocumentService.d.ts +4 -4
  93. package/lib/odspDocumentService.d.ts.map +1 -1
  94. package/lib/odspDocumentService.js +9 -9
  95. package/lib/odspDocumentService.js.map +1 -1
  96. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  97. package/lib/odspDocumentServiceFactoryCore.js +6 -4
  98. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  99. package/lib/odspDocumentStorageManager.d.ts +2 -2
  100. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  101. package/lib/odspDocumentStorageManager.js +26 -20
  102. package/lib/odspDocumentStorageManager.js.map +1 -1
  103. package/lib/odspSummaryUploadManager.d.ts +2 -3
  104. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  105. package/lib/odspSummaryUploadManager.js +7 -6
  106. package/lib/odspSummaryUploadManager.js.map +1 -1
  107. package/lib/odspUtils.d.ts +11 -3
  108. package/lib/odspUtils.d.ts.map +1 -1
  109. package/lib/odspUtils.js +11 -7
  110. package/lib/odspUtils.js.map +1 -1
  111. package/lib/packageVersion.d.ts +1 -1
  112. package/lib/packageVersion.js +1 -1
  113. package/lib/packageVersion.js.map +1 -1
  114. package/lib/prefetchLatestSnapshot.d.ts +1 -1
  115. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  116. package/lib/prefetchLatestSnapshot.js +5 -5
  117. package/lib/prefetchLatestSnapshot.js.map +1 -1
  118. package/lib/vroom.d.ts +2 -2
  119. package/lib/vroom.d.ts.map +1 -1
  120. package/lib/vroom.js +7 -9
  121. package/lib/vroom.js.map +1 -1
  122. package/package.json +11 -11
  123. package/src/createFile.ts +15 -21
  124. package/src/createNewContainerOnExistingFile.ts +2 -2
  125. package/src/createNewUtils.ts +32 -24
  126. package/src/fetchSnapshot.ts +35 -34
  127. package/src/getFileLink.ts +26 -20
  128. package/src/getUrlAndHeadersWithAuth.ts +6 -31
  129. package/src/odspDelayLoadedDeltaStream.ts +4 -5
  130. package/src/odspDeltaStorageService.ts +11 -7
  131. package/src/odspDocumentService.ts +8 -8
  132. package/src/odspDocumentServiceFactoryCore.ts +5 -3
  133. package/src/odspDocumentStorageManager.ts +36 -33
  134. package/src/odspSummaryUploadManager.ts +9 -9
  135. package/src/odspUtils.ts +19 -6
  136. package/src/packageVersion.ts +1 -1
  137. package/src/prefetchLatestSnapshot.ts +9 -5
  138. package/src/vroom.ts +11 -11
@@ -31,7 +31,7 @@ import { getWithRetryForTokenRefresh } from "./odspUtils.js";
31
31
  export class OdspDeltaStorageService {
32
32
  constructor(
33
33
  private readonly deltaFeedUrl: string,
34
- private readonly getStorageToken: InstrumentedStorageTokenFetcher,
34
+ private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
35
35
  private readonly epochTracker: EpochTracker,
36
36
  private readonly logger: ITelemetryLoggerExt,
37
37
  ) {}
@@ -52,9 +52,13 @@ export class OdspDeltaStorageService {
52
52
  ): Promise<IDeltasFetchResult> {
53
53
  return getWithRetryForTokenRefresh(async (options) => {
54
54
  // Note - this call ends up in getSocketStorageDiscovery() and can refresh token
55
- // Thus it needs to be done before we call getStorageToken() to reduce extra calls
56
- const baseUrl = this.buildUrl(from, to);
57
- const storageToken = await this.getStorageToken(options, "DeltaStorage");
55
+ // Thus it needs to be done before we call getAuthHeader() to reduce extra calls
56
+ const url = this.buildUrl(from, to);
57
+ const method = "POST";
58
+ const authHeader = await this.getAuthHeader(
59
+ { ...options, request: { url, method } },
60
+ "DeltaStorage",
61
+ );
58
62
 
59
63
  return PerformanceEvent.timedExecAsync(
60
64
  this.logger,
@@ -69,7 +73,7 @@ export class OdspDeltaStorageService {
69
73
  async (event) => {
70
74
  const formBoundary = uuid();
71
75
  let postBody = `--${formBoundary}\r\n`;
72
- postBody += `Authorization: Bearer ${storageToken}\r\n`;
76
+ postBody += `Authorization: ${authHeader}\r\n`;
73
77
  postBody += `X-HTTP-Method-Override: GET\r\n`;
74
78
 
75
79
  postBody += `_post: 1\r\n`;
@@ -88,11 +92,11 @@ export class OdspDeltaStorageService {
88
92
 
89
93
  const response =
90
94
  await this.epochTracker.fetchAndParseAsJSON<IDeltaStorageGetResponse>(
91
- baseUrl,
95
+ url,
92
96
  {
93
97
  headers,
94
98
  body: postBody,
95
- method: "POST",
99
+ method,
96
100
  signal: abort.signal,
97
101
  },
98
102
  "ops",
@@ -64,7 +64,7 @@ export class OdspDocumentService
64
64
  * Creates a new OdspDocumentService instance.
65
65
  *
66
66
  * @param resolvedUrl - resolved url identifying document that will be managed by returned service instance.
67
- * @param getStorageToken - function that can provide the storage token. This is is also referred to as
67
+ * @param getAuthHeader - function that can provide the Authentication header value. This is is also referred to as
68
68
  * the "Vroom" token in SPO.
69
69
  * @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
70
70
  * to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
@@ -77,7 +77,7 @@ export class OdspDocumentService
77
77
  */
78
78
  public static async create(
79
79
  resolvedUrl: IResolvedUrl,
80
- getStorageToken: InstrumentedStorageTokenFetcher,
80
+ getAuthHeader: InstrumentedStorageTokenFetcher,
81
81
  // eslint-disable-next-line @rushstack/no-new-null
82
82
  getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
83
83
  logger: ITelemetryLoggerExt,
@@ -89,7 +89,7 @@ export class OdspDocumentService
89
89
  ): Promise<IDocumentService> {
90
90
  return new OdspDocumentService(
91
91
  getOdspResolvedUrl(resolvedUrl),
92
- getStorageToken,
92
+ getAuthHeader,
93
93
  getWebsocketToken,
94
94
  logger,
95
95
  cache,
@@ -110,7 +110,7 @@ export class OdspDocumentService
110
110
 
111
111
  /**
112
112
  * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
113
- * @param getStorageToken - function that can provide the storage token. This is is also referred to as
113
+ * @param getAuthHeader - function that can provide the Authentication header value. This is is also referred to as
114
114
  * the "Vroom" token in SPO.
115
115
  * @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
116
116
  * to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
@@ -124,7 +124,7 @@ export class OdspDocumentService
124
124
  */
125
125
  private constructor(
126
126
  public readonly odspResolvedUrl: IOdspResolvedUrl,
127
- private readonly getStorageToken: InstrumentedStorageTokenFetcher,
127
+ private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
128
128
  private readonly getWebsocketToken:
129
129
  | ((options: TokenFetchOptions) => Promise<string | null>)
130
130
  | undefined,
@@ -175,7 +175,7 @@ export class OdspDocumentService
175
175
  if (!this.storageManager) {
176
176
  this.storageManager = new OdspDocumentStorageService(
177
177
  this.odspResolvedUrl,
178
- this.getStorageToken,
178
+ this.getAuthHeader,
179
179
  this.mc.logger,
180
180
  true,
181
181
  this.cache,
@@ -208,7 +208,7 @@ export class OdspDocumentService
208
208
  const snapshotOps = this.storageManager?.ops ?? [];
209
209
  const service = new OdspDeltaStorageService(
210
210
  this.odspResolvedUrl.endpoints.deltaStorageUrl,
211
- this.getStorageToken,
211
+ this.getAuthHeader,
212
212
  this.epochTracker,
213
213
  this.mc.logger,
214
214
  );
@@ -285,7 +285,7 @@ export class OdspDocumentService
285
285
  this.odspDelayLoadedDeltaStream = new module.OdspDelayLoadedDeltaStream(
286
286
  this.odspResolvedUrl,
287
287
  this._policies,
288
- this.getStorageToken,
288
+ this.getAuthHeader,
289
289
  this.getWebsocketToken,
290
290
  this.mc,
291
291
  this.cache,
@@ -167,7 +167,7 @@ export class OdspDocumentServiceFactoryCore
167
167
  this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
168
168
  },
169
169
  async (event) => {
170
- const getStorageToken = toInstrumentedOdspStorageTokenFetcher(
170
+ const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
171
171
  odspLogger,
172
172
  resolvedUrlData,
173
173
  this.getStorageToken,
@@ -188,7 +188,7 @@ export class OdspDocumentServiceFactoryCore
188
188
  });
189
189
  const _odspResolvedUrl = isNewFileInfo(fileInfo)
190
190
  ? await module.createNewFluidFile(
191
- getStorageToken,
191
+ getAuthHeader,
192
192
  fileInfo,
193
193
  odspLogger,
194
194
  createNewSummary,
@@ -200,7 +200,7 @@ export class OdspDocumentServiceFactoryCore
200
200
  this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
201
201
  )
202
202
  : await module.createNewContainerOnExistingFile(
203
- getStorageToken,
203
+ getAuthHeader,
204
204
  fileInfo,
205
205
  odspLogger,
206
206
  createNewSummary,
@@ -300,11 +300,13 @@ export class OdspDocumentServiceFactoryCore
300
300
  this.getWebsocketToken === undefined
301
301
  ? undefined
302
302
  : async (options: TokenFetchOptions): Promise<string | null> =>
303
+ // websocket expects a plain token
303
304
  toInstrumentedOdspTokenFetcher(
304
305
  extLogger,
305
306
  resolvedUrlData,
306
307
  this.getWebsocketToken!,
307
308
  false /* throwOnNullToken */,
309
+ true /* returnPlainToken */,
308
310
  )(options, "GetWebsocketToken");
309
311
 
310
312
  return OdspDocumentService.create(
@@ -51,7 +51,7 @@ import {
51
51
  fetchSnapshot,
52
52
  fetchSnapshotWithRedeem,
53
53
  } from "./fetchSnapshot.js";
54
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
54
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
55
55
  import { IOdspCache, IPrefetchSnapshotContents } from "./odspCache.js";
56
56
  import { FlushResult } from "./odspDocumentDeltaConnection.js";
57
57
  import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase.js";
@@ -63,6 +63,7 @@ import {
63
63
  isInstanceOfISnapshot,
64
64
  isSnapshotFetchForLoadingGroup,
65
65
  useLegacyFlowWithoutGroupsForSnapshotFetch,
66
+ type TokenFetchOptionsEx,
66
67
  } from "./odspUtils.js";
67
68
  import { pkgVersion as driverVersion } from "./packageVersion.js";
68
69
 
@@ -98,7 +99,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
98
99
 
99
100
  constructor(
100
101
  private readonly odspResolvedUrl: IOdspResolvedUrl,
101
- private readonly getStorageToken: InstrumentedStorageTokenFetcher,
102
+ private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
102
103
  private readonly logger: ITelemetryLoggerExt,
103
104
  private readonly fetchFullSnapshot: boolean,
104
105
  private readonly cache: IOdspCache,
@@ -124,12 +125,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
124
125
  this.checkAttachmentPOSTUrl();
125
126
 
126
127
  const response = await getWithRetryForTokenRefresh(async (options) => {
127
- const storageToken = await this.getStorageToken(options, "CreateBlob");
128
- const { url, headers } = getUrlAndHeadersWithAuth(
129
- `${this.attachmentPOSTUrl}/content`,
130
- storageToken,
131
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
128
+ const url = `${this.attachmentPOSTUrl}/content`;
129
+ const method = "POST";
130
+ const authHeader = await this.getAuthHeader(
131
+ { ...options, request: { url, method } },
132
+ "CreateBlob",
132
133
  );
134
+ const headers = getHeadersWithAuth(authHeader);
133
135
  headers["Content-Type"] = "application/octet-stream";
134
136
 
135
137
  return PerformanceEvent.timedExecAsync(
@@ -146,7 +148,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
146
148
  {
147
149
  body: file,
148
150
  headers,
149
- method: "POST",
151
+ method,
150
152
  },
151
153
  "createBlob",
152
154
  ),
@@ -170,13 +172,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
170
172
  this.checkAttachmentGETUrl();
171
173
 
172
174
  const blob = await getWithRetryForTokenRefresh(async (options) => {
173
- const storageToken = await this.getStorageToken(options, "GetBlob");
174
- const unAuthedUrl = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
175
- const { url, headers } = getUrlAndHeadersWithAuth(
176
- unAuthedUrl,
177
- storageToken,
178
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
175
+ const url = `${this.attachmentGETUrl}/${encodeURIComponent(blobId)}/content`;
176
+ const method = "GET";
177
+ const authHeader = await this.getAuthHeader(
178
+ { ...options, request: { url, method } },
179
+ "GetBlob",
179
180
  );
181
+ const headers = getHeadersWithAuth(authHeader);
180
182
 
181
183
  return PerformanceEvent.timedExecAsync(
182
184
  this.logger,
@@ -483,12 +485,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
483
485
  }
484
486
 
485
487
  return getWithRetryForTokenRefresh(async (options) => {
486
- const storageToken = await this.getStorageToken(options, "GetVersions");
487
- const { url, headers } = getUrlAndHeadersWithAuth(
488
- `${this.snapshotUrl}/versions?top=${count}`,
489
- storageToken,
490
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
488
+ const url = `${this.snapshotUrl}/versions?top=${count}`;
489
+ const method = "GET";
490
+ const storageToken = await this.getAuthHeader(
491
+ { ...options, request: { url, method } },
492
+ "GetVersions",
491
493
  );
494
+ const headers = getHeadersWithAuth(storageToken);
492
495
 
493
496
  // Fetch the latest snapshot versions for the document
494
497
  const response = await PerformanceEvent.timedExecAsync(
@@ -604,14 +607,16 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
604
607
 
605
608
  const snapshotDownloader = async (
606
609
  finalOdspResolvedUrl: IOdspResolvedUrl,
607
- storageToken: string,
610
+ tokenFetcher: InstrumentedStorageTokenFetcher,
611
+ tokenFetchOptions: TokenFetchOptionsEx,
608
612
  loadingGroupId: string[] | undefined,
609
613
  options: ISnapshotOptions | undefined,
610
614
  controller?: AbortController,
611
615
  ): Promise<ISnapshotRequestAndResponseOptions> => {
612
616
  return downloadSnapshot(
613
617
  finalOdspResolvedUrl,
614
- storageToken,
618
+ tokenFetcher,
619
+ tokenFetchOptions,
615
620
  loadingGroupId,
616
621
  options,
617
622
  this.snapshotFormatFetchType,
@@ -631,7 +636,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
631
636
  try {
632
637
  const odspSnapshot = await fetchSnapshotWithRedeem(
633
638
  this.odspResolvedUrl,
634
- this.getStorageToken,
639
+ this.getAuthHeader,
635
640
  snapshotOptions,
636
641
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
637
642
  this.logger,
@@ -673,7 +678,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
673
678
  };
674
679
  const odspSnapshot = await fetchSnapshotWithRedeem(
675
680
  this.odspResolvedUrl,
676
- this.getStorageToken,
681
+ this.getAuthHeader,
677
682
  snapshotOptionsWithoutBlobs,
678
683
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
679
684
  this.logger,
@@ -767,10 +772,9 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
767
772
  });
768
773
  this.odspSummaryUploadManager = new module.OdspSummaryUploadManager(
769
774
  this.odspResolvedUrl.endpoints.snapshotStorageUrl,
770
- this.getStorageToken,
775
+ this.getAuthHeader,
771
776
  this.logger,
772
777
  this.epochTracker,
773
- !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
774
778
  this.relayServiceTenantAndSessionId,
775
779
  );
776
780
  return this.odspSummaryUploadManager;
@@ -811,15 +815,15 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
811
815
  scenarioName?: string,
812
816
  ): Promise<ISnapshotTree | undefined> {
813
817
  return getWithRetryForTokenRefresh(async (options) => {
814
- const storageToken = await this.getStorageToken(options, "ReadCommit");
815
- const snapshotDownloader = async (
816
- url: string,
817
- fetchOptions: RequestInit,
818
- // eslint-disable-next-line unicorn/consistent-function-scoping
819
- ): Promise<IOdspResponse<unknown>> => {
818
+ const snapshotDownloader = async (url: string): Promise<IOdspResponse<unknown>> => {
819
+ const authHeader = await this.getAuthHeader(
820
+ { ...options, request: { url, method: "GET" } },
821
+ "ReadCommit",
822
+ );
823
+ const headers = getHeadersWithAuth(authHeader);
820
824
  return this.epochTracker.fetchAndParseAsJSON(
821
825
  url,
822
- fetchOptions,
826
+ { headers },
823
827
  "snapshotTree",
824
828
  undefined,
825
829
  scenarioName,
@@ -827,7 +831,6 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
827
831
  };
828
832
  const snapshot = await fetchSnapshot(
829
833
  this.snapshotUrl!,
830
- storageToken,
831
834
  id,
832
835
  this.fetchFullSnapshot,
833
836
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
@@ -28,7 +28,7 @@ import {
28
28
  OdspSummaryTreeValue,
29
29
  } from "./contracts.js";
30
30
  import { EpochTracker } from "./epochTracker.js";
31
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
31
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
32
32
  import { getWithRetryForTokenRefresh } from "./odspUtils.js";
33
33
 
34
34
  /**
@@ -42,10 +42,9 @@ export class OdspSummaryUploadManager {
42
42
 
43
43
  constructor(
44
44
  private readonly snapshotUrl: string,
45
- private readonly getStorageToken: InstrumentedStorageTokenFetcher,
45
+ private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
46
46
  logger: ITelemetryLoggerExt,
47
47
  private readonly epochTracker: EpochTracker,
48
- private readonly forceAccessTokenViaAuthorizationHeader: boolean,
49
48
  private readonly relayServiceTenantAndSessionId: () => string | undefined,
50
49
  ) {
51
50
  this.mc = loggerToMonitoringContext(logger);
@@ -102,13 +101,14 @@ export class OdspSummaryUploadManager {
102
101
  };
103
102
 
104
103
  return getWithRetryForTokenRefresh(async (options) => {
105
- const storageToken = await this.getStorageToken(options, "WriteSummaryTree");
106
-
107
- const { url, headers } = getUrlAndHeadersWithAuth(
108
- `${this.snapshotUrl}/snapshot`,
109
- storageToken,
110
- this.forceAccessTokenViaAuthorizationHeader,
104
+ const url = `${this.snapshotUrl}/snapshot`;
105
+ const method = "POST";
106
+ const authHeader = await this.getAuthHeader(
107
+ { ...options, request: { url, method } },
108
+ "WriteSummaryTree",
111
109
  );
110
+
111
+ const headers = getHeadersWithAuth(authHeader);
112
112
  headers["Content-Type"] = "application/json";
113
113
  const relayServiceTenantAndSessionId = this.relayServiceTenantAndSessionId();
114
114
  // This would be undefined in case of summary is uploaded in detached container with attachment
package/src/odspUtils.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  InstrumentedStorageTokenFetcher,
32
32
  InstrumentedTokenFetcher,
33
33
  OdspErrorTypes,
34
+ authHeaderFromTokenResponse,
34
35
  OdspResourceTokenFetchOptions,
35
36
  TokenFetchOptions,
36
37
  TokenFetcher,
@@ -64,7 +65,14 @@ export interface IOdspResponse<T> {
64
65
  duration: number;
65
66
  }
66
67
 
67
- export interface TokenFetchOptionsEx extends TokenFetchOptions {
68
+ /**
69
+ * This interface captures the portion of TokenFetchOptions required for refreshing tokens
70
+ * It is controlled by logic in getWithRetryForTokenRefresh to specify what is the required refresh behavior
71
+ */
72
+ export interface TokenFetchOptionsEx {
73
+ refresh: boolean;
74
+ claims?: string;
75
+ tenantId?: string;
68
76
  /**
69
77
  * The previous error we hit in {@link getWithRetryForTokenRefresh}.
70
78
  */
@@ -354,7 +362,8 @@ export function toInstrumentedOdspStorageTokenFetcher(
354
362
  logger,
355
363
  resolvedUrlParts,
356
364
  tokenFetcher,
357
- true, // throwOnNullToken,
365
+ true, // throwOnNullToken
366
+ false, // returnPlainToken
358
367
  );
359
368
  // Drop undefined from signature - we can do it safely due to throwOnNullToken == true above
360
369
  return res as InstrumentedStorageTokenFetcher;
@@ -364,12 +373,14 @@ export function toInstrumentedOdspStorageTokenFetcher(
364
373
  * Returns a function that can be used to fetch storage or websocket token.
365
374
  * There are scenarios where websocket token is not required / present (consumer stack and ordering service token),
366
375
  * thus it could return null. Use toInstrumentedOdspStorageTokenFetcher if you deal with storage token.
376
+ * @param returnPlainToken - When true, tokenResponse.token is returned. When false, tokenResponse.authorizationHeader is returned or an authorization header value is created based on tokenResponse.token
367
377
  */
368
378
  export function toInstrumentedOdspTokenFetcher(
369
379
  logger: ITelemetryLoggerExt,
370
380
  resolvedUrlParts: IOdspUrlParts,
371
381
  tokenFetcher: TokenFetcher<OdspResourceTokenFetchOptions>,
372
382
  throwOnNullToken: boolean,
383
+ returnPlainToken: boolean,
373
384
  ): InstrumentedTokenFetcher {
374
385
  return async (
375
386
  options: TokenFetchOptions,
@@ -394,7 +405,9 @@ export function toInstrumentedOdspTokenFetcher(
394
405
  ...resolvedUrlParts,
395
406
  }).then(
396
407
  (tokenResponse) => {
397
- const token = tokenFromResponse(tokenResponse);
408
+ const returnVal = returnPlainToken
409
+ ? tokenFromResponse(tokenResponse)
410
+ : authHeaderFromTokenResponse(tokenResponse);
398
411
  // This event alone generates so many events that is materially impacts cost of telemetry
399
412
  // Thus do not report end event when it comes back quickly.
400
413
  // Note that most of the hosts do not report if result is comming from cache or not,
@@ -403,10 +416,10 @@ export function toInstrumentedOdspTokenFetcher(
403
416
  if (alwaysRecordTokenFetchTelemetry || event.duration >= 32) {
404
417
  event.end({
405
418
  fromCache: isTokenFromCache(tokenResponse),
406
- isNull: token === null,
419
+ isNull: returnVal === null,
407
420
  });
408
421
  }
409
- if (token === null && throwOnNullToken) {
422
+ if (returnVal === null && throwOnNullToken) {
410
423
  throw new NonRetryableError(
411
424
  // pre-0.58 error message: Token is null for ${name} call
412
425
  `The Host-provided token fetcher returned null`,
@@ -414,7 +427,7 @@ export function toInstrumentedOdspTokenFetcher(
414
427
  { method: name, driverVersion },
415
428
  );
416
429
  }
417
- return token;
430
+ return returnVal;
418
431
  },
419
432
  (error) => {
420
433
  // There is an important but unofficial contract here where token providers can set canRetry: true
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.0.0";
9
+ export const pkgVersion = "2.0.2";
@@ -15,6 +15,7 @@ import {
15
15
  OdspResourceTokenFetchOptions,
16
16
  TokenFetcher,
17
17
  getKeyForCacheEntry,
18
+ type InstrumentedStorageTokenFetcher,
18
19
  } from "@fluidframework/odsp-driver-definitions/internal";
19
20
  import {
20
21
  PerformanceEvent,
@@ -35,6 +36,7 @@ import {
35
36
  createOdspLogger,
36
37
  getOdspResolvedUrl,
37
38
  toInstrumentedOdspStorageTokenFetcher,
39
+ type TokenFetchOptionsEx,
38
40
  } from "./odspUtils.js";
39
41
 
40
42
  /**
@@ -45,7 +47,7 @@ import {
45
47
  * @param getStorageToken - function that can provide the storage token for a given site. This is
46
48
  * is also referred to as the "VROOM" token in SPO.
47
49
  * @param persistedCache - Cache to store the fetched snapshot.
48
- * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header.
50
+ * @param forceAccessTokenViaAuthorizationHeader - @deprecated Not used, true value always used instead. Whether to force passing given token via authorization header.
49
51
  * @param logger - Logger to have telemetry events.
50
52
  * @param hostSnapshotFetchOptions - Options to fetch the snapshot if any. Otherwise default will be used.
51
53
  * @param enableRedeemFallback - True to have the sharing link redeem fallback in case the Trees Latest/Redeem
@@ -86,7 +88,7 @@ export async function prefetchLatestSnapshot(
86
88
  driveId: odspResolvedUrl.driveId,
87
89
  itemId: odspResolvedUrl.itemId,
88
90
  };
89
- const storageTokenFetcher = toInstrumentedOdspStorageTokenFetcher(
91
+ const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
90
92
  odspLogger,
91
93
  resolvedUrlData,
92
94
  getStorageToken,
@@ -94,14 +96,16 @@ export async function prefetchLatestSnapshot(
94
96
 
95
97
  const snapshotDownloader = async (
96
98
  finalOdspResolvedUrl: IOdspResolvedUrl,
97
- storageToken: string,
99
+ storageTokenFetcher: InstrumentedStorageTokenFetcher,
100
+ tokenFetchOptions: TokenFetchOptionsEx,
98
101
  loadingGroupId: string[] | undefined,
99
102
  snapshotOptions: ISnapshotOptions | undefined,
100
103
  controller?: AbortController,
101
104
  ): Promise<ISnapshotRequestAndResponseOptions> => {
102
105
  return downloadSnapshot(
103
106
  finalOdspResolvedUrl,
104
- storageToken,
107
+ storageTokenFetcher,
108
+ tokenFetchOptions,
105
109
  loadingGroupId,
106
110
  snapshotOptions,
107
111
  undefined,
@@ -135,7 +139,7 @@ export async function prefetchLatestSnapshot(
135
139
  );
136
140
  await fetchSnapshotWithRedeem(
137
141
  odspResolvedUrl,
138
- storageTokenFetcher,
142
+ getAuthHeader,
139
143
  hostSnapshotFetchOptions,
140
144
  forceAccessTokenViaAuthorizationHeader,
141
145
  odspLogger,
package/src/vroom.ts CHANGED
@@ -31,7 +31,7 @@ interface IJoinSessionBody {
31
31
  * @param path - The API path that is relevant to this request
32
32
  * @param method - The type of request, such as GET or POST
33
33
  * @param logger - A logger to use for this request
34
- * @param getStorageToken - A function that is able to provide the access token for this request
34
+ * @param getAuthHeader - A function that is able to provide the access token for this request
35
35
  * @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call
36
36
  * @param requestSocketToken - flag indicating whether joinSession is expected to return access token
37
37
  * which is used when establishing websocket connection with collab session backend service.
@@ -44,17 +44,21 @@ interface IJoinSessionBody {
44
44
  export async function fetchJoinSession(
45
45
  urlParts: IOdspUrlParts,
46
46
  path: string,
47
- method: string,
47
+ method: "GET" | "POST",
48
48
  logger: ITelemetryLoggerExt,
49
- getStorageToken: InstrumentedStorageTokenFetcher,
49
+ getAuthHeader: InstrumentedStorageTokenFetcher,
50
50
  epochTracker: EpochTracker,
51
51
  requestSocketToken: boolean,
52
52
  options: TokenFetchOptionsEx,
53
53
  disableJoinSessionRefresh: boolean | undefined,
54
54
  isRefreshingJoinSession: boolean,
55
- guestDisplayName?: string,
56
55
  ): Promise<ISocketStorageDiscovery> {
57
- const token = await getStorageToken(options, "JoinSession");
56
+ const apiRoot = getApiRoot(new URL(urlParts.siteUrl));
57
+ const url = `${apiRoot}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?ump=1`;
58
+ const authHeader = await getAuthHeader(
59
+ { ...options, request: { url, method } },
60
+ "JoinSession",
61
+ );
58
62
 
59
63
  const tokenRefreshProps = options.refresh
60
64
  ? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
@@ -75,10 +79,9 @@ export async function fetchJoinSession(
75
79
  ...tokenRefreshProps,
76
80
  },
77
81
  async (event) => {
78
- const apiRoot = getApiRoot(new URL(urlParts.siteUrl));
79
82
  const formBoundary = uuid();
80
83
  let postBody = `--${formBoundary}\r\n`;
81
- postBody += `Authorization: Bearer ${token}\r\n`;
84
+ postBody += `Authorization: ${authHeader}\r\n`;
82
85
  postBody += `X-HTTP-Method-Override: POST\r\n`;
83
86
  postBody += `Content-Type: application/json\r\n`;
84
87
  if (!disableJoinSessionRefresh) {
@@ -90,9 +93,6 @@ export async function fetchJoinSession(
90
93
  const body: IJoinSessionBody = {
91
94
  requestSocketToken: true,
92
95
  };
93
- if (guestDisplayName !== undefined) {
94
- body.guestDisplayName = guestDisplayName;
95
- }
96
96
  postBody += `\r\n${JSON.stringify(body)}\r\n`;
97
97
  }
98
98
  postBody += `\r\n--${formBoundary}--`;
@@ -103,7 +103,7 @@ export async function fetchJoinSession(
103
103
  const response = await runWithRetry(
104
104
  async () =>
105
105
  epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(
106
- `${apiRoot}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?ump=1`,
106
+ url,
107
107
  { method, headers, body: postBody },
108
108
  "joinSession",
109
109
  true,