@medplum/core 1.0.6 → 2.0.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/dist/cjs/client.d.ts +22 -6
- package/dist/cjs/index.cjs +67 -50
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/cjs/utils.d.ts +8 -1
- package/dist/esm/client.d.ts +22 -6
- package/dist/esm/client.mjs +51 -50
- package/dist/esm/client.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/utils.d.ts +8 -1
- package/dist/esm/utils.mjs +16 -1
- package/dist/esm/utils.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cjs/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CodeableConcept, ObservationDefinition, ObservationDefinitionQualifiedInterval, Patient, Practitioner, QuestionnaireResponse, QuestionnaireResponseItemAnswer, Range, Reference, RelatedPerson, Resource } from '@medplum/fhirtypes';
|
|
1
|
+
import { CodeableConcept, Extension, ObservationDefinition, ObservationDefinitionQualifiedInterval, Patient, Practitioner, QuestionnaireResponse, QuestionnaireResponseItemAnswer, Range, Reference, RelatedPerson, Resource } from '@medplum/fhirtypes';
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*/
|
|
@@ -93,6 +93,13 @@ export declare function getIdentifier(resource: Resource, system: string): strin
|
|
|
93
93
|
* @returns The extension value if found; undefined otherwise.
|
|
94
94
|
*/
|
|
95
95
|
export declare function getExtensionValue(resource: any, ...urls: string[]): string | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Returns an extension by extension URLs.
|
|
98
|
+
* @param resource The base resource.
|
|
99
|
+
* @param urls Array of extension URLs. Each entry represents a nested extension.
|
|
100
|
+
* @returns The extension object if found; undefined otherwise.
|
|
101
|
+
*/
|
|
102
|
+
export declare function getExtension(resource: any, ...urls: string[]): Extension | undefined;
|
|
96
103
|
/**
|
|
97
104
|
* FHIR JSON stringify.
|
|
98
105
|
* Removes properties with empty string values.
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -146,6 +146,7 @@ export interface BaseLoginRequest {
|
|
|
146
146
|
readonly codeChallengeMethod?: string;
|
|
147
147
|
readonly googleClientId?: string;
|
|
148
148
|
readonly launch?: string;
|
|
149
|
+
readonly redirectUri?: string;
|
|
149
150
|
}
|
|
150
151
|
export interface EmailPasswordLoginRequest extends BaseLoginRequest {
|
|
151
152
|
readonly email: string;
|
|
@@ -338,6 +339,12 @@ export declare class MedplumClient extends EventTarget {
|
|
|
338
339
|
* @category Authentication
|
|
339
340
|
*/
|
|
340
341
|
clear(): void;
|
|
342
|
+
/**
|
|
343
|
+
* Clears the active login from local storage.
|
|
344
|
+
* Does not clear all local storage (such as other logins).
|
|
345
|
+
* @category Authentication
|
|
346
|
+
*/
|
|
347
|
+
clearActiveLogin(): void;
|
|
341
348
|
/**
|
|
342
349
|
* Invalidates any cached values or cached requests for the given URL.
|
|
343
350
|
* @category Caching
|
|
@@ -467,10 +474,15 @@ export declare class MedplumClient extends EventTarget {
|
|
|
467
474
|
* @returns Promise to the authentication response.
|
|
468
475
|
*/
|
|
469
476
|
startGoogleLogin(loginRequest: GoogleLoginRequest): Promise<LoginAuthenticationResponse>;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
477
|
+
/**
|
|
478
|
+
* Returns the PKCE code challenge and method.
|
|
479
|
+
* If the login request already includes a code challenge, it is returned.
|
|
480
|
+
* Otherwise, a new PKCE code challenge is generated.
|
|
481
|
+
* @category Authentication
|
|
482
|
+
* @param loginRequest The original login request.
|
|
483
|
+
* @returns The PKCE code challenge and method.
|
|
484
|
+
*/
|
|
485
|
+
ensureCodeChallenge<T extends BaseLoginRequest>(loginRequest: T): Promise<T>;
|
|
474
486
|
/**
|
|
475
487
|
* Signs out locally.
|
|
476
488
|
* Does not invalidate tokens with the server.
|
|
@@ -482,8 +494,9 @@ export declare class MedplumClient extends EventTarget {
|
|
|
482
494
|
* Returns true if the user is signed in.
|
|
483
495
|
* This may result in navigating away to the sign in page.
|
|
484
496
|
* @category Authentication
|
|
497
|
+
* @param loginParams Optional login parameters.
|
|
485
498
|
*/
|
|
486
|
-
signInWithRedirect(): Promise<ProfileResource | void>;
|
|
499
|
+
signInWithRedirect(loginParams?: Partial<BaseLoginRequest>): Promise<ProfileResource | void>;
|
|
487
500
|
/**
|
|
488
501
|
* Tries to sign out the user.
|
|
489
502
|
* See: https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
|
|
@@ -1154,7 +1167,10 @@ export declare class MedplumClient extends EventTarget {
|
|
|
1154
1167
|
* Starts a new PKCE flow.
|
|
1155
1168
|
* These PKCE values are stateful, and must survive redirects and page refreshes.
|
|
1156
1169
|
*/
|
|
1157
|
-
startPkce(): Promise<
|
|
1170
|
+
startPkce(): Promise<{
|
|
1171
|
+
codeChallengeMethod: string;
|
|
1172
|
+
codeChallenge: string;
|
|
1173
|
+
}>;
|
|
1158
1174
|
/**
|
|
1159
1175
|
* Processes an OAuth authorization code.
|
|
1160
1176
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
package/dist/esm/client.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { createReference, arrayBufferToBase64 } from './utils.mjs';
|
|
|
11
11
|
// PKCE auth based on:
|
|
12
12
|
// https://aws.amazon.com/blogs/security/how-to-add-authentication-single-page-web-application-with-amazon-cognito-oauth2-implementation/
|
|
13
13
|
var _MedplumClient_instances, _MedplumClient_fetch, _MedplumClient_createPdf, _MedplumClient_storage, _MedplumClient_requestCache, _MedplumClient_cacheTime, _MedplumClient_baseUrl, _MedplumClient_authorizeUrl, _MedplumClient_tokenUrl, _MedplumClient_logoutUrl, _MedplumClient_onUnauthenticated, _MedplumClient_clientId, _MedplumClient_clientSecret, _MedplumClient_accessToken, _MedplumClient_refreshToken, _MedplumClient_refreshPromise, _MedplumClient_profilePromise, _MedplumClient_profile, _MedplumClient_config, _MedplumClient_addLogin, _MedplumClient_refreshProfile, _MedplumClient_getCacheEntry, _MedplumClient_setCacheEntry, _MedplumClient_request, _MedplumClient_addFetchOptionsDefaults, _MedplumClient_setRequestContentType, _MedplumClient_setRequestBody, _MedplumClient_handleUnauthenticated, _MedplumClient_requestAuthorization, _MedplumClient_refresh, _MedplumClient_fetchTokens, _MedplumClient_verifyTokens, _MedplumClient_setupStorageListener;
|
|
14
|
-
const MEDPLUM_VERSION = "
|
|
14
|
+
const MEDPLUM_VERSION = "2.0.0-7fe99080";
|
|
15
15
|
const DEFAULT_BASE_URL = 'https://api.medplum.com/';
|
|
16
16
|
const DEFAULT_RESOURCE_CACHE_SIZE = 1000;
|
|
17
17
|
const DEFAULT_CACHE_TIME = 60000; // 60 seconds
|
|
@@ -132,6 +132,15 @@ class MedplumClient extends EventTarget {
|
|
|
132
132
|
*/
|
|
133
133
|
clear() {
|
|
134
134
|
__classPrivateFieldGet(this, _MedplumClient_storage, "f").clear();
|
|
135
|
+
this.clearActiveLogin();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Clears the active login from local storage.
|
|
139
|
+
* Does not clear all local storage (such as other logins).
|
|
140
|
+
* @category Authentication
|
|
141
|
+
*/
|
|
142
|
+
clearActiveLogin() {
|
|
143
|
+
__classPrivateFieldGet(this, _MedplumClient_storage, "f").setString('activeLogin', undefined);
|
|
135
144
|
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
|
|
136
145
|
__classPrivateFieldSet(this, _MedplumClient_accessToken, undefined, "f");
|
|
137
146
|
__classPrivateFieldSet(this, _MedplumClient_refreshToken, undefined, "f");
|
|
@@ -283,11 +292,11 @@ class MedplumClient extends EventTarget {
|
|
|
283
292
|
* @returns Promise to the authentication response.
|
|
284
293
|
*/
|
|
285
294
|
async startNewUser(newUserRequest) {
|
|
286
|
-
await this.startPkce();
|
|
295
|
+
const { codeChallengeMethod, codeChallenge } = await this.startPkce();
|
|
287
296
|
return this.post('auth/newuser', {
|
|
288
297
|
...newUserRequest,
|
|
289
|
-
codeChallengeMethod
|
|
290
|
-
codeChallenge
|
|
298
|
+
codeChallengeMethod,
|
|
299
|
+
codeChallenge,
|
|
291
300
|
});
|
|
292
301
|
}
|
|
293
302
|
/**
|
|
@@ -319,13 +328,10 @@ class MedplumClient extends EventTarget {
|
|
|
319
328
|
* @returns Promise to the authentication response.
|
|
320
329
|
*/
|
|
321
330
|
async startLogin(loginRequest) {
|
|
322
|
-
const { codeChallenge, codeChallengeMethod } = this.getCodeChallenge(loginRequest);
|
|
323
331
|
return this.post('auth/login', {
|
|
324
|
-
...loginRequest,
|
|
332
|
+
...(await this.ensureCodeChallenge(loginRequest)),
|
|
325
333
|
clientId: loginRequest.clientId ?? __classPrivateFieldGet(this, _MedplumClient_clientId, "f"),
|
|
326
334
|
scope: loginRequest.scope,
|
|
327
|
-
codeChallengeMethod,
|
|
328
|
-
codeChallenge,
|
|
329
335
|
});
|
|
330
336
|
}
|
|
331
337
|
/**
|
|
@@ -337,30 +343,25 @@ class MedplumClient extends EventTarget {
|
|
|
337
343
|
* @returns Promise to the authentication response.
|
|
338
344
|
*/
|
|
339
345
|
async startGoogleLogin(loginRequest) {
|
|
340
|
-
const { codeChallenge, codeChallengeMethod } = this.getCodeChallenge(loginRequest);
|
|
341
346
|
return this.post('auth/google', {
|
|
342
|
-
...loginRequest,
|
|
347
|
+
...(await this.ensureCodeChallenge(loginRequest)),
|
|
343
348
|
clientId: loginRequest.clientId ?? __classPrivateFieldGet(this, _MedplumClient_clientId, "f"),
|
|
344
349
|
scope: loginRequest.scope,
|
|
345
|
-
codeChallengeMethod,
|
|
346
|
-
codeChallenge,
|
|
347
350
|
});
|
|
348
351
|
}
|
|
349
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Returns the PKCE code challenge and method.
|
|
354
|
+
* If the login request already includes a code challenge, it is returned.
|
|
355
|
+
* Otherwise, a new PKCE code challenge is generated.
|
|
356
|
+
* @category Authentication
|
|
357
|
+
* @param loginRequest The original login request.
|
|
358
|
+
* @returns The PKCE code challenge and method.
|
|
359
|
+
*/
|
|
360
|
+
async ensureCodeChallenge(loginRequest) {
|
|
350
361
|
if (loginRequest.codeChallenge) {
|
|
351
|
-
return
|
|
352
|
-
codeChallenge: loginRequest.codeChallenge,
|
|
353
|
-
codeChallengeMethod: loginRequest.codeChallengeMethod,
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
const codeChallenge = sessionStorage.getItem('codeChallenge');
|
|
357
|
-
if (codeChallenge) {
|
|
358
|
-
return {
|
|
359
|
-
codeChallenge,
|
|
360
|
-
codeChallengeMethod: 'S256',
|
|
361
|
-
};
|
|
362
|
+
return loginRequest;
|
|
362
363
|
}
|
|
363
|
-
return {};
|
|
364
|
+
return { ...loginRequest, ...(await this.startPkce()) };
|
|
364
365
|
}
|
|
365
366
|
/**
|
|
366
367
|
* Signs out locally.
|
|
@@ -375,12 +376,13 @@ class MedplumClient extends EventTarget {
|
|
|
375
376
|
* Returns true if the user is signed in.
|
|
376
377
|
* This may result in navigating away to the sign in page.
|
|
377
378
|
* @category Authentication
|
|
379
|
+
* @param loginParams Optional login parameters.
|
|
378
380
|
*/
|
|
379
|
-
async signInWithRedirect() {
|
|
381
|
+
async signInWithRedirect(loginParams) {
|
|
380
382
|
const urlParams = new URLSearchParams(window.location.search);
|
|
381
383
|
const code = urlParams.get('code');
|
|
382
384
|
if (!code) {
|
|
383
|
-
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_requestAuthorization).call(this);
|
|
385
|
+
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_requestAuthorization).call(this, loginParams);
|
|
384
386
|
return undefined;
|
|
385
387
|
}
|
|
386
388
|
else {
|
|
@@ -1233,13 +1235,11 @@ class MedplumClient extends EventTarget {
|
|
|
1233
1235
|
* @category Authentication
|
|
1234
1236
|
*/
|
|
1235
1237
|
async setActiveLogin(login) {
|
|
1238
|
+
this.clearActiveLogin();
|
|
1236
1239
|
__classPrivateFieldSet(this, _MedplumClient_accessToken, login.accessToken, "f");
|
|
1237
1240
|
__classPrivateFieldSet(this, _MedplumClient_refreshToken, login.refreshToken, "f");
|
|
1238
|
-
__classPrivateFieldSet(this, _MedplumClient_profile, undefined, "f");
|
|
1239
|
-
__classPrivateFieldSet(this, _MedplumClient_config, undefined, "f");
|
|
1240
1241
|
__classPrivateFieldGet(this, _MedplumClient_storage, "f").setObject('activeLogin', login);
|
|
1241
1242
|
__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_addLogin).call(this, login);
|
|
1242
|
-
__classPrivateFieldGet(this, _MedplumClient_requestCache, "f").clear();
|
|
1243
1243
|
__classPrivateFieldSet(this, _MedplumClient_refreshPromise, undefined, "f");
|
|
1244
1244
|
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refreshProfile).call(this);
|
|
1245
1245
|
}
|
|
@@ -1318,6 +1318,7 @@ class MedplumClient extends EventTarget {
|
|
|
1318
1318
|
const arrayHash = await encryptSHA256(codeVerifier);
|
|
1319
1319
|
const codeChallenge = arrayBufferToBase64(arrayHash).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
1320
1320
|
sessionStorage.setItem('codeChallenge', codeChallenge);
|
|
1321
|
+
return { codeChallengeMethod: 'S256', codeChallenge };
|
|
1321
1322
|
}
|
|
1322
1323
|
/**
|
|
1323
1324
|
* Processes an OAuth authorization code.
|
|
@@ -1453,7 +1454,7 @@ async function _MedplumClient_request(method, url, options = {}) {
|
|
|
1453
1454
|
if (__classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_refresh).call(this)) {
|
|
1454
1455
|
return __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_request).call(this, method, url, options);
|
|
1455
1456
|
}
|
|
1456
|
-
this.
|
|
1457
|
+
this.clearActiveLogin();
|
|
1457
1458
|
if (__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f")) {
|
|
1458
1459
|
__classPrivateFieldGet(this, _MedplumClient_onUnauthenticated, "f").call(this);
|
|
1459
1460
|
}
|
|
@@ -1464,15 +1465,16 @@ async function _MedplumClient_request(method, url, options = {}) {
|
|
|
1464
1465
|
* Clears all auth state including local storage and session storage.
|
|
1465
1466
|
* See: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint
|
|
1466
1467
|
*/
|
|
1467
|
-
async function _MedplumClient_requestAuthorization() {
|
|
1468
|
-
await this.
|
|
1468
|
+
async function _MedplumClient_requestAuthorization(loginParams) {
|
|
1469
|
+
const loginRequest = await this.ensureCodeChallenge(loginParams || {});
|
|
1469
1470
|
const url = new URL(__classPrivateFieldGet(this, _MedplumClient_authorizeUrl, "f"));
|
|
1470
1471
|
url.searchParams.set('response_type', 'code');
|
|
1471
1472
|
url.searchParams.set('state', sessionStorage.getItem('pkceState'));
|
|
1472
|
-
url.searchParams.set('client_id', __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
1473
|
-
url.searchParams.set('redirect_uri', getBaseUrl());
|
|
1474
|
-
url.searchParams.set('code_challenge_method',
|
|
1475
|
-
url.searchParams.set('code_challenge',
|
|
1473
|
+
url.searchParams.set('client_id', loginRequest.clientId || __classPrivateFieldGet(this, _MedplumClient_clientId, "f"));
|
|
1474
|
+
url.searchParams.set('redirect_uri', loginRequest.redirectUri || getBaseUrl());
|
|
1475
|
+
url.searchParams.set('code_challenge_method', loginRequest.codeChallengeMethod);
|
|
1476
|
+
url.searchParams.set('code_challenge', loginRequest.codeChallenge);
|
|
1477
|
+
url.searchParams.set('scope', loginRequest.scope || 'openid profile');
|
|
1476
1478
|
window.location.assign(url.toString());
|
|
1477
1479
|
}, _MedplumClient_refresh = function _MedplumClient_refresh() {
|
|
1478
1480
|
if (__classPrivateFieldGet(this, _MedplumClient_refreshPromise, "f")) {
|
|
@@ -1498,20 +1500,19 @@ async function _MedplumClient_requestAuthorization() {
|
|
|
1498
1500
|
* @param formBody Token parameters in URL encoded format.
|
|
1499
1501
|
*/
|
|
1500
1502
|
async function _MedplumClient_fetchTokens(formBody) {
|
|
1501
|
-
|
|
1503
|
+
const response = await __classPrivateFieldGet(this, _MedplumClient_fetch, "f").call(this, __classPrivateFieldGet(this, _MedplumClient_tokenUrl, "f"), {
|
|
1502
1504
|
method: 'POST',
|
|
1503
1505
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1504
1506
|
body: formBody,
|
|
1505
1507
|
credentials: 'include',
|
|
1506
|
-
})
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
.then(() => this.getProfile());
|
|
1508
|
+
});
|
|
1509
|
+
if (!response.ok) {
|
|
1510
|
+
this.clearActiveLogin();
|
|
1511
|
+
throw new Error('Failed to fetch tokens');
|
|
1512
|
+
}
|
|
1513
|
+
const tokens = await response.json();
|
|
1514
|
+
await __classPrivateFieldGet(this, _MedplumClient_instances, "m", _MedplumClient_verifyTokens).call(this, tokens);
|
|
1515
|
+
return this.getProfile();
|
|
1515
1516
|
}, _MedplumClient_verifyTokens =
|
|
1516
1517
|
/**
|
|
1517
1518
|
* Verifies the tokens received from the auth server.
|
|
@@ -1524,15 +1525,15 @@ async function _MedplumClient_verifyTokens(tokens) {
|
|
|
1524
1525
|
// Verify token has not expired
|
|
1525
1526
|
const tokenPayload = parseJWTPayload(token);
|
|
1526
1527
|
if (Date.now() >= tokenPayload.exp * 1000) {
|
|
1527
|
-
this.
|
|
1528
|
+
this.clearActiveLogin();
|
|
1528
1529
|
throw new Error('Token expired');
|
|
1529
1530
|
}
|
|
1530
1531
|
// Verify app_client_id
|
|
1531
1532
|
if (__classPrivateFieldGet(this, _MedplumClient_clientId, "f") && tokenPayload.client_id !== __classPrivateFieldGet(this, _MedplumClient_clientId, "f")) {
|
|
1532
|
-
this.
|
|
1533
|
+
this.clearActiveLogin();
|
|
1533
1534
|
throw new Error('Token was not issued for this audience');
|
|
1534
1535
|
}
|
|
1535
|
-
|
|
1536
|
+
return this.setActiveLogin({
|
|
1536
1537
|
accessToken: token,
|
|
1537
1538
|
refreshToken: tokens.refresh_token,
|
|
1538
1539
|
project: tokens.project,
|