@ovok/core 0.4.37 → 0.4.38
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/client/auth/account-storage.d.ts +1 -0
- package/dist/client/auth/account-storage.js +2 -1
- package/dist/{utils → client}/auth/exchange-code.d.ts +1 -1
- package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.js +5 -17
- package/dist/{utils → client}/auth/handle-tenant-code.d.ts +1 -1
- package/dist/{utils → client}/auth/handle-tenant-code.js +1 -1
- package/dist/client/auth/methods/appleLogin.js +2 -6
- package/dist/client/auth/methods/changePassword.js +2 -1
- package/dist/client/auth/methods/getSessions.js +2 -1
- package/dist/client/auth/methods/googleLogin.js +2 -6
- package/dist/client/auth/methods/login.js +26 -44
- package/dist/client/auth/methods/logout.js +2 -1
- package/dist/client/auth/methods/requestDeleteUser.js +5 -4
- package/dist/client/auth/methods/revokeSessions.js +2 -1
- package/dist/client/auth/methods/socialLogin.d.ts +7 -0
- package/dist/client/auth/methods/socialLogin.js +11 -0
- package/dist/client/auth/session-fetch.js +5 -6
- package/dist/client/auth/session-http.js +8 -19
- package/dist/client/auth/sha256.d.ts +2 -0
- package/dist/client/auth/sha256.js +65 -0
- package/dist/{utils → client}/auth/start-login.d.ts +2 -2
- package/dist/client/change-history/methods.js +2 -1
- package/dist/client/cms/methods.js +16 -28
- package/dist/client/consent/methods.js +3 -2
- package/dist/client/email/methods.js +2 -1
- package/dist/client/errors/ovok-error-utils.d.ts +6 -0
- package/dist/client/errors/ovok-error-utils.js +10 -1
- package/dist/client/errors/ovok-error.js +6 -13
- package/dist/client/errors/rate-limit-error.js +2 -6
- package/dist/client/experience/methods.js +13 -12
- package/dist/client/invites/methods.js +8 -12
- package/dist/client/notifications/methods.js +4 -3
- package/dist/client/observation/fhir-systems.d.ts +4 -0
- package/dist/client/observation/fhir-systems.js +7 -0
- package/dist/client/observation/methods/generateObservationBodiesByMeasurement.js +23 -29
- package/dist/client/observation/methods/getLatestObservationsByCodes.js +6 -9
- package/dist/client/observation/methods/observationsToMeasurements.js +44 -91
- package/dist/client/observation/services/base-observation-service.js +4 -3
- package/dist/client/observation/services/blood-pressure.js +1 -4
- package/dist/client/observation/services/{baby-sleep.d.ts → component-observation-services.d.ts} +18 -13
- package/dist/client/observation/services/component-observation-services.js +69 -0
- package/dist/client/observation/services/creator.d.ts +1 -2
- package/dist/client/observation/services/creator.js +14 -18
- package/dist/client/observation/services/ecg-observation-fragments.js +5 -6
- package/dist/client/observation/services/ecg.js +3 -2
- package/dist/client/observation/services/lipid-panel.js +8 -12
- package/dist/client/observation/services/measurement-time.d.ts +4 -0
- package/dist/client/observation/services/measurement-time.js +6 -0
- package/dist/client/observation/services/single-value-services.d.ts +5 -0
- package/dist/client/observation/services/single-value-services.js +6 -1
- package/dist/client/observation/services/uric-acid-service.js +6 -7
- package/dist/client/observation/types/generate-measurement/HealthConnectTypes.d.ts +2 -458
- package/dist/client/observation/types/generate-measurement/HealthConnectTypes.js +0 -6
- package/dist/client/offline/offline-measurement-queue.js +31 -28
- package/dist/client/ovok-client.d.ts +5 -2
- package/dist/client/ovok-client.js +46 -42
- package/dist/client/patient/methods.js +25 -25
- package/dist/client/patient-sharing/methods.js +4 -3
- package/dist/client/platform/methods.js +17 -16
- package/dist/client/platform/types.d.ts +3 -6
- package/dist/client/questionnaire-response/methods/createQuestionnaireResponseWithObservations.js +2 -1
- package/dist/client/questionnaire-response/methods/submitQuestionnaireResponse.js +2 -1
- package/dist/client/request-options.d.ts +2 -0
- package/dist/client/request-options.js +6 -0
- package/dist/client/signals/errors.js +2 -6
- package/dist/client/signals/methods.js +14 -13
- package/dist/client/slim/live-read.d.ts +4 -0
- package/dist/client/slim/live-read.js +87 -0
- package/dist/client/slim/methods.d.ts +3 -0
- package/dist/client/slim/methods.js +63 -146
- package/dist/client/storage.js +3 -2
- package/dist/conformance/capability-requirements.js +1 -1
- package/dist/conformance/capability.js +8 -7
- package/dist/hooks/account-hooks.js +1 -1
- package/dist/hooks/api-hooks.d.ts +5 -6
- package/dist/hooks/api-hooks.js +10 -24
- package/dist/hooks/cms-hooks.js +8 -13
- package/dist/hooks/experience-hooks.js +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +4 -4
- package/dist/hooks/invites-hooks.js +0 -1
- package/dist/hooks/notification-hooks.d.ts +4 -1
- package/dist/hooks/notification-hooks.js +28 -35
- package/dist/hooks/notification-invalidation.d.ts +1 -1
- package/dist/hooks/notification-note-hooks.js +13 -28
- package/dist/hooks/observation-hooks.js +2 -4
- package/dist/hooks/patient-hooks.js +1 -1
- package/dist/hooks/patient-sharing-hooks.js +1 -2
- package/dist/hooks/poll-hooks.js +10 -8
- package/dist/hooks/signals-alert-hooks.js +4 -6
- package/package.json +1 -1
- package/dist/client/observation/services/baby-sleep.js +0 -69
- package/dist/client/observation/services/respiratory-therapy.d.ts +0 -28
- package/dist/client/observation/services/respiratory-therapy.js +0 -69
- package/dist/utils/auth/sha256.d.ts +0 -8
- package/dist/utils/auth/sha256.js +0 -300
- /package/dist/{utils → client}/auth/exchange-code.js +0 -0
- /package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.d.ts +0 -0
- /package/dist/{utils → client}/auth/start-login.js +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IClientStorage } from "@medplum/core";
|
|
2
2
|
export declare const LOGINS_STORAGE_KEY = "logins";
|
|
3
|
+
export declare const ACTIVE_LOGIN_STORAGE_KEY = "activeLogin";
|
|
3
4
|
export declare const EXPIRED_ACCOUNTS_STORAGE_KEY = "ovok.core.expired-accounts";
|
|
4
5
|
export declare const setStoredAccountExpired: (storage: IClientStorage, accountId: string, expired: boolean) => void;
|
|
5
6
|
export declare const removeStoredAccount: (storage: IClientStorage, accountId: string) => void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.removeStoredAccount = exports.setStoredAccountExpired = exports.EXPIRED_ACCOUNTS_STORAGE_KEY = exports.LOGINS_STORAGE_KEY = void 0;
|
|
3
|
+
exports.removeStoredAccount = exports.setStoredAccountExpired = exports.EXPIRED_ACCOUNTS_STORAGE_KEY = exports.ACTIVE_LOGIN_STORAGE_KEY = exports.LOGINS_STORAGE_KEY = void 0;
|
|
4
4
|
exports.LOGINS_STORAGE_KEY = "logins";
|
|
5
|
+
exports.ACTIVE_LOGIN_STORAGE_KEY = "activeLogin";
|
|
5
6
|
exports.EXPIRED_ACCOUNTS_STORAGE_KEY = "ovok.core.expired-accounts";
|
|
6
7
|
const setStoredAccountExpired = (storage, accountId, expired) => {
|
|
7
8
|
var _a;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MedplumRequestOptions } from "@medplum/core";
|
|
2
|
-
import { OvokClient } from "../../client/ovok-client";
|
|
3
2
|
import { AuthResponse } from "../../types";
|
|
3
|
+
import { OvokClient } from "../ovok-client";
|
|
4
4
|
type ExchangeCodeProps = {
|
|
5
5
|
client: OvokClient;
|
|
6
6
|
sessionCode: string;
|
|
@@ -6,26 +6,16 @@ function generateRandomString(length) {
|
|
|
6
6
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
7
7
|
let result = "";
|
|
8
8
|
const randomValues = new Uint8Array(length);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
else {
|
|
13
|
-
for (let i = 0; i < length; i++) {
|
|
14
|
-
randomValues[i] = Math.floor(Math.random() * 256);
|
|
15
|
-
}
|
|
9
|
+
// A predictable verifier defeats PKCE, so there is no Math.random fallback.
|
|
10
|
+
if (typeof crypto === "undefined" || typeof crypto.getRandomValues !== "function") {
|
|
11
|
+
throw new Error("PKCE needs crypto.getRandomValues. On React Native, install a polyfill such as react-native-get-random-values (or use @ovok/native, which provides one).");
|
|
16
12
|
}
|
|
13
|
+
crypto.getRandomValues(randomValues);
|
|
17
14
|
for (let i = 0; i < length; i++) {
|
|
18
15
|
result += charset[randomValues[i] % charset.length];
|
|
19
16
|
}
|
|
20
17
|
return result;
|
|
21
18
|
}
|
|
22
|
-
function hexToUint8Array(hex) {
|
|
23
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
24
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
25
|
-
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
26
|
-
}
|
|
27
|
-
return bytes;
|
|
28
|
-
}
|
|
29
19
|
function base64URLEncode(data) {
|
|
30
20
|
let binary = "";
|
|
31
21
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -35,9 +25,7 @@ function base64URLEncode(data) {
|
|
|
35
25
|
}
|
|
36
26
|
function generateCodeChallengeAndVerifier() {
|
|
37
27
|
const codeVerifier = generateRandomString(128);
|
|
38
|
-
const
|
|
39
|
-
const hashBytes = hexToUint8Array(hexHash);
|
|
40
|
-
const codeChallenge = base64URLEncode(hashBytes);
|
|
28
|
+
const codeChallenge = base64URLEncode((0, sha256_1.sha256Bytes)(codeVerifier));
|
|
41
29
|
return {
|
|
42
30
|
codeVerifier,
|
|
43
31
|
codeChallenge,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StartLoginResponse } from "
|
|
1
|
+
import { StartLoginResponse } from "./types/StartLoginResponse";
|
|
2
2
|
export declare const handleTenantCode: ({ profiles, tenantCode, type, }: {
|
|
3
3
|
profiles: StartLoginResponse["profiles"];
|
|
4
4
|
tenantCode?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleTenantCode = void 0;
|
|
4
|
-
const LoginError_1 = require("
|
|
4
|
+
const LoginError_1 = require("./types/LoginError");
|
|
5
5
|
const handleTenantCode = ({ profiles, tenantCode, type, }) => {
|
|
6
6
|
if (type !== "Practitioner") {
|
|
7
7
|
if (!tenantCode) {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.appleLogin = appleLogin;
|
|
4
|
+
const socialLogin_1 = require("./socialLogin");
|
|
4
5
|
async function appleLogin(body, requestOptions) {
|
|
5
|
-
|
|
6
|
-
const queryParams = new URLSearchParams({
|
|
7
|
-
client: `ClientApplication/${this.socialLoginClientId}`,
|
|
8
|
-
internalId,
|
|
9
|
-
});
|
|
10
|
-
return this.post(`auth/external/apple?${queryParams.toString()}`, rest, "application/json", requestOptions);
|
|
6
|
+
return socialLogin_1.socialLogin.call(this, "apple", body, requestOptions);
|
|
11
7
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.changePassword = changePassword;
|
|
4
|
+
const request_options_1 = require("../../request-options");
|
|
4
5
|
async function changePassword(body, requestOptions) {
|
|
5
6
|
const { oldPassword, newPassword } = body;
|
|
6
|
-
return this.patch("v2/auth/change-password", { oldPassword, newPassword }, ...(
|
|
7
|
+
return this.patch("v2/auth/change-password", { oldPassword, newPassword }, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
7
8
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getSessions = getSessions;
|
|
4
|
+
const request_options_1 = require("../../request-options");
|
|
4
5
|
/** Lists the server-side sessions for the current account. */
|
|
5
6
|
async function getSessions(requestOptions) {
|
|
6
|
-
return this.get("auth/session", ...(
|
|
7
|
+
return this.get("auth/session", ...(0, request_options_1.optionalArg)(requestOptions));
|
|
7
8
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.googleLogin = googleLogin;
|
|
4
|
+
const socialLogin_1 = require("./socialLogin");
|
|
4
5
|
async function googleLogin(body, requestOptions) {
|
|
5
|
-
|
|
6
|
-
const queryParams = new URLSearchParams({
|
|
7
|
-
client: `ClientApplication/${this.socialLoginClientId}`,
|
|
8
|
-
internalId,
|
|
9
|
-
});
|
|
10
|
-
return this.post(`auth/external/google?${queryParams.toString()}`, rest, "application/json", requestOptions);
|
|
6
|
+
return socialLogin_1.socialLogin.call(this, "google", body, requestOptions);
|
|
11
7
|
}
|
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.startTenantLogin = startTenantLogin;
|
|
4
4
|
exports.login = login;
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
5
|
+
const request_options_1 = require("../../request-options");
|
|
6
|
+
const exchange_code_1 = require("../exchange-code");
|
|
7
|
+
const generate_code_challenge_and_verifier_1 = require("../generate-code-challenge-and-verifier");
|
|
8
|
+
const handle_tenant_code_1 = require("../handle-tenant-code");
|
|
9
|
+
const start_login_1 = require("../start-login");
|
|
9
10
|
const LoginError_1 = require("../types/LoginError");
|
|
11
|
+
/** The MFA step: verifying the code yields a session code that `exchange` turns into a session. */
|
|
12
|
+
const mfaStep = (client, type, loginId, exchange, requestOptions) => ({
|
|
13
|
+
nextStep: "mfa",
|
|
14
|
+
verify: async (code) => {
|
|
15
|
+
const { sessionCode } = await client.post(`auth/tenant/${type}/login/mfa`, { loginId, mfaToken: code }, "application/json", requestOptions);
|
|
16
|
+
return exchange(sessionCode);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
10
19
|
/** Starts practitioner sign-in and lets the app choose among returned projects. */
|
|
11
20
|
async function startTenantLogin(body, requestOptions) {
|
|
12
21
|
const { codeVerifier, codeChallenge } = (0, generate_code_challenge_and_verifier_1.generateCodeChallengeAndVerifier)();
|
|
@@ -23,36 +32,23 @@ async function startTenantLogin(body, requestOptions) {
|
|
|
23
32
|
if (!tenantCode) {
|
|
24
33
|
throw new LoginError_1.LoginError("tenant_code_required", "Choose a project before completing sign-in.", profiles);
|
|
25
34
|
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
nextStep: "mfa",
|
|
29
|
-
verify: async (code) => {
|
|
30
|
-
const mfaResponse = await this.post("auth/tenant/Practitioner/login/mfa", { loginId: startResponse.loginId, mfaToken: code }, "application/json", requestOptions);
|
|
31
|
-
return (0, exchange_code_1.exchangeCode)({
|
|
32
|
-
client: this,
|
|
33
|
-
type: "Practitioner",
|
|
34
|
-
sessionCode: mfaResponse.sessionCode,
|
|
35
|
-
codeVerifier,
|
|
36
|
-
tenantCode,
|
|
37
|
-
requestOptions,
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
return (0, exchange_code_1.exchangeCode)({
|
|
35
|
+
const exchange = (sessionCode) => (0, exchange_code_1.exchangeCode)({
|
|
43
36
|
client: this,
|
|
44
37
|
type: "Practitioner",
|
|
45
|
-
sessionCode
|
|
38
|
+
sessionCode,
|
|
46
39
|
codeVerifier,
|
|
47
40
|
tenantCode,
|
|
48
41
|
requestOptions,
|
|
49
42
|
});
|
|
43
|
+
return startResponse.nextStep === "mfa"
|
|
44
|
+
? mfaStep(this, "Practitioner", startResponse.loginId, exchange, requestOptions)
|
|
45
|
+
: exchange(startResponse.sessionCode);
|
|
50
46
|
},
|
|
51
47
|
};
|
|
52
48
|
}
|
|
53
49
|
async function login(body, requestOptions) {
|
|
54
50
|
if (body.type === "Practitioner") {
|
|
55
|
-
const start = await startTenantLogin.call(this, body, ...(
|
|
51
|
+
const start = await startTenantLogin.call(this, body, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
56
52
|
return start.exchange(body.tenantCode);
|
|
57
53
|
}
|
|
58
54
|
const { codeVerifier, codeChallenge } = (0, generate_code_challenge_and_verifier_1.generateCodeChallengeAndVerifier)();
|
|
@@ -62,30 +58,16 @@ async function login(body, requestOptions) {
|
|
|
62
58
|
codeChallenge,
|
|
63
59
|
requestOptions,
|
|
64
60
|
});
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
nextStep: "mfa",
|
|
68
|
-
verify: async (code) => {
|
|
69
|
-
const mfaResponse = await this.post(`auth/tenant/${body.type}/login/mfa`, {
|
|
70
|
-
loginId: startResponse.loginId,
|
|
71
|
-
mfaToken: code,
|
|
72
|
-
}, "application/json", requestOptions);
|
|
73
|
-
return (0, exchange_code_1.exchangeCode)({
|
|
74
|
-
client: this,
|
|
75
|
-
type: "Patient",
|
|
76
|
-
sessionCode: mfaResponse.sessionCode,
|
|
77
|
-
codeVerifier,
|
|
78
|
-
requestOptions,
|
|
79
|
-
});
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
(0, handle_tenant_code_1.handleTenantCode)({ profiles: startResponse.profiles, tenantCode: body.tenantCode, type: "Patient" });
|
|
84
|
-
return (0, exchange_code_1.exchangeCode)({
|
|
61
|
+
const exchange = (sessionCode) => (0, exchange_code_1.exchangeCode)({
|
|
85
62
|
client: this,
|
|
86
63
|
type: "Patient",
|
|
87
|
-
sessionCode
|
|
64
|
+
sessionCode,
|
|
88
65
|
codeVerifier,
|
|
89
66
|
requestOptions,
|
|
90
67
|
});
|
|
68
|
+
if (startResponse.nextStep === "mfa") {
|
|
69
|
+
return mfaStep(this, body.type, startResponse.loginId, exchange, requestOptions);
|
|
70
|
+
}
|
|
71
|
+
(0, handle_tenant_code_1.handleTenantCode)({ profiles: startResponse.profiles, tenantCode: body.tenantCode, type: "Patient" });
|
|
72
|
+
return exchange(startResponse.sessionCode);
|
|
91
73
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.logout = logout;
|
|
4
|
+
const request_options_1 = require("../../request-options");
|
|
4
5
|
const account_storage_1 = require("../account-storage");
|
|
5
6
|
/** Revokes the current server session and always removes its local account. */
|
|
6
7
|
async function logout(requestOptions) {
|
|
7
8
|
var _a, _b;
|
|
8
9
|
const accountId = (_b = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
|
|
9
10
|
try {
|
|
10
|
-
await this.delete("auth/session/current", ...(
|
|
11
|
+
await this.delete("auth/session/current", ...(0, request_options_1.optionalArg)(requestOptions));
|
|
11
12
|
}
|
|
12
13
|
finally {
|
|
13
14
|
this.clearActiveLogin();
|
|
@@ -9,8 +9,9 @@ exports.requestDeleteUser = requestDeleteUser;
|
|
|
9
9
|
* @returns {Promise<RequestDeleteUserResponse>} Response containing scheduled deletion status and user information
|
|
10
10
|
*/
|
|
11
11
|
async function requestDeleteUser(body, requestOptions) {
|
|
12
|
-
return this.delete("auth/delete", {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
return this.delete("auth/delete", {
|
|
13
|
+
body: JSON.stringify(body),
|
|
14
|
+
headers: { "Content-Type": "application/json" },
|
|
15
|
+
...requestOptions,
|
|
16
|
+
});
|
|
16
17
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.revokeSessions = revokeSessions;
|
|
4
|
+
const request_options_1 = require("../../request-options");
|
|
4
5
|
/** Revokes the selected server-side session or session group. */
|
|
5
6
|
async function revokeSessions(option, requestOptions) {
|
|
6
|
-
await this.delete(`auth/session/${encodeURIComponent(option)}`, ...(
|
|
7
|
+
await this.delete(`auth/session/${encodeURIComponent(option)}`, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
7
8
|
if (option === "current") {
|
|
8
9
|
this.clearActiveLogin();
|
|
9
10
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MedplumRequestOptions } from "@medplum/core";
|
|
2
|
+
import { OvokClient } from "../../ovok-client";
|
|
3
|
+
import { AppleLoginBody } from "../types/AppleLoginBody";
|
|
4
|
+
import { AuthResponse } from "../types/AuthResponse";
|
|
5
|
+
import { GoogleLoginBody } from "../types/GoogleLoginBody";
|
|
6
|
+
/** Signs in with a provider token through the social-login client. Not bound onto OvokClient itself. */
|
|
7
|
+
export declare function socialLogin(this: OvokClient, provider: "apple" | "google", { internalId, ...rest }: AppleLoginBody | GoogleLoginBody, requestOptions?: MedplumRequestOptions): Promise<AuthResponse>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.socialLogin = socialLogin;
|
|
4
|
+
/** Signs in with a provider token through the social-login client. Not bound onto OvokClient itself. */
|
|
5
|
+
function socialLogin(provider, { internalId, ...rest }, requestOptions) {
|
|
6
|
+
const queryParams = new URLSearchParams({
|
|
7
|
+
client: `ClientApplication/${this.socialLoginClientId}`,
|
|
8
|
+
internalId,
|
|
9
|
+
});
|
|
10
|
+
return this.post(`auth/external/${provider}?${queryParams.toString()}`, rest, "application/json", requestOptions);
|
|
11
|
+
}
|
|
@@ -19,15 +19,14 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
|
|
|
19
19
|
(options === null || options === void 0 ? void 0 : options.method) === "POST" &&
|
|
20
20
|
typeof options.body === "string" &&
|
|
21
21
|
new URLSearchParams(options.body).get("grant_type") === "refresh_token";
|
|
22
|
+
const activeAccountId = () => { var _a, _b; return (_b = (_a = storage.getObject(account_storage_1.ACTIVE_LOGIN_STORAGE_KEY)) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference; };
|
|
22
23
|
const markActiveAccountExpired = () => {
|
|
23
|
-
|
|
24
|
-
const accountId = (_b = (_a = storage.getObject("activeLogin")) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
|
|
24
|
+
const accountId = activeAccountId();
|
|
25
25
|
if (accountId) {
|
|
26
26
|
(0, account_storage_1.setStoredAccountExpired)(storage, accountId, true);
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
const fetchOnce = async (url, options) => {
|
|
30
|
-
var _a, _b;
|
|
31
30
|
const response = await (0, session_http_1.fetchWithTimeout)(fetchImplementation, url, options, config.requestTimeoutMs);
|
|
32
31
|
const requestUrl = new URL(url, normalizedBaseUrl);
|
|
33
32
|
if (isTokenRefresh(url, options)) {
|
|
@@ -36,7 +35,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
|
|
|
36
35
|
try {
|
|
37
36
|
invalidGrant = (await response.clone().json()).error === "invalid_grant";
|
|
38
37
|
}
|
|
39
|
-
catch (
|
|
38
|
+
catch (_a) {
|
|
40
39
|
// Do not sign out unless the response confirms that the refresh token was revoked.
|
|
41
40
|
}
|
|
42
41
|
if (!invalidGrant) {
|
|
@@ -51,7 +50,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
|
|
|
51
50
|
markActiveAccountExpired();
|
|
52
51
|
}
|
|
53
52
|
else if (response.ok) {
|
|
54
|
-
const accountId = (
|
|
53
|
+
const accountId = activeAccountId();
|
|
55
54
|
if (accountId) {
|
|
56
55
|
(0, account_storage_1.setStoredAccountExpired)(storage, accountId, false);
|
|
57
56
|
}
|
|
@@ -82,7 +81,7 @@ function createSessionFetch({ config, storage, fetchImplementation, normalizedBa
|
|
|
82
81
|
}
|
|
83
82
|
return response;
|
|
84
83
|
}
|
|
85
|
-
const login = storage.getObject(
|
|
84
|
+
const login = storage.getObject(account_storage_1.ACTIVE_LOGIN_STORAGE_KEY);
|
|
86
85
|
if (terminalRefreshError) {
|
|
87
86
|
if ((login === null || login === void 0 ? void 0 : login.accessToken) && login.accessToken !== terminalRefreshToken) {
|
|
88
87
|
terminalRefreshToken = undefined;
|
|
@@ -16,11 +16,7 @@ const retryAfterMs = (response) => {
|
|
|
16
16
|
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : undefined;
|
|
17
17
|
};
|
|
18
18
|
exports.retryAfterMs = retryAfterMs;
|
|
19
|
-
const timeoutError = () =>
|
|
20
|
-
const error = new Error("The request timed out.");
|
|
21
|
-
error.name = "TimeoutError";
|
|
22
|
-
return error;
|
|
23
|
-
};
|
|
19
|
+
const timeoutError = () => (0, ovok_error_utils_1.namedError)("TimeoutError", "The request timed out.");
|
|
24
20
|
const withHttpErrorMetadata = async (response) => {
|
|
25
21
|
var _a, _b;
|
|
26
22
|
if (response.ok || typeof response.clone !== "function") {
|
|
@@ -94,9 +90,7 @@ exports.withHttpErrorMetadata = withHttpErrorMetadata;
|
|
|
94
90
|
const waitForRetry = (milliseconds, signal) => new Promise((resolve, reject) => {
|
|
95
91
|
var _a;
|
|
96
92
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
97
|
-
|
|
98
|
-
error.name = "AbortError";
|
|
99
|
-
reject((_a = signal.reason) !== null && _a !== void 0 ? _a : error);
|
|
93
|
+
reject((_a = signal.reason) !== null && _a !== void 0 ? _a : (0, ovok_error_utils_1.namedError)("AbortError", "Token refresh aborted"));
|
|
100
94
|
return;
|
|
101
95
|
}
|
|
102
96
|
const timer = setTimeout(() => {
|
|
@@ -106,9 +100,7 @@ const waitForRetry = (milliseconds, signal) => new Promise((resolve, reject) =>
|
|
|
106
100
|
const abort = () => {
|
|
107
101
|
var _a;
|
|
108
102
|
clearTimeout(timer);
|
|
109
|
-
|
|
110
|
-
error.name = "AbortError";
|
|
111
|
-
reject((_a = signal === null || signal === void 0 ? void 0 : signal.reason) !== null && _a !== void 0 ? _a : error);
|
|
103
|
+
reject((_a = signal === null || signal === void 0 ? void 0 : signal.reason) !== null && _a !== void 0 ? _a : (0, ovok_error_utils_1.namedError)("AbortError", "Token refresh aborted"));
|
|
112
104
|
};
|
|
113
105
|
signal === null || signal === void 0 ? void 0 : signal.addEventListener("abort", abort, { once: true });
|
|
114
106
|
});
|
|
@@ -116,17 +108,14 @@ exports.waitForRetry = waitForRetry;
|
|
|
116
108
|
const isTemporaryRefreshFailure = (status) => status === 429 || (status >= 500 && status <= 599);
|
|
117
109
|
exports.isTemporaryRefreshFailure = isTemporaryRefreshFailure;
|
|
118
110
|
const refreshUnavailableError = (response) => {
|
|
119
|
-
|
|
120
|
-
error.name = "NetworkError";
|
|
121
|
-
Object.assign(error, { cause: response });
|
|
122
|
-
return error;
|
|
111
|
+
return Object.assign((0, ovok_error_utils_1.namedError)("NetworkError", "Token refresh is temporarily unavailable"), { cause: response });
|
|
123
112
|
};
|
|
124
113
|
exports.refreshUnavailableError = refreshUnavailableError;
|
|
125
114
|
const refreshResponseError = (response) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
115
|
+
return Object.assign((0, ovok_error_utils_1.namedError)("TokenRefreshError", `Token refresh returned HTTP ${response.status}`), {
|
|
116
|
+
status: response.status,
|
|
117
|
+
cause: response,
|
|
118
|
+
});
|
|
130
119
|
};
|
|
131
120
|
exports.refreshResponseError = refreshResponseError;
|
|
132
121
|
const isNetworkError = (error) => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SHA-256 (FIPS 180-4), synchronous and dependency-free: React Native has no `crypto.subtle`,
|
|
3
|
+
// and the PKCE login start is synchronous.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.sha256Bytes = void 0;
|
|
6
|
+
const K = new Uint32Array([
|
|
7
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
8
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
9
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
10
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
11
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
12
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
13
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
14
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
15
|
+
]);
|
|
16
|
+
const rotr = (x, n) => (x >>> n) | (x << (32 - n));
|
|
17
|
+
/** SHA-256 digest of an ASCII string (a PKCE verifier is `[A-Za-z0-9-._~]`). */
|
|
18
|
+
const sha256Bytes = (ascii) => {
|
|
19
|
+
const blocks = new Uint8Array(Math.ceil((ascii.length + 9) / 64) * 64);
|
|
20
|
+
const view = new DataView(blocks.buffer);
|
|
21
|
+
for (let i = 0; i < ascii.length; i += 1) {
|
|
22
|
+
const code = ascii.charCodeAt(i);
|
|
23
|
+
if (code > 0x7f) {
|
|
24
|
+
throw new RangeError("sha256Bytes hashes ASCII input only.");
|
|
25
|
+
}
|
|
26
|
+
blocks[i] = code;
|
|
27
|
+
}
|
|
28
|
+
blocks[ascii.length] = 0x80;
|
|
29
|
+
view.setUint32(blocks.length - 4, ascii.length * 8);
|
|
30
|
+
const hash = new Uint32Array([
|
|
31
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
32
|
+
]);
|
|
33
|
+
const w = new Uint32Array(64);
|
|
34
|
+
for (let offset = 0; offset < blocks.length; offset += 64) {
|
|
35
|
+
for (let t = 0; t < 16; t += 1) {
|
|
36
|
+
w[t] = view.getUint32(offset + t * 4);
|
|
37
|
+
}
|
|
38
|
+
for (let t = 16; t < 64; t += 1) {
|
|
39
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ (w[t - 15] >>> 3);
|
|
40
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ (w[t - 2] >>> 10);
|
|
41
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1;
|
|
42
|
+
}
|
|
43
|
+
let [a, b, c, d, e, f, g, h] = hash;
|
|
44
|
+
for (let t = 0; t < 64; t += 1) {
|
|
45
|
+
const t1 = (h + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + ((e & f) ^ (~e & g)) + K[t] + w[t]) >>> 0;
|
|
46
|
+
const t2 = ((rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0;
|
|
47
|
+
h = g;
|
|
48
|
+
g = f;
|
|
49
|
+
f = e;
|
|
50
|
+
e = (d + t1) >>> 0;
|
|
51
|
+
d = c;
|
|
52
|
+
c = b;
|
|
53
|
+
b = a;
|
|
54
|
+
a = (t1 + t2) >>> 0;
|
|
55
|
+
}
|
|
56
|
+
[a, b, c, d, e, f, g, h].forEach((value, index) => {
|
|
57
|
+
hash[index] += value;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const digest = new Uint8Array(32);
|
|
61
|
+
const digestView = new DataView(digest.buffer);
|
|
62
|
+
hash.forEach((word, index) => digestView.setUint32(index * 4, word));
|
|
63
|
+
return digest;
|
|
64
|
+
};
|
|
65
|
+
exports.sha256Bytes = sha256Bytes;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MedplumRequestOptions } from "@medplum/core";
|
|
2
|
-
import { StartLoginResponse } from "../../client/auth/types/StartLoginResponse";
|
|
3
|
-
import { OvokClient } from "../../client/ovok-client";
|
|
4
2
|
import { LoginBody } from "../../types";
|
|
3
|
+
import { OvokClient } from "../ovok-client";
|
|
4
|
+
import { StartLoginResponse } from "./types/StartLoginResponse";
|
|
5
5
|
type StartLoginProps = {
|
|
6
6
|
client: OvokClient;
|
|
7
7
|
body: LoginBody;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.serializeChangeHistoryQuery = serializeChangeHistoryQuery;
|
|
4
4
|
exports.listChangeHistory = listChangeHistory;
|
|
5
|
+
const request_options_1 = require("../request-options");
|
|
5
6
|
function serializeChangeHistoryQuery(params = {}) {
|
|
6
7
|
const search = new URLSearchParams();
|
|
7
8
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -21,5 +22,5 @@ function serializeChangeHistoryQuery(params = {}) {
|
|
|
21
22
|
return encoded ? `?${encoded}` : "";
|
|
22
23
|
}
|
|
23
24
|
function listChangeHistory(params = {}, requestOptions) {
|
|
24
|
-
return this.get(`/v1/slim/change-history${serializeChangeHistoryQuery(params)}`, ...(
|
|
25
|
+
return this.get(`/v1/slim/change-history${serializeChangeHistoryQuery(params)}`, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
25
26
|
}
|
|
@@ -6,31 +6,19 @@ exports.getLegalPage = getLegalPage;
|
|
|
6
6
|
exports.listReleaseNotes = listReleaseNotes;
|
|
7
7
|
exports.getTranslations = getTranslations;
|
|
8
8
|
const sign_in_required_error_1 = require("../errors/sign-in-required-error");
|
|
9
|
+
const query_string_1 = require("../query-string");
|
|
10
|
+
const request_options_1 = require("../request-options");
|
|
9
11
|
const locale_1 = require("./locale");
|
|
10
12
|
const collectionPath = (collection) => `/v1/public/cms/collections/${encodeURIComponent(collection)}/items`;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
params.set("environment", environment);
|
|
21
|
-
}
|
|
22
|
-
if (page !== undefined) {
|
|
23
|
-
params.set("page", String(page));
|
|
24
|
-
}
|
|
25
|
-
if (limit !== undefined) {
|
|
26
|
-
params.set("limit", String(limit));
|
|
27
|
-
}
|
|
28
|
-
if (sort) {
|
|
29
|
-
params.set("sort", sort);
|
|
30
|
-
}
|
|
31
|
-
const query = params.toString();
|
|
32
|
-
return query ? `?${query}` : "";
|
|
33
|
-
};
|
|
13
|
+
// Empty strings count as absent, like undefined.
|
|
14
|
+
const cmsQuery = ({ locale, tenantCode, environment, page, limit, sort }) => (0, query_string_1.toQueryString)({
|
|
15
|
+
locale: locale ? (0, locale_1.toCmsLocale)(locale) : undefined,
|
|
16
|
+
tenant: tenantCode || undefined,
|
|
17
|
+
environment,
|
|
18
|
+
page,
|
|
19
|
+
limit,
|
|
20
|
+
sort: sort || undefined,
|
|
21
|
+
});
|
|
34
22
|
/**
|
|
35
23
|
* Signed out, the backend serves only a few collections, read by tenant code;
|
|
36
24
|
* any other read is a 401, which MedplumClient answers by clearing its
|
|
@@ -53,7 +41,7 @@ async function getCmsDocument(collection, slug, options = {}, requestOptions) {
|
|
|
53
41
|
throw new Error("A CMS document read needs a slug.");
|
|
54
42
|
}
|
|
55
43
|
assertReadable(this, collection, options);
|
|
56
|
-
return this.get(`${collectionPath(collection)}/${encodeURIComponent(slug)}${cmsQuery(options)}`, ...(
|
|
44
|
+
return this.get(`${collectionPath(collection)}/${encodeURIComponent(slug)}${cmsQuery(options)}`, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
57
45
|
}
|
|
58
46
|
/**
|
|
59
47
|
* A page of a CMS collection's published documents, merged with the shared
|
|
@@ -61,17 +49,17 @@ async function getCmsDocument(collection, slug, options = {}, requestOptions) {
|
|
|
61
49
|
*/
|
|
62
50
|
async function listCmsDocuments(collection, options = {}, requestOptions) {
|
|
63
51
|
assertReadable(this, collection, options);
|
|
64
|
-
return this.get(`${collectionPath(collection)}${cmsQuery(options)}`, ...(
|
|
52
|
+
return this.get(`${collectionPath(collection)}${cmsQuery(options)}`, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
65
53
|
}
|
|
66
54
|
/** A legal page ("terms-and-conditions", "data-privacy", …). Readable before sign-in with a tenant code. */
|
|
67
55
|
function getLegalPage(slug, options = {}, requestOptions) {
|
|
68
56
|
var _a;
|
|
69
|
-
return this.getCmsDocument("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true }, ...(
|
|
57
|
+
return this.getCmsDocument("legal-pages", slug, { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true }, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
70
58
|
}
|
|
71
59
|
/** Release notes, newest first unless `sort` says otherwise. Needs a session. */
|
|
72
60
|
function listReleaseNotes(options = {}, requestOptions) {
|
|
73
61
|
var _a;
|
|
74
|
-
return this.listCmsDocuments("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" }, ...(
|
|
62
|
+
return this.listCmsDocuments("release-notes", { ...options, sort: (_a = options.sort) !== null && _a !== void 0 ? _a : "-publishedAt" }, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
75
63
|
}
|
|
76
64
|
/** A translations read takes pages of 50 documents, the CMS's most, and at most this many. */
|
|
77
65
|
const MAX_TRANSLATION_PAGES = 20;
|
|
@@ -127,7 +115,7 @@ async function getTranslations(options = {}, requestOptions) {
|
|
|
127
115
|
const readOptions = { ...options, allowSignedOut: (_a = options.allowSignedOut) !== null && _a !== void 0 ? _a : true };
|
|
128
116
|
const translations = {};
|
|
129
117
|
for (let page = 1; page <= MAX_TRANSLATION_PAGES; page++) {
|
|
130
|
-
const { docs, hasNextPage } = await this.listCmsDocuments("translations", { ...readOptions, page, limit: 50 }, ...(
|
|
118
|
+
const { docs, hasNextPage } = await this.listCmsDocuments("translations", { ...readOptions, page, limit: 50 }, ...(0, request_options_1.optionalArg)(requestOptions));
|
|
131
119
|
addTranslations(translations, docs);
|
|
132
120
|
if (!hasNextPage) {
|
|
133
121
|
break;
|
|
@@ -4,15 +4,16 @@ exports.getLegalConsent = getLegalConsent;
|
|
|
4
4
|
exports.setLegalConsent = setLegalConsent;
|
|
5
5
|
exports.getMedicalConsent = getMedicalConsent;
|
|
6
6
|
exports.setMedicalConsent = setMedicalConsent;
|
|
7
|
+
const request_options_1 = require("../request-options");
|
|
7
8
|
function getLegalConsent(requestOptions) {
|
|
8
|
-
return this.get("/v1/me/consent/legal", ...(
|
|
9
|
+
return this.get("/v1/me/consent/legal", ...(0, request_options_1.optionalArg)(requestOptions));
|
|
9
10
|
}
|
|
10
11
|
function setLegalConsent(accepted, requestOptions) {
|
|
11
12
|
const body = { termsAndConditionsConsent: accepted };
|
|
12
13
|
return this.post("/v1/me/consent/legal", body, undefined, requestOptions);
|
|
13
14
|
}
|
|
14
15
|
function getMedicalConsent(requestOptions) {
|
|
15
|
-
return this.get("/v1/me/consent/medical", ...(
|
|
16
|
+
return this.get("/v1/me/consent/medical", ...(0, request_options_1.optionalArg)(requestOptions));
|
|
16
17
|
}
|
|
17
18
|
function setMedicalConsent(accepted, requestOptions) {
|
|
18
19
|
const body = { medicalSettingsNotificationsConsent: accepted };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getEmailTemplates = getEmailTemplates;
|
|
4
|
+
const request_options_1 = require("../request-options");
|
|
4
5
|
function getEmailTemplates(requestOptions) {
|
|
5
|
-
return this.get("/v1/email/templates", ...(
|
|
6
|
+
return this.get("/v1/email/templates", ...(0, request_options_1.optionalArg)(requestOptions));
|
|
6
7
|
}
|