@fluidframework/tool-utils 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.0
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/.eslintrc.js +19 -23
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/fluidToolRC.d.ts.map +1 -1
- package/dist/fluidToolRC.js.map +1 -1
- package/dist/httpHelpers.d.ts.map +1 -1
- package/dist/httpHelpers.js +6 -2
- package/dist/httpHelpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/odspTokenManager.d.ts.map +1 -1
- package/dist/odspTokenManager.js +5 -2
- package/dist/odspTokenManager.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/snapshotNormalizer.d.ts.map +1 -1
- package/dist/snapshotNormalizer.js +13 -10
- package/dist/snapshotNormalizer.js.map +1 -1
- package/lib/fluidToolRC.d.ts.map +1 -1
- package/lib/fluidToolRC.js.map +1 -1
- package/lib/httpHelpers.d.ts.map +1 -1
- package/lib/httpHelpers.js +6 -2
- package/lib/httpHelpers.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/odspTokenManager.d.ts.map +1 -1
- package/lib/odspTokenManager.js +5 -2
- package/lib/odspTokenManager.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/snapshotNormalizer.d.ts.map +1 -1
- package/lib/snapshotNormalizer.js +14 -11
- package/lib/snapshotNormalizer.js.map +1 -1
- package/package.json +40 -39
- package/prettier.config.cjs +1 -1
- package/src/fluidToolRC.ts +36 -36
- package/src/httpHelpers.ts +58 -44
- package/src/index.ts +5 -1
- package/src/odspTokenManager.ts +301 -309
- package/src/packageVersion.ts +1 -1
- package/src/snapshotNormalizer.ts +178 -171
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +10 -16
package/src/odspTokenManager.ts
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import { unreachableCase } from "@fluidframework/common-utils";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
IOdspTokens,
|
|
9
|
+
IClientConfig,
|
|
10
|
+
fetchTokens,
|
|
11
|
+
refreshTokens,
|
|
12
|
+
getOdspScope,
|
|
13
|
+
pushScope,
|
|
14
|
+
getLoginPageUrl,
|
|
15
|
+
TokenRequestCredentials,
|
|
16
16
|
} from "@fluidframework/odsp-doclib-utils";
|
|
17
17
|
import jwtDecode from "jwt-decode";
|
|
18
18
|
import { Mutex } from "async-mutex";
|
|
@@ -25,345 +25,337 @@ const odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;
|
|
|
25
25
|
const odspAuthRedirectUri = new URL("/auth/callback", odspAuthRedirectOrigin).href;
|
|
26
26
|
|
|
27
27
|
export const getMicrosoftConfiguration = (): IClientConfig => ({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
get clientId() {
|
|
29
|
+
if (!process.env.login__microsoft__clientId) {
|
|
30
|
+
throw new Error("Client ID environment variable not set: login__microsoft__clientId.");
|
|
31
|
+
}
|
|
32
|
+
return process.env.login__microsoft__clientId;
|
|
33
|
+
},
|
|
34
|
+
get clientSecret() {
|
|
35
|
+
if (!process.env.login__microsoft__secret) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Client Secret environment variable not set: login__microsoft__secret.",
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return process.env.login__microsoft__secret;
|
|
41
|
+
},
|
|
40
42
|
});
|
|
41
43
|
|
|
42
|
-
export type OdspTokenConfig =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
export type OdspTokenConfig =
|
|
45
|
+
| {
|
|
46
|
+
type: "password";
|
|
47
|
+
username: string;
|
|
48
|
+
password: string;
|
|
49
|
+
}
|
|
50
|
+
| {
|
|
51
|
+
type: "browserLogin";
|
|
52
|
+
navigator: (url: string) => void;
|
|
53
|
+
redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>;
|
|
54
|
+
};
|
|
51
55
|
|
|
52
56
|
export interface IOdspTokenManagerCacheKey {
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
readonly isPush: boolean;
|
|
58
|
+
readonly userOrServer: string;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
const isValidToken = (token: string) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
// Return false for undefined or empty tokens.
|
|
63
|
+
if (!token || token.length === 0) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
const decodedToken = jwtDecode<any>(token);
|
|
68
|
+
// Give it a 60s buffer
|
|
69
|
+
return decodedToken.exp - 60 >= new Date().getTime() / 1000;
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
const cacheKeyToString = (key: IOdspTokenManagerCacheKey) => {
|
|
69
|
-
|
|
73
|
+
return `${key.userOrServer}${key.isPush ? "[Push]" : ""}`;
|
|
70
74
|
};
|
|
71
75
|
|
|
72
76
|
export class OdspTokenManager {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
private readonly storageCache = new Map<string, IOdspTokens>();
|
|
78
|
+
private readonly pushCache = new Map<string, IOdspTokens>();
|
|
79
|
+
private readonly cacheMutex = new Mutex();
|
|
80
|
+
constructor(
|
|
81
|
+
private readonly tokenCache?: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens>,
|
|
82
|
+
) {}
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
public async updateTokensCache(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {
|
|
85
|
+
await this.cacheMutex.runExclusive(async () => {
|
|
86
|
+
await this.updateTokensCacheWithoutLock(key, value);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
private async updateTokensCacheWithoutLock(key: IOdspTokenManagerCacheKey, value: IOdspTokens) {
|
|
91
|
+
debug(`${cacheKeyToString(key)}: Saving tokens`);
|
|
92
|
+
const memoryCache = key.isPush ? this.pushCache : this.storageCache;
|
|
93
|
+
memoryCache.set(key.userOrServer, value);
|
|
94
|
+
await this.tokenCache?.save(key, value);
|
|
95
|
+
}
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
server,
|
|
103
|
-
clientConfig,
|
|
104
|
-
tokenConfig,
|
|
105
|
-
forceRefresh,
|
|
106
|
-
forceReauth,
|
|
107
|
-
);
|
|
108
|
-
}
|
|
97
|
+
public async getOdspTokens(
|
|
98
|
+
server: string,
|
|
99
|
+
clientConfig: IClientConfig,
|
|
100
|
+
tokenConfig: OdspTokenConfig,
|
|
101
|
+
forceRefresh = false,
|
|
102
|
+
forceReauth = false,
|
|
103
|
+
): Promise<IOdspTokens> {
|
|
104
|
+
return this.getTokens(false, server, clientConfig, tokenConfig, forceRefresh, forceReauth);
|
|
105
|
+
}
|
|
109
106
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
server,
|
|
120
|
-
clientConfig,
|
|
121
|
-
tokenConfig,
|
|
122
|
-
forceRefresh,
|
|
123
|
-
forceReauth,
|
|
124
|
-
);
|
|
125
|
-
}
|
|
107
|
+
public async getPushTokens(
|
|
108
|
+
server: string,
|
|
109
|
+
clientConfig: IClientConfig,
|
|
110
|
+
tokenConfig: OdspTokenConfig,
|
|
111
|
+
forceRefresh = false,
|
|
112
|
+
forceReauth = false,
|
|
113
|
+
): Promise<IOdspTokens> {
|
|
114
|
+
return this.getTokens(true, server, clientConfig, tokenConfig, forceRefresh, forceReauth);
|
|
115
|
+
}
|
|
126
116
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
}
|
|
117
|
+
private async getTokenFromCache(cacheKey: IOdspTokenManagerCacheKey) {
|
|
118
|
+
const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;
|
|
119
|
+
const memoryToken = memoryCache.get(cacheKey.userOrServer);
|
|
120
|
+
if (memoryToken) {
|
|
121
|
+
debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);
|
|
122
|
+
return memoryToken;
|
|
123
|
+
}
|
|
124
|
+
const fileToken = await this.tokenCache?.get(cacheKey);
|
|
125
|
+
if (fileToken) {
|
|
126
|
+
debug(`${cacheKeyToString(cacheKey)}: Token found in file`);
|
|
127
|
+
memoryCache.set(cacheKey.userOrServer, fileToken);
|
|
128
|
+
return fileToken;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
143
131
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
132
|
+
private static getCacheKey(
|
|
133
|
+
isPush: boolean,
|
|
134
|
+
tokenConfig: OdspTokenConfig,
|
|
135
|
+
server: string,
|
|
136
|
+
): IOdspTokenManagerCacheKey {
|
|
137
|
+
// If we are using password, we should cache the token per user instead of per server
|
|
138
|
+
return {
|
|
139
|
+
isPush,
|
|
140
|
+
userOrServer: tokenConfig.type === "password" ? tokenConfig.username : server,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
152
143
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
144
|
+
private async getTokens(
|
|
145
|
+
isPush: boolean,
|
|
146
|
+
server: string,
|
|
147
|
+
clientConfig: IClientConfig,
|
|
148
|
+
tokenConfig: OdspTokenConfig,
|
|
149
|
+
forceRefresh: boolean,
|
|
150
|
+
forceReauth: boolean,
|
|
151
|
+
): Promise<IOdspTokens> {
|
|
152
|
+
const invokeGetTokensCore = async () => {
|
|
153
|
+
// Don't solely rely on tokenCache lock, ensure serialized execution of
|
|
154
|
+
// cache update to avoid multiple fetch.
|
|
155
|
+
return this.cacheMutex.runExclusive(async () => {
|
|
156
|
+
return this.getTokensCore(
|
|
157
|
+
isPush,
|
|
158
|
+
server,
|
|
159
|
+
clientConfig,
|
|
160
|
+
tokenConfig,
|
|
161
|
+
forceRefresh,
|
|
162
|
+
forceReauth,
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
if (!forceReauth && !forceRefresh) {
|
|
167
|
+
// check and return if it exists without lock
|
|
168
|
+
const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);
|
|
169
|
+
const tokensFromCache = await this.getTokenFromCache(cacheKey);
|
|
170
|
+
if (tokensFromCache) {
|
|
171
|
+
if (isValidToken(tokensFromCache.accessToken)) {
|
|
172
|
+
debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);
|
|
173
|
+
await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);
|
|
174
|
+
return tokensFromCache;
|
|
175
|
+
}
|
|
176
|
+
debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (this.tokenCache) {
|
|
180
|
+
// check with lock, used to prevent concurrent auth attempts
|
|
181
|
+
return this.tokenCache.lock(invokeGetTokensCore);
|
|
182
|
+
}
|
|
183
|
+
return invokeGetTokensCore();
|
|
184
|
+
}
|
|
193
185
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
186
|
+
private async getTokensCore(
|
|
187
|
+
isPush: boolean,
|
|
188
|
+
server: string,
|
|
189
|
+
clientConfig: IClientConfig,
|
|
190
|
+
tokenConfig: OdspTokenConfig,
|
|
191
|
+
forceRefresh,
|
|
192
|
+
forceReauth,
|
|
193
|
+
): Promise<IOdspTokens> {
|
|
194
|
+
const scope = isPush ? pushScope : getOdspScope(server);
|
|
195
|
+
const cacheKey = OdspTokenManager.getCacheKey(isPush, tokenConfig, server);
|
|
196
|
+
let tokens: IOdspTokens | undefined;
|
|
197
|
+
if (!forceReauth) {
|
|
198
|
+
// check the cache again under the lock (if it is there)
|
|
199
|
+
const tokensFromCache = await this.getTokenFromCache(cacheKey);
|
|
200
|
+
if (tokensFromCache) {
|
|
201
|
+
if (forceRefresh || !isValidToken(tokensFromCache.accessToken)) {
|
|
202
|
+
try {
|
|
203
|
+
// This updates the tokens in tokensFromCache
|
|
204
|
+
tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);
|
|
205
|
+
await this.updateTokensCacheWithoutLock(cacheKey, tokens);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
tokens = tokensFromCache;
|
|
211
|
+
debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
223
215
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
216
|
+
if (tokens) {
|
|
217
|
+
await this.onTokenRetrievalFromCache(tokenConfig, tokens);
|
|
218
|
+
return tokens;
|
|
219
|
+
}
|
|
228
220
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
221
|
+
switch (tokenConfig.type) {
|
|
222
|
+
case "password":
|
|
223
|
+
tokens = await this.acquireTokensWithPassword(
|
|
224
|
+
server,
|
|
225
|
+
scope,
|
|
226
|
+
clientConfig,
|
|
227
|
+
tokenConfig.username,
|
|
228
|
+
tokenConfig.password,
|
|
229
|
+
);
|
|
230
|
+
break;
|
|
231
|
+
case "browserLogin":
|
|
232
|
+
tokens = await this.acquireTokensViaBrowserLogin(
|
|
233
|
+
getLoginPageUrl(server, clientConfig, scope, odspAuthRedirectUri),
|
|
234
|
+
server,
|
|
235
|
+
clientConfig,
|
|
236
|
+
scope,
|
|
237
|
+
tokenConfig.navigator,
|
|
238
|
+
tokenConfig.redirectUriCallback,
|
|
239
|
+
);
|
|
240
|
+
break;
|
|
241
|
+
default:
|
|
242
|
+
unreachableCase(tokenConfig);
|
|
243
|
+
}
|
|
252
244
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
245
|
+
await this.updateTokensCacheWithoutLock(cacheKey, tokens);
|
|
246
|
+
return tokens;
|
|
247
|
+
}
|
|
256
248
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
249
|
+
private async acquireTokensWithPassword(
|
|
250
|
+
server: string,
|
|
251
|
+
scope: string,
|
|
252
|
+
clientConfig: IClientConfig,
|
|
253
|
+
username: string,
|
|
254
|
+
password: string,
|
|
255
|
+
): Promise<IOdspTokens> {
|
|
256
|
+
const credentials: TokenRequestCredentials = {
|
|
257
|
+
grant_type: "password",
|
|
258
|
+
username,
|
|
259
|
+
password,
|
|
260
|
+
};
|
|
261
|
+
return fetchTokens(server, scope, clientConfig, credentials);
|
|
262
|
+
}
|
|
271
263
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
264
|
+
private async acquireTokensViaBrowserLogin(
|
|
265
|
+
loginPageUrl: string,
|
|
266
|
+
server: string,
|
|
267
|
+
clientConfig: IClientConfig,
|
|
268
|
+
scope: string,
|
|
269
|
+
navigator: (url: string) => void,
|
|
270
|
+
redirectUriCallback?: (tokens: IOdspTokens) => Promise<string>,
|
|
271
|
+
): Promise<IOdspTokens> {
|
|
272
|
+
// Start up a local auth redirect handler service to receive the tokens after login
|
|
273
|
+
const tokenGetter = await serverListenAndHandle(odspAuthRedirectPort, async (req, res) => {
|
|
274
|
+
// extract code from request URL and fetch the tokens
|
|
275
|
+
const credentials: TokenRequestCredentials = {
|
|
276
|
+
grant_type: "authorization_code",
|
|
277
|
+
code: this.extractAuthorizationCode(req.url),
|
|
278
|
+
redirect_uri: odspAuthRedirectUri,
|
|
279
|
+
};
|
|
280
|
+
const tokens = await fetchTokens(server, scope, clientConfig, credentials);
|
|
289
281
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
282
|
+
// redirect now that the browser is done with auth
|
|
283
|
+
if (redirectUriCallback) {
|
|
284
|
+
res.writeHead(301, { Location: await redirectUriCallback(tokens) });
|
|
285
|
+
await endResponse(res);
|
|
286
|
+
} else {
|
|
287
|
+
res.write("Please close the window");
|
|
288
|
+
await endResponse(res);
|
|
289
|
+
}
|
|
298
290
|
|
|
299
|
-
|
|
300
|
-
|
|
291
|
+
return tokens;
|
|
292
|
+
});
|
|
301
293
|
|
|
302
|
-
|
|
303
|
-
|
|
294
|
+
// Now that our local redirect handler is up, navigate the browser to the login page
|
|
295
|
+
navigator(loginPageUrl);
|
|
304
296
|
|
|
305
|
-
|
|
306
|
-
|
|
297
|
+
// Receive and extract the tokens
|
|
298
|
+
const odspTokens = await tokenGetter();
|
|
307
299
|
|
|
308
|
-
|
|
309
|
-
|
|
300
|
+
return odspTokens;
|
|
301
|
+
}
|
|
310
302
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
303
|
+
private async onTokenRetrievalFromCache(config: OdspTokenConfig, tokens: IOdspTokens) {
|
|
304
|
+
if (config.type === "browserLogin" && config.redirectUriCallback) {
|
|
305
|
+
config.navigator(await config.redirectUriCallback(tokens));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
316
308
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
309
|
+
private extractAuthorizationCode(relativeUrl: string | undefined): string {
|
|
310
|
+
if (relativeUrl === undefined) {
|
|
311
|
+
throw Error("Failed to get authorization");
|
|
312
|
+
}
|
|
313
|
+
const parsedUrl = new URL(relativeUrl, odspAuthRedirectOrigin);
|
|
314
|
+
const code = parsedUrl.searchParams.get("code");
|
|
315
|
+
if (!code) {
|
|
316
|
+
throw Error("Failed to get authorization");
|
|
317
|
+
}
|
|
318
|
+
return code;
|
|
319
|
+
}
|
|
328
320
|
}
|
|
329
321
|
|
|
330
322
|
async function loadAndPatchRC() {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
323
|
+
const rc = await loadRC();
|
|
324
|
+
if (rc.tokens && rc.tokens.version === undefined) {
|
|
325
|
+
// Clean up older versions
|
|
326
|
+
delete (rc as any).tokens;
|
|
327
|
+
delete (rc as any).pushTokens;
|
|
328
|
+
}
|
|
329
|
+
return rc;
|
|
338
330
|
}
|
|
339
331
|
|
|
340
332
|
export const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
333
|
+
async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {
|
|
334
|
+
const rc = await loadAndPatchRC();
|
|
335
|
+
return rc.tokens?.data[key.userOrServer]?.[key.isPush ? "push" : "storage"];
|
|
336
|
+
},
|
|
337
|
+
async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {
|
|
338
|
+
const rc = await loadAndPatchRC();
|
|
339
|
+
if (!rc.tokens) {
|
|
340
|
+
rc.tokens = {
|
|
341
|
+
version: 1,
|
|
342
|
+
data: {},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
let prevTokens = rc.tokens.data[key.userOrServer];
|
|
346
|
+
if (!prevTokens) {
|
|
347
|
+
prevTokens = {};
|
|
348
|
+
rc.tokens.data[key.userOrServer] = prevTokens;
|
|
349
|
+
}
|
|
350
|
+
prevTokens[key.isPush ? "push" : "storage"] = tokens;
|
|
351
|
+
return saveRC(rc);
|
|
352
|
+
},
|
|
353
|
+
async lock<T>(callback: () => Promise<T>): Promise<T> {
|
|
354
|
+
const release = await lockRC();
|
|
355
|
+
try {
|
|
356
|
+
return await callback();
|
|
357
|
+
} finally {
|
|
358
|
+
await release();
|
|
359
|
+
}
|
|
360
|
+
},
|
|
369
361
|
};
|