@icure/api 7.1.22 → 7.1.23
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/icc-api/api/IccAuthApi.d.ts +2 -1
- package/icc-api/api/IccAuthApi.js +3 -2
- package/icc-api/api/IccAuthApi.js.map +1 -1
- package/icc-api/api/IccUserApi.d.ts +6 -0
- package/icc-api/api/IccUserApi.js +13 -0
- package/icc-api/api/IccUserApi.js.map +1 -1
- package/icc-api/api/XHR.d.ts +1 -1
- package/icc-api/api/XHR.js +4 -3
- package/icc-api/api/XHR.js.map +1 -1
- package/icc-x-api/auth/AuthService.d.ts +2 -1
- package/icc-x-api/auth/AuthService.js.map +1 -1
- package/icc-x-api/auth/AuthenticationProvider.d.ts +1 -1
- package/icc-x-api/auth/AuthenticationProvider.js +1 -1
- package/icc-x-api/auth/AuthenticationProvider.js.map +1 -1
- package/icc-x-api/auth/EnsembleAuthService.d.ts +1 -1
- package/icc-x-api/auth/EnsembleAuthService.js +2 -2
- package/icc-x-api/auth/EnsembleAuthService.js.map +1 -1
- package/icc-x-api/auth/JwtAuthService.d.ts +0 -2
- package/icc-x-api/auth/JwtAuthService.js +3 -15
- package/icc-x-api/auth/JwtAuthService.js.map +1 -1
- package/icc-x-api/auth/JwtUtils.d.ts +10 -0
- package/icc-x-api/auth/JwtUtils.js +31 -0
- package/icc-x-api/auth/JwtUtils.js.map +1 -0
- package/icc-x-api/auth/SmartAuthProvider.d.ts +120 -0
- package/icc-x-api/auth/SmartAuthProvider.js +375 -0
- package/icc-x-api/auth/SmartAuthProvider.js.map +1 -0
- package/icc-x-api/index.d.ts +54 -0
- package/icc-x-api/index.js +11 -11
- package/icc-x-api/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SmartAuthProvider = exports.AuthSecretType = void 0;
|
|
13
|
+
const XHR_1 = require("../../icc-api/api/XHR");
|
|
14
|
+
var XHRError = XHR_1.XHR.XHRError;
|
|
15
|
+
const JwtUtils_1 = require("./JwtUtils");
|
|
16
|
+
/**
|
|
17
|
+
* Represents a type of secret that can be used for authentication with iCure.
|
|
18
|
+
*/
|
|
19
|
+
var AuthSecretType;
|
|
20
|
+
(function (AuthSecretType) {
|
|
21
|
+
/**
|
|
22
|
+
* Password chosen by the user.
|
|
23
|
+
*/
|
|
24
|
+
AuthSecretType["PASSWORD"] = "PASSWORD";
|
|
25
|
+
/**
|
|
26
|
+
* Time based one time password provided by authenticator applications, generated on the basis of a timestamp and a shared secret between the iCure
|
|
27
|
+
* server and the authenticator application.
|
|
28
|
+
*/
|
|
29
|
+
AuthSecretType["TWO_FACTOR_AUTHENTICATION_TOKEN"] = "TWO_FACTOR_AUTHENTICATION_TOKEN";
|
|
30
|
+
/**
|
|
31
|
+
* A short-lived iCure token, an internal authentication token that lasts 5 minutes or less. Unlike passwords these tokens usually are generated by
|
|
32
|
+
* some component of iCure, and are not chosen by the user.
|
|
33
|
+
*/
|
|
34
|
+
AuthSecretType["SHORT_LIVED_TOKEN"] = "SHORT_LIVED_TOKEN";
|
|
35
|
+
/**
|
|
36
|
+
* A long-lived iCure token, an internal authentication token that lasts longer than 5 minutes. Unlike passwords these tokens usually are generated
|
|
37
|
+
* by some component of iCure, and are not chosen by the user.
|
|
38
|
+
*/
|
|
39
|
+
AuthSecretType["LONG_LIVED_TOKEN"] = "LONG_LIVED_TOKEN";
|
|
40
|
+
/**
|
|
41
|
+
* A token provided by an external authentication provider (e.g. Oauth/Google).
|
|
42
|
+
* Not yet in use.
|
|
43
|
+
*/
|
|
44
|
+
// EXTERNAL_AUTHENTICATION = 'EXTERNAL_AUTHENTICATION',
|
|
45
|
+
/**
|
|
46
|
+
* A special case of external authentication where the provider is a digital identity provider.
|
|
47
|
+
* Not yet in use.
|
|
48
|
+
*/
|
|
49
|
+
// DIGITAL_ID = 'DIGITAL_ID',
|
|
50
|
+
})(AuthSecretType = exports.AuthSecretType || (exports.AuthSecretType = {}));
|
|
51
|
+
// Here starts internal entities that should not be used directly.
|
|
52
|
+
/**
|
|
53
|
+
* @internal this class is meant for internal use only and may be changed without notice. The SmartAuthProvider will be initialised automatically
|
|
54
|
+
* by the iCure api depending on the authentication options you provide.
|
|
55
|
+
*
|
|
56
|
+
* An authentication provider that automatically requests secrets for authentication as needed.
|
|
57
|
+
*
|
|
58
|
+
* This authentication provider can be initialised already with some secrets or tokens, and the provider will cache them and use them as needed for
|
|
59
|
+
* as long as they remain valid. If at any point however the provider needs an updated secret or a secret of a different kind it will automatically
|
|
60
|
+
* request this to the {@link SmartAuthProvider} to get the secret.
|
|
61
|
+
*
|
|
62
|
+
* An advantage of using this provider over others is that in case all the cached tokens and secrets were to expire while performing a request,
|
|
63
|
+
* instead of having the request fail the provider will ask for new secrets from the {@link SmartAuthProvider} and the request will automatically
|
|
64
|
+
* be retried with the new secret. Additionally, the provider may request updated secrets also for performing some sensitive operations (e.g. changing
|
|
65
|
+
* password of the user) even if the cached tokens and/or did not expire. This could be the case for example if the cached secret is a long-lived
|
|
66
|
+
* token, but in order to change the password the user needs to provide his current password.
|
|
67
|
+
*
|
|
68
|
+
* Note that in this context the cache of secrets and token is in memory only, and is not persisted in any way. Different instances of this provider
|
|
69
|
+
* will not share the same cache.
|
|
70
|
+
*
|
|
71
|
+
*/
|
|
72
|
+
class SmartAuthProvider {
|
|
73
|
+
/**
|
|
74
|
+
* Initialises a {@link SmartAuthProvider}.
|
|
75
|
+
* @param authApi an "anonymous" {@link IccAuthApi} to use for authentication.
|
|
76
|
+
* @param login
|
|
77
|
+
* @param secretProvider
|
|
78
|
+
* @param props optional initialisation properties.
|
|
79
|
+
*/
|
|
80
|
+
static initialise(authApi, login, secretProvider, props = {}) {
|
|
81
|
+
return new SmartAuthProvider(new TokenProvider(login, props.loginGroupId, props.initialSecret ? { value: props.initialSecret, type: undefined } : undefined, props.initialAuthToken, props.initialRefreshToken, authApi, secretProvider), props.loginGroupId);
|
|
82
|
+
}
|
|
83
|
+
constructor(tokenProvider, groupId) {
|
|
84
|
+
this.tokenProvider = tokenProvider;
|
|
85
|
+
this.groupId = groupId;
|
|
86
|
+
}
|
|
87
|
+
getAuthService() {
|
|
88
|
+
return new SmartAuthService(this.tokenProvider);
|
|
89
|
+
}
|
|
90
|
+
switchGroup(newGroupId, matches) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
if (newGroupId == this.groupId)
|
|
93
|
+
return Promise.resolve(this);
|
|
94
|
+
if (!matches.find((match) => match.groupId == newGroupId))
|
|
95
|
+
throw new Error('New group id not found in matches.');
|
|
96
|
+
const switchedProvider = yield this.tokenProvider.switchedGroup(newGroupId);
|
|
97
|
+
return new SmartAuthProvider(switchedProvider, this.groupId);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
getIcureTokens() {
|
|
101
|
+
return Promise.resolve(undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.SmartAuthProvider = SmartAuthProvider;
|
|
105
|
+
var ServerAuthenticationClass;
|
|
106
|
+
(function (ServerAuthenticationClass) {
|
|
107
|
+
// DIGITAL_ID = 60,
|
|
108
|
+
ServerAuthenticationClass[ServerAuthenticationClass["TWO_FACTOR_AUTHENTICATION"] = 50] = "TWO_FACTOR_AUTHENTICATION";
|
|
109
|
+
ServerAuthenticationClass[ServerAuthenticationClass["SHORT_LIVED_TOKEN"] = 40] = "SHORT_LIVED_TOKEN";
|
|
110
|
+
// EXTERNAL_AUTHENTICATION = 30,
|
|
111
|
+
ServerAuthenticationClass[ServerAuthenticationClass["PASSWORD"] = 20] = "PASSWORD";
|
|
112
|
+
ServerAuthenticationClass[ServerAuthenticationClass["LONG_LIVED_TOKEN"] = 10] = "LONG_LIVED_TOKEN";
|
|
113
|
+
})(ServerAuthenticationClass || (ServerAuthenticationClass = {}));
|
|
114
|
+
class TokenProvider {
|
|
115
|
+
constructor(login, groupId, currentLongLivedSecret, cachedToken, cachedRefreshToken, authApi, authSecretProvider) {
|
|
116
|
+
this.login = login;
|
|
117
|
+
this.groupId = groupId;
|
|
118
|
+
this.currentLongLivedSecret = currentLongLivedSecret;
|
|
119
|
+
this.cachedToken = cachedToken;
|
|
120
|
+
this.cachedRefreshToken = cachedRefreshToken;
|
|
121
|
+
this.authApi = authApi;
|
|
122
|
+
this.authSecretProvider = authSecretProvider;
|
|
123
|
+
}
|
|
124
|
+
getCachedOrRefreshedOrNewToken() {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
126
|
+
if (!!this.cachedToken && !(0, JwtUtils_1.isJwtInvalidOrExpired)(this.cachedToken)) {
|
|
127
|
+
return { token: this.cachedToken, type: RetrievedTokenType.CACHED };
|
|
128
|
+
}
|
|
129
|
+
else if (!!this.cachedRefreshToken && !(0, JwtUtils_1.isJwtInvalidOrExpired)(this.cachedRefreshToken)) {
|
|
130
|
+
return this.refreshAndCacheToken(this.cachedRefreshToken);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return { token: yield this.getAndCacheNewToken(undefined), type: RetrievedTokenType.NEW };
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
getNewTokenWithClass(minimumAuthenticationClass) {
|
|
138
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
return yield this.getAndCacheNewToken(minimumAuthenticationClass);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
getAndCacheNewToken(minimumAuthenticationClassLevel) {
|
|
143
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
144
|
+
const { token, refreshToken } = yield this.getNewToken(minimumAuthenticationClassLevel !== null && minimumAuthenticationClassLevel !== void 0 ? minimumAuthenticationClassLevel : 0);
|
|
145
|
+
this.cachedToken = token;
|
|
146
|
+
this.cachedRefreshToken = refreshToken;
|
|
147
|
+
return token;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
refreshAndCacheToken(refreshToken) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
return yield this.authApi.refreshAuthenticationJWT(refreshToken).then((authResult) => {
|
|
153
|
+
if (!authResult.token)
|
|
154
|
+
throw new Error('Internal error: refresh succeeded but no token was returned. Unsupported backend version?');
|
|
155
|
+
this.cachedToken = authResult.token;
|
|
156
|
+
return { token: authResult.token, type: RetrievedTokenType.REFRESHED };
|
|
157
|
+
}, () => __awaiter(this, void 0, void 0, function* () { return ({ token: yield this.getAndCacheNewToken(undefined), type: RetrievedTokenType.NEW }); }));
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
getNewToken(minimumAuthenticationClassLevel) {
|
|
161
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
if (!!this.currentLongLivedSecret && (!this.currentLongLivedSecret.type || this.currentLongLivedSecret.type >= minimumAuthenticationClassLevel)) {
|
|
163
|
+
const resultWithCachedSecret = yield this.doGetTokenWithSecret(this.currentLongLivedSecret.value, minimumAuthenticationClassLevel);
|
|
164
|
+
if ('success' in resultWithCachedSecret) {
|
|
165
|
+
return resultWithCachedSecret.success;
|
|
166
|
+
}
|
|
167
|
+
else if (resultWithCachedSecret.failure === DoGetTokenResultFailureReason.NEEDS_2FA &&
|
|
168
|
+
minimumAuthenticationClassLevel <= ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION) {
|
|
169
|
+
return this.askTotpAndGetToken(this.currentLongLivedSecret.value, minimumAuthenticationClassLevel);
|
|
170
|
+
}
|
|
171
|
+
else
|
|
172
|
+
return this.askSecretAndGetToken(minimumAuthenticationClassLevel, true);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
return this.askSecretAndGetToken(minimumAuthenticationClassLevel, true);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
askSecretAndGetToken(minimumAuthenticationClassLevel, passwordIsValidAs2fa) {
|
|
180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
const acceptedSecrets = [
|
|
182
|
+
minimumAuthenticationClassLevel <= ServerAuthenticationClass.LONG_LIVED_TOKEN ? [AuthSecretType.LONG_LIVED_TOKEN] : [],
|
|
183
|
+
minimumAuthenticationClassLevel <= ServerAuthenticationClass.SHORT_LIVED_TOKEN ? [AuthSecretType.SHORT_LIVED_TOKEN] : [],
|
|
184
|
+
minimumAuthenticationClassLevel <= ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION &&
|
|
185
|
+
(passwordIsValidAs2fa || minimumAuthenticationClassLevel <= ServerAuthenticationClass.PASSWORD)
|
|
186
|
+
? [AuthSecretType.PASSWORD]
|
|
187
|
+
: [],
|
|
188
|
+
].flat();
|
|
189
|
+
if (!acceptedSecrets.length)
|
|
190
|
+
throw new Error('Internal error: no secret type is accepted for this request. Group may be misconfigured, or client may be outdated.');
|
|
191
|
+
const attempts = [];
|
|
192
|
+
while (true) {
|
|
193
|
+
const { secret, secretType } = yield this.authSecretProvider.getSecret([...acceptedSecrets], attempts);
|
|
194
|
+
if (!acceptedSecrets.includes(secretType))
|
|
195
|
+
throw new Error(`Accepted secret types are ${JSON.stringify(acceptedSecrets)}, but got a secret of type ${secretType}.`);
|
|
196
|
+
attempts.push({ secret, secretType });
|
|
197
|
+
const result = yield this.doGetTokenWithSecret(secret, minimumAuthenticationClassLevel);
|
|
198
|
+
if ('success' in result) {
|
|
199
|
+
this.updateCachedSecret(secret, secretType);
|
|
200
|
+
return result.success;
|
|
201
|
+
}
|
|
202
|
+
else if (result.failure == DoGetTokenResultFailureReason.NEEDS_2FA) {
|
|
203
|
+
return this.askTotpAndGetToken(secret, minimumAuthenticationClassLevel);
|
|
204
|
+
}
|
|
205
|
+
else if (secretType == AuthSecretType.PASSWORD && result.failure == DoGetTokenResultFailureReason.INVALID_AUTH_CLASS_LEVEL) {
|
|
206
|
+
return this.askSecretAndGetToken(minimumAuthenticationClassLevel, false);
|
|
207
|
+
} // else retry
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
askTotpAndGetToken(password, minimumAuthenticationClassLevel) {
|
|
212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
if (minimumAuthenticationClassLevel > ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION)
|
|
214
|
+
throw new Error("Internal error: asking for totp to login but minimumAuthenticationClassLevel is higher than TWO_FACTOR_AUTHENTICATION's level.");
|
|
215
|
+
const attempts = [];
|
|
216
|
+
while (true) {
|
|
217
|
+
const { secret, secretType } = yield this.authSecretProvider.getSecret([AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN], attempts);
|
|
218
|
+
if (secretType != AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN)
|
|
219
|
+
throw new Error(`Was expecting a 2fa token but got a secret of type ${secretType}.`);
|
|
220
|
+
attempts.push({ secret, secretType });
|
|
221
|
+
const result = yield this.doGetTokenWithSecret(`${password}|${secret}`, minimumAuthenticationClassLevel);
|
|
222
|
+
if ('success' in result) {
|
|
223
|
+
this.updateCachedSecret(password, AuthSecretType.PASSWORD);
|
|
224
|
+
return result.success;
|
|
225
|
+
}
|
|
226
|
+
else if (result.failure != DoGetTokenResultFailureReason.INVALID_2FA) {
|
|
227
|
+
throw new Error(`Unexpected error while trying to login with (previously) valid password and 2fa token ${result.failure}.`);
|
|
228
|
+
} // else retry
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
doGetTokenWithSecret(secret, minimumAuthenticationClassLevel) {
|
|
233
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
+
return this.authApi.login({ username: this.login, password: secret }, this.groupId).then((authResult) => {
|
|
235
|
+
const { token, refreshToken } = authResult;
|
|
236
|
+
if (!token || !refreshToken)
|
|
237
|
+
throw new Error('Internal error: login succeeded but no token was returned. Unsupported backend version?');
|
|
238
|
+
const claims = (0, JwtUtils_1.decodeJwtClaims)(token);
|
|
239
|
+
const authClassLevel = claims['tac'];
|
|
240
|
+
if (!authClassLevel || typeof authClassLevel !== 'number')
|
|
241
|
+
throw new Error('Internal error: authClassLevel is not a number. Unsupported backend version?');
|
|
242
|
+
if (authClassLevel < minimumAuthenticationClassLevel) {
|
|
243
|
+
return { failure: DoGetTokenResultFailureReason.INVALID_AUTH_CLASS_LEVEL };
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return { success: { token, refreshToken } };
|
|
247
|
+
}
|
|
248
|
+
}, (error) => {
|
|
249
|
+
if (!(error instanceof XHRError))
|
|
250
|
+
throw error;
|
|
251
|
+
if (error.statusCode == 401 || error.statusCode == 412) {
|
|
252
|
+
// Password is wrong (401) or unacceptable (e.g. too short, 412)
|
|
253
|
+
return { failure: DoGetTokenResultFailureReason.INVALID_PW_OR_TOKEN };
|
|
254
|
+
}
|
|
255
|
+
else if (error.statusCode == 406) {
|
|
256
|
+
// Password is correct, but 2fa token is not
|
|
257
|
+
return { failure: DoGetTokenResultFailureReason.INVALID_2FA };
|
|
258
|
+
}
|
|
259
|
+
else if (error.statusCode == 417) {
|
|
260
|
+
// Password is correct, but the user has 2fa enabled and no 2fa token was provided
|
|
261
|
+
return { failure: DoGetTokenResultFailureReason.NEEDS_2FA };
|
|
262
|
+
}
|
|
263
|
+
else
|
|
264
|
+
throw error;
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
switchedGroup(newGroupId) {
|
|
269
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
const groupSwitchedTokens = this.cachedRefreshToken
|
|
271
|
+
? yield this.authApi.switchGroup(this.cachedRefreshToken, newGroupId).then((response) => {
|
|
272
|
+
if (!response.token || !response.refreshToken)
|
|
273
|
+
throw new Error('Internal error: group switch succeeded but no token was returned. Unsupported backend version?');
|
|
274
|
+
return { token: response.token, refreshToken: response.refreshToken };
|
|
275
|
+
}, () => ({ token: undefined, refreshToken: undefined }))
|
|
276
|
+
: { token: undefined, refreshToken: undefined };
|
|
277
|
+
return new TokenProvider(this.login, newGroupId, this.currentLongLivedSecret ? { value: this.currentLongLivedSecret.value, type: undefined } : undefined, groupSwitchedTokens.token, groupSwitchedTokens.refreshToken, this.authApi, this.authSecretProvider);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
updateCachedSecret(secret, secretType) {
|
|
281
|
+
if (secretType == AuthSecretType.LONG_LIVED_TOKEN || secretType == AuthSecretType.PASSWORD) {
|
|
282
|
+
this.currentLongLivedSecret = { value: secret, type: ServerAuthenticationClass.LONG_LIVED_TOKEN };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
var DoGetTokenResultFailureReason;
|
|
287
|
+
(function (DoGetTokenResultFailureReason) {
|
|
288
|
+
DoGetTokenResultFailureReason[DoGetTokenResultFailureReason["NEEDS_2FA"] = 0] = "NEEDS_2FA";
|
|
289
|
+
DoGetTokenResultFailureReason[DoGetTokenResultFailureReason["INVALID_2FA"] = 1] = "INVALID_2FA";
|
|
290
|
+
DoGetTokenResultFailureReason[DoGetTokenResultFailureReason["INVALID_PW_OR_TOKEN"] = 2] = "INVALID_PW_OR_TOKEN";
|
|
291
|
+
DoGetTokenResultFailureReason[DoGetTokenResultFailureReason["INVALID_AUTH_CLASS_LEVEL"] = 3] = "INVALID_AUTH_CLASS_LEVEL";
|
|
292
|
+
})(DoGetTokenResultFailureReason || (DoGetTokenResultFailureReason = {}));
|
|
293
|
+
var RetrievedTokenType;
|
|
294
|
+
(function (RetrievedTokenType) {
|
|
295
|
+
RetrievedTokenType[RetrievedTokenType["CACHED"] = 0] = "CACHED";
|
|
296
|
+
RetrievedTokenType[RetrievedTokenType["REFRESHED"] = 1] = "REFRESHED";
|
|
297
|
+
RetrievedTokenType[RetrievedTokenType["NEW"] = 2] = "NEW";
|
|
298
|
+
})(RetrievedTokenType || (RetrievedTokenType = {}));
|
|
299
|
+
var SmartAuthServiceState;
|
|
300
|
+
(function (SmartAuthServiceState) {
|
|
301
|
+
SmartAuthServiceState[SmartAuthServiceState["INITIAL"] = 0] = "INITIAL";
|
|
302
|
+
SmartAuthServiceState[SmartAuthServiceState["DONE_INITIAL"] = 1] = "DONE_INITIAL";
|
|
303
|
+
SmartAuthServiceState[SmartAuthServiceState["REATTEMPT"] = 2] = "REATTEMPT";
|
|
304
|
+
SmartAuthServiceState[SmartAuthServiceState["REATTEMPTED_WITH_NEW_UNBOUND_TOKEN"] = 3] = "REATTEMPTED_WITH_NEW_UNBOUND_TOKEN";
|
|
305
|
+
SmartAuthServiceState[SmartAuthServiceState["REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN"] = 4] = "REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN";
|
|
306
|
+
SmartAuthServiceState[SmartAuthServiceState["EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS"] = 5] = "EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS";
|
|
307
|
+
SmartAuthServiceState[SmartAuthServiceState["TERMINAL_ERROR"] = 6] = "TERMINAL_ERROR";
|
|
308
|
+
})(SmartAuthServiceState || (SmartAuthServiceState = {}));
|
|
309
|
+
class SmartAuthService {
|
|
310
|
+
constructor(tokenProvider) {
|
|
311
|
+
this.tokenProvider = tokenProvider;
|
|
312
|
+
this.currentState = { id: SmartAuthServiceState.INITIAL };
|
|
313
|
+
}
|
|
314
|
+
getAuthHeaders(minimumAuthenticationClassLevel) {
|
|
315
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
316
|
+
return [new XHR_1.XHR.Header('Authorization', `Bearer ${yield this.getAuthToken(minimumAuthenticationClassLevel)}`)];
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
getAuthToken(minimumAuthenticationClassLevel) {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
switch (this.currentState.id) {
|
|
322
|
+
case SmartAuthServiceState.INITIAL:
|
|
323
|
+
if (minimumAuthenticationClassLevel != undefined) {
|
|
324
|
+
throw new Error('Illegal state: cannot ask for a specific auth class level at the first request attempt.');
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const { token } = yield this.tokenProvider.getCachedOrRefreshedOrNewToken();
|
|
328
|
+
this.currentState = { id: SmartAuthServiceState.DONE_INITIAL, initialToken: token };
|
|
329
|
+
return token;
|
|
330
|
+
}
|
|
331
|
+
case SmartAuthServiceState.REATTEMPT:
|
|
332
|
+
if (minimumAuthenticationClassLevel != undefined) {
|
|
333
|
+
const token = yield this.tokenProvider.getNewTokenWithClass(minimumAuthenticationClassLevel);
|
|
334
|
+
this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN };
|
|
335
|
+
return token;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
const { token } = yield this.tokenProvider.getCachedOrRefreshedOrNewToken();
|
|
339
|
+
if (token == this.currentState.initialToken)
|
|
340
|
+
throw this.currentState.initialError;
|
|
341
|
+
this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_NEW_UNBOUND_TOKEN };
|
|
342
|
+
return token;
|
|
343
|
+
}
|
|
344
|
+
case SmartAuthServiceState.EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS:
|
|
345
|
+
if (minimumAuthenticationClassLevel != undefined) {
|
|
346
|
+
const token = yield this.tokenProvider.getNewTokenWithClass(minimumAuthenticationClassLevel);
|
|
347
|
+
this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN };
|
|
348
|
+
return token;
|
|
349
|
+
}
|
|
350
|
+
else
|
|
351
|
+
throw this.currentState.errorFromNewToken;
|
|
352
|
+
case SmartAuthServiceState.TERMINAL_ERROR:
|
|
353
|
+
throw this.currentState.error;
|
|
354
|
+
default:
|
|
355
|
+
throw new Error(`Illegal state: cannot get token in state ${this.currentState.id}.`);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
invalidateHeader(error) {
|
|
360
|
+
switch (this.currentState.id) {
|
|
361
|
+
case SmartAuthServiceState.DONE_INITIAL:
|
|
362
|
+
this.currentState = { id: SmartAuthServiceState.REATTEMPT, initialToken: this.currentState.initialToken, initialError: error };
|
|
363
|
+
break;
|
|
364
|
+
case SmartAuthServiceState.REATTEMPTED_WITH_NEW_UNBOUND_TOKEN:
|
|
365
|
+
this.currentState = { id: SmartAuthServiceState.EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS, errorFromNewToken: error };
|
|
366
|
+
break;
|
|
367
|
+
case SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN:
|
|
368
|
+
this.currentState = { id: SmartAuthServiceState.TERMINAL_ERROR, error: error };
|
|
369
|
+
break;
|
|
370
|
+
default:
|
|
371
|
+
throw new Error(`Illegal state: cannot invalidate header in state ${this.currentState.id}.`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
//# sourceMappingURL=SmartAuthProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartAuthProvider.js","sourceRoot":"","sources":["../../../icc-x-api/auth/SmartAuthProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthenticationProvider, NoAuthenticationProvider } from './AuthenticationProvider'\nimport { UserGroup } from '../../icc-api/model/UserGroup'\nimport { AuthService } from './AuthService'\nimport { IccAuthApi } from '../../icc-api'\nimport { XHR } from '../../icc-api/api/XHR'\nimport XHRError = XHR.XHRError\nimport { decodeJwtClaims, isJwtInvalidOrExpired } from './JwtUtils'\n\n/**\n * Needed by a {@link SmartAuthProvider} to get the secrets (password, token, etc.) for authentication to the iCure SDK as needed.\n */\nexport interface AuthSecretProvider {\n /**\n * Provides a secret for authentication to the iCure SDK.\n *\n * ## Accepted secrets\n *\n * The method will be provided with an array of the secrets types that are acceptable (`acceptedSecrets`). Usually this array will contain multiple\n * elements, but this depends on the group configuration, the user (if he has 2fa setup or not), or the operation being performed. For groups using\n * default configurations and for patients without 2fa enabled for example the array will always contain the {@link AuthSecretType.PASSWORD} element.\n * Usually the array contain also the {@link AuthSecretType.LONG_LIVED_TOKEN} element, but if the user is attempting to perform a sensitive operations\n * such as changing his password the default group configuration does not allow for the user to authenticate using a JWT obtained from a long-lived\n * token for this operation, meaning the array will not contain the {@link AuthSecretType.LONG_LIVED_TOKEN} element.\n *\n * Regardless of the number of elements in the array only one secret of the accepted types is sufficient for the operation to succeed.\n *\n * ## TWO_FACTOR_AUTHENTICATION_TOKEN secret type\n *\n * The {@link AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN} secret type is only used when the user has 2fa enabled. In this case the SDK will call\n * this method twice, once containing the {@link AuthSecretType.PASSWORD} element in the `acceptedSecrets` array, and if the provided secret is a\n * valid password the SDK will immediately call this method again, this time containing the {@link AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN}\n * instead of the {@link AuthSecretType.PASSWORD} element.\n *\n * Any future call to this method from the same provider instance will not contain the {@link AuthSecretType.PASSWORD} element anymore, as it is\n * cached, but it may contain the {@link AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN} element instead.\n *\n * Note that the 2fa token is not needed for logging in through a long-lived or short-lived token, it is only used in combination with a password.\n * If the user is using 2fa, and you get in input as `acceptedSecrets` an array `[PASSWORD, LONG_LIVED_TOKEN, SHORT_LIVED_TOKEN]`, and you pass to\n * authenticate a long-lived token, the SDK will not call this method again to ask for the 2fa token.\n *\n * @param acceptedSecrets the types of secrets that are acceptable for the operation being performed.\n * @param previousAttempts the secrets that were previously attempted by the SDK for this operation. This array will be empty the first time this\n * method is called for a given operation, but it may contain multiple elements if the SDK has already called this method multiple times because the\n * previously returned secrets were not valid. The first element is the first secret that was attempted, and the last element is the most recently\n * attempted.\n * @return a promise that resolves with the secret and the secret type to use for authentication. If the promise rejects then the ongoing SDK\n * operation will fail without being re-attempted.\n */\n getSecret(\n acceptedSecrets: AuthSecretType[],\n previousAttempts: { secret: string; secretType: AuthSecretType }[]\n ): Promise<{ secret: string; secretType: AuthSecretType }> // We may want to add some onSuccess callback in future or similar\n}\n\n/**\n * Represents a type of secret that can be used for authentication with iCure.\n */\nexport enum AuthSecretType {\n /**\n * Password chosen by the user.\n */\n PASSWORD = 'PASSWORD', // pragma: allowlist secret\n /**\n * Time based one time password provided by authenticator applications, generated on the basis of a timestamp and a shared secret between the iCure\n * server and the authenticator application.\n */\n TWO_FACTOR_AUTHENTICATION_TOKEN = 'TWO_FACTOR_AUTHENTICATION_TOKEN',\n /**\n * A short-lived iCure token, an internal authentication token that lasts 5 minutes or less. Unlike passwords these tokens usually are generated by\n * some component of iCure, and are not chosen by the user.\n */\n SHORT_LIVED_TOKEN = 'SHORT_LIVED_TOKEN',\n /**\n * A long-lived iCure token, an internal authentication token that lasts longer than 5 minutes. Unlike passwords these tokens usually are generated\n * by some component of iCure, and are not chosen by the user.\n */\n LONG_LIVED_TOKEN = 'LONG_LIVED_TOKEN',\n /**\n * A token provided by an external authentication provider (e.g. Oauth/Google).\n * Not yet in use.\n */\n // EXTERNAL_AUTHENTICATION = 'EXTERNAL_AUTHENTICATION',\n /**\n * A special case of external authentication where the provider is a digital identity provider.\n * Not yet in use.\n */\n // DIGITAL_ID = 'DIGITAL_ID',\n}\n\n// Here starts internal entities that should not be used directly.\n\n/**\n * @internal this class is meant for internal use only and may be changed without notice. The SmartAuthProvider will be initialised automatically\n * by the iCure api depending on the authentication options you provide.\n *\n * An authentication provider that automatically requests secrets for authentication as needed.\n *\n * This authentication provider can be initialised already with some secrets or tokens, and the provider will cache them and use them as needed for\n * as long as they remain valid. If at any point however the provider needs an updated secret or a secret of a different kind it will automatically\n * request this to the {@link SmartAuthProvider} to get the secret.\n *\n * An advantage of using this provider over others is that in case all the cached tokens and secrets were to expire while performing a request,\n * instead of having the request fail the provider will ask for new secrets from the {@link SmartAuthProvider} and the request will automatically\n * be retried with the new secret. Additionally, the provider may request updated secrets also for performing some sensitive operations (e.g. changing\n * password of the user) even if the cached tokens and/or did not expire. This could be the case for example if the cached secret is a long-lived\n * token, but in order to change the password the user needs to provide his current password.\n *\n * Note that in this context the cache of secrets and token is in memory only, and is not persisted in any way. Different instances of this provider\n * will not share the same cache.\n *\n */\nexport class SmartAuthProvider implements AuthenticationProvider {\n /**\n * Initialises a {@link SmartAuthProvider}.\n * @param authApi an \"anonymous\" {@link IccAuthApi} to use for authentication.\n * @param login\n * @param secretProvider\n * @param props optional initialisation properties.\n */\n static initialise(\n authApi: IccAuthApi,\n login: string,\n secretProvider: AuthSecretProvider,\n props: {\n initialSecret?: string\n initialAuthToken?: string\n initialRefreshToken?: string\n loginGroupId?: string\n } = {}\n ): SmartAuthProvider {\n return new SmartAuthProvider(\n new TokenProvider(\n login,\n props.loginGroupId,\n props.initialSecret ? { value: props.initialSecret, type: undefined } : undefined,\n props.initialAuthToken,\n props.initialRefreshToken,\n authApi,\n secretProvider\n ),\n props.loginGroupId\n )\n }\n\n private constructor(private readonly tokenProvider: TokenProvider, private readonly groupId: string | undefined) {}\n\n getAuthService(): AuthService {\n return new SmartAuthService(this.tokenProvider)\n }\n\n async switchGroup(newGroupId: string, matches: Array<UserGroup>): Promise<AuthenticationProvider> {\n if (newGroupId == this.groupId) return Promise.resolve(this)\n if (!matches.find((match) => match.groupId == newGroupId)) throw new Error('New group id not found in matches.')\n const switchedProvider = await this.tokenProvider.switchedGroup(newGroupId)\n return new SmartAuthProvider(switchedProvider, this.groupId)\n }\n\n getIcureTokens(): Promise<{ token: string; refreshToken: string } | undefined> {\n return Promise.resolve(undefined)\n }\n}\n\nenum ServerAuthenticationClass {\n // DIGITAL_ID = 60,\n TWO_FACTOR_AUTHENTICATION = 50,\n SHORT_LIVED_TOKEN = 40,\n // EXTERNAL_AUTHENTICATION = 30,\n PASSWORD = 20,\n LONG_LIVED_TOKEN = 10,\n}\ntype LongLivedSecretType =\n | ServerAuthenticationClass.LONG_LIVED_TOKEN\n | ServerAuthenticationClass.PASSWORD\n | ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION\nclass TokenProvider {\n constructor(\n private login: string,\n private groupId: string | undefined,\n private currentLongLivedSecret: { value: string; type: LongLivedSecretType | undefined } | undefined,\n private cachedToken: string | undefined,\n private cachedRefreshToken: string | undefined,\n private readonly authApi: IccAuthApi,\n private readonly authSecretProvider: AuthSecretProvider\n ) {}\n\n async getCachedOrRefreshedOrNewToken(): Promise<{ token: string; type: RetrievedTokenType }> {\n if (!!this.cachedToken && !isJwtInvalidOrExpired(this.cachedToken)) {\n return { token: this.cachedToken, type: RetrievedTokenType.CACHED }\n } else if (!!this.cachedRefreshToken && !isJwtInvalidOrExpired(this.cachedRefreshToken)) {\n return this.refreshAndCacheToken(this.cachedRefreshToken)\n } else {\n return { token: await this.getAndCacheNewToken(undefined), type: RetrievedTokenType.NEW }\n }\n }\n\n async getNewTokenWithClass(minimumAuthenticationClass: number): Promise<string> {\n return await this.getAndCacheNewToken(minimumAuthenticationClass)\n }\n\n private async getAndCacheNewToken(minimumAuthenticationClassLevel: number | undefined): Promise<string> {\n const { token, refreshToken } = await this.getNewToken(minimumAuthenticationClassLevel ?? 0)\n this.cachedToken = token\n this.cachedRefreshToken = refreshToken\n return token\n }\n\n private async refreshAndCacheToken(refreshToken: string): Promise<{ token: string; type: RetrievedTokenType }> {\n return await this.authApi.refreshAuthenticationJWT(refreshToken).then(\n (authResult) => {\n if (!authResult.token) throw new Error('Internal error: refresh succeeded but no token was returned. Unsupported backend version?')\n this.cachedToken = authResult.token\n return { token: authResult.token, type: RetrievedTokenType.REFRESHED }\n },\n async () => ({ token: await this.getAndCacheNewToken(undefined), type: RetrievedTokenType.NEW })\n )\n }\n\n private async getNewToken(minimumAuthenticationClassLevel: number): Promise<{ token: string; refreshToken: string }> {\n if (!!this.currentLongLivedSecret && (!this.currentLongLivedSecret.type || this.currentLongLivedSecret.type >= minimumAuthenticationClassLevel)) {\n const resultWithCachedSecret = await this.doGetTokenWithSecret(this.currentLongLivedSecret.value, minimumAuthenticationClassLevel)\n if ('success' in resultWithCachedSecret) {\n return resultWithCachedSecret.success\n } else if (\n resultWithCachedSecret.failure === DoGetTokenResultFailureReason.NEEDS_2FA &&\n minimumAuthenticationClassLevel <= ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION\n ) {\n return this.askTotpAndGetToken(this.currentLongLivedSecret.value, minimumAuthenticationClassLevel)\n } else return this.askSecretAndGetToken(minimumAuthenticationClassLevel, true)\n } else {\n return this.askSecretAndGetToken(minimumAuthenticationClassLevel, true)\n }\n }\n\n private async askSecretAndGetToken(\n minimumAuthenticationClassLevel: number,\n passwordIsValidAs2fa: boolean\n ): Promise<{ token: string; refreshToken: string }> {\n const acceptedSecrets = [\n minimumAuthenticationClassLevel <= ServerAuthenticationClass.LONG_LIVED_TOKEN ? [AuthSecretType.LONG_LIVED_TOKEN] : [],\n minimumAuthenticationClassLevel <= ServerAuthenticationClass.SHORT_LIVED_TOKEN ? [AuthSecretType.SHORT_LIVED_TOKEN] : [],\n minimumAuthenticationClassLevel <= ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION &&\n (passwordIsValidAs2fa || minimumAuthenticationClassLevel <= ServerAuthenticationClass.PASSWORD)\n ? [AuthSecretType.PASSWORD]\n : [],\n ].flat()\n if (!acceptedSecrets.length)\n throw new Error('Internal error: no secret type is accepted for this request. Group may be misconfigured, or client may be outdated.')\n const attempts: { secret: string; secretType: AuthSecretType }[] = []\n while (true) {\n const { secret, secretType } = await this.authSecretProvider.getSecret([...acceptedSecrets], attempts)\n if (!acceptedSecrets.includes(secretType))\n throw new Error(`Accepted secret types are ${JSON.stringify(acceptedSecrets)}, but got a secret of type ${secretType}.`)\n attempts.push({ secret, secretType })\n const result = await this.doGetTokenWithSecret(secret, minimumAuthenticationClassLevel)\n if ('success' in result) {\n this.updateCachedSecret(secret, secretType)\n return result.success\n } else if (result.failure == DoGetTokenResultFailureReason.NEEDS_2FA) {\n return this.askTotpAndGetToken(secret, minimumAuthenticationClassLevel)\n } else if (secretType == AuthSecretType.PASSWORD && result.failure == DoGetTokenResultFailureReason.INVALID_AUTH_CLASS_LEVEL) {\n return this.askSecretAndGetToken(minimumAuthenticationClassLevel, false)\n } // else retry\n }\n }\n\n private async askTotpAndGetToken(password: string, minimumAuthenticationClassLevel: number): Promise<{ token: string; refreshToken: string }> {\n if (minimumAuthenticationClassLevel > ServerAuthenticationClass.TWO_FACTOR_AUTHENTICATION)\n throw new Error(\n \"Internal error: asking for totp to login but minimumAuthenticationClassLevel is higher than TWO_FACTOR_AUTHENTICATION's level.\"\n )\n const attempts: { secret: string; secretType: AuthSecretType }[] = []\n while (true) {\n const { secret, secretType } = await this.authSecretProvider.getSecret([AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN], attempts)\n if (secretType != AuthSecretType.TWO_FACTOR_AUTHENTICATION_TOKEN)\n throw new Error(`Was expecting a 2fa token but got a secret of type ${secretType}.`)\n attempts.push({ secret, secretType })\n const result = await this.doGetTokenWithSecret(`${password}|${secret}`, minimumAuthenticationClassLevel)\n if ('success' in result) {\n this.updateCachedSecret(password, AuthSecretType.PASSWORD)\n return result.success\n } else if (result.failure != DoGetTokenResultFailureReason.INVALID_2FA) {\n throw new Error(`Unexpected error while trying to login with (previously) valid password and 2fa token ${result.failure}.`)\n } // else retry\n }\n }\n\n private async doGetTokenWithSecret(secret: string, minimumAuthenticationClassLevel: number): Promise<DoGetTokenResult> {\n return this.authApi.login({ username: this.login, password: secret }, this.groupId).then(\n (authResult) => {\n const { token, refreshToken } = authResult\n if (!token || !refreshToken) throw new Error('Internal error: login succeeded but no token was returned. Unsupported backend version?')\n const claims = decodeJwtClaims(token)\n const authClassLevel = claims['tac']\n if (!authClassLevel || typeof authClassLevel !== 'number')\n throw new Error('Internal error: authClassLevel is not a number. Unsupported backend version?')\n if (authClassLevel < minimumAuthenticationClassLevel) {\n return { failure: DoGetTokenResultFailureReason.INVALID_AUTH_CLASS_LEVEL }\n } else {\n return { success: { token, refreshToken } }\n }\n },\n (error) => {\n if (!(error instanceof XHRError)) throw error\n if (error.statusCode == 401 || error.statusCode == 412) {\n // Password is wrong (401) or unacceptable (e.g. too short, 412)\n return { failure: DoGetTokenResultFailureReason.INVALID_PW_OR_TOKEN }\n } else if (error.statusCode == 406) {\n // Password is correct, but 2fa token is not\n return { failure: DoGetTokenResultFailureReason.INVALID_2FA }\n } else if (error.statusCode == 417) {\n // Password is correct, but the user has 2fa enabled and no 2fa token was provided\n return { failure: DoGetTokenResultFailureReason.NEEDS_2FA }\n } else throw error\n }\n )\n }\n\n async switchedGroup(newGroupId: string): Promise<TokenProvider> {\n const groupSwitchedTokens = this.cachedRefreshToken\n ? await this.authApi.switchGroup(this.cachedRefreshToken, newGroupId).then(\n (response) => {\n if (!response.token || !response.refreshToken)\n throw new Error('Internal error: group switch succeeded but no token was returned. Unsupported backend version?')\n return { token: response.token, refreshToken: response.refreshToken }\n },\n () => ({ token: undefined, refreshToken: undefined })\n )\n : { token: undefined, refreshToken: undefined }\n return new TokenProvider(\n this.login,\n newGroupId,\n this.currentLongLivedSecret ? { value: this.currentLongLivedSecret.value, type: undefined } : undefined,\n groupSwitchedTokens.token,\n groupSwitchedTokens.refreshToken,\n this.authApi,\n this.authSecretProvider\n )\n }\n\n private updateCachedSecret(secret: string, secretType: AuthSecretType) {\n if (secretType == AuthSecretType.LONG_LIVED_TOKEN || secretType == AuthSecretType.PASSWORD) {\n this.currentLongLivedSecret = { value: secret, type: ServerAuthenticationClass.LONG_LIVED_TOKEN }\n }\n }\n}\ntype DoGetTokenResult = { success: { token: string; refreshToken: string } } | { failure: DoGetTokenResultFailureReason }\nenum DoGetTokenResultFailureReason {\n NEEDS_2FA,\n INVALID_2FA,\n INVALID_PW_OR_TOKEN,\n INVALID_AUTH_CLASS_LEVEL,\n}\nenum RetrievedTokenType {\n CACHED,\n REFRESHED,\n NEW,\n}\n\nenum SmartAuthServiceState {\n INITIAL,\n DONE_INITIAL,\n REATTEMPT,\n REATTEMPTED_WITH_NEW_UNBOUND_TOKEN,\n REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN,\n EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS,\n TERMINAL_ERROR,\n}\nclass SmartAuthService implements AuthService {\n private currentState:\n | { id: SmartAuthServiceState.INITIAL }\n | { id: SmartAuthServiceState.DONE_INITIAL; initialToken: string }\n | { id: SmartAuthServiceState.REATTEMPT; initialToken: string; initialError: Error }\n | { id: SmartAuthServiceState.REATTEMPTED_WITH_NEW_UNBOUND_TOKEN }\n | { id: SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN }\n | { id: SmartAuthServiceState.EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS; errorFromNewToken: Error }\n | { id: SmartAuthServiceState.TERMINAL_ERROR; error: Error } = { id: SmartAuthServiceState.INITIAL }\n\n constructor(private readonly tokenProvider: TokenProvider) {}\n\n async getAuthHeaders(minimumAuthenticationClassLevel: number | undefined): Promise<Array<XHR.Header>> {\n return [new XHR.Header('Authorization', `Bearer ${await this.getAuthToken(minimumAuthenticationClassLevel)}`)]\n }\n\n private async getAuthToken(minimumAuthenticationClassLevel: number | undefined): Promise<string> {\n switch (this.currentState.id) {\n case SmartAuthServiceState.INITIAL:\n if (minimumAuthenticationClassLevel != undefined) {\n throw new Error('Illegal state: cannot ask for a specific auth class level at the first request attempt.')\n } else {\n const { token } = await this.tokenProvider.getCachedOrRefreshedOrNewToken()\n this.currentState = { id: SmartAuthServiceState.DONE_INITIAL, initialToken: token }\n return token\n }\n case SmartAuthServiceState.REATTEMPT:\n if (minimumAuthenticationClassLevel != undefined) {\n const token = await this.tokenProvider.getNewTokenWithClass(minimumAuthenticationClassLevel)\n this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN }\n return token\n } else {\n const { token } = await this.tokenProvider.getCachedOrRefreshedOrNewToken()\n if (token == this.currentState.initialToken) throw this.currentState.initialError\n this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_NEW_UNBOUND_TOKEN }\n return token\n }\n case SmartAuthServiceState.EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS:\n if (minimumAuthenticationClassLevel != undefined) {\n const token = await this.tokenProvider.getNewTokenWithClass(minimumAuthenticationClassLevel)\n this.currentState = { id: SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN }\n return token\n } else throw this.currentState.errorFromNewToken\n case SmartAuthServiceState.TERMINAL_ERROR:\n throw this.currentState.error\n default:\n throw new Error(`Illegal state: cannot get token in state ${this.currentState.id}.`)\n }\n }\n\n invalidateHeader(error: Error): void {\n switch (this.currentState.id) {\n case SmartAuthServiceState.DONE_INITIAL:\n this.currentState = { id: SmartAuthServiceState.REATTEMPT, initialToken: this.currentState.initialToken, initialError: error }\n break\n case SmartAuthServiceState.REATTEMPTED_WITH_NEW_UNBOUND_TOKEN:\n this.currentState = { id: SmartAuthServiceState.EXPECT_REQUEST_WITH_SPECIFIC_AUTH_CLASS, errorFromNewToken: error }\n break\n case SmartAuthServiceState.REATTEMPTED_WITH_AUTH_CLASS_SPECIFIC_TOKEN:\n this.currentState = { id: SmartAuthServiceState.TERMINAL_ERROR, error: error }\n break\n default:\n throw new Error(`Illegal state: cannot invalidate header in state ${this.currentState.id}.`)\n }\n }\n}\n"]}
|
package/icc-x-api/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { IccDoctemplateXApi } from './icc-doctemplate-x-api';
|
|
|
28
28
|
import { UserGroup } from '../icc-api/model/UserGroup';
|
|
29
29
|
import { IccDeviceXApi } from './icc-device-x-api';
|
|
30
30
|
import { IccRoleApi } from '../icc-api/api/IccRoleApi';
|
|
31
|
+
import { AuthSecretProvider } from './auth/SmartAuthProvider';
|
|
31
32
|
export * from './icc-accesslog-x-api';
|
|
32
33
|
export * from './icc-bekmehr-x-api';
|
|
33
34
|
export * from './icc-calendar-item-x-api';
|
|
@@ -336,6 +337,39 @@ export type AuthenticationDetails = {
|
|
|
336
337
|
thirdPartyTokens: {
|
|
337
338
|
[thirdParty: string]: string;
|
|
338
339
|
};
|
|
340
|
+
} | SmartAuthenticationDetails;
|
|
341
|
+
/**
|
|
342
|
+
* Allows to perform authentication through an {@link AuthSecretProvider}.
|
|
343
|
+
*
|
|
344
|
+
* The iCure SDK can authenticate to the backend using different kinds of secrets, such as passwords, long-lived authentication tokens, and
|
|
345
|
+
* short-lived authentication tokens generated through the message gateway. iCure associates to each kind of secret a certain security level, and for
|
|
346
|
+
* some sensitive operations, depending on the configurations of the user and his group, some operations may require a secret of a certain security
|
|
347
|
+
* level. For example, with the default configurations, in order to change his own email the user can't have logged in with a long-lived token, but he
|
|
348
|
+
* needs to provide his current password or a short-lived token.
|
|
349
|
+
*
|
|
350
|
+
* By using this authentication option, the iCure SDK will automatically request and cache the secret from the {@link AuthSecretProvider} only when
|
|
351
|
+
* needed, which should help to minimise the interaction with the user.
|
|
352
|
+
*
|
|
353
|
+
* Another advantage of using this authentication option over others is that in case all the cached tokens and secrets were to expire while performing
|
|
354
|
+
* a request, instead of having the request fail the SDK will ask for a new secret from the {@link SmartAuthProvider} and the request will
|
|
355
|
+
* automatically be retried with the new secret.
|
|
356
|
+
*
|
|
357
|
+
* You must provide the following information:
|
|
358
|
+
* - username: any kind of value that can identify the user (userId, groupId/userId, username, email, ...). More generic identifiers, valid
|
|
359
|
+
* on multiple groups, allow for simpler group switching by using {@link IcureApi.switchGroup}.
|
|
360
|
+
* - secretProvider: the secret provider to use for authentication. Will handle interaction with the gui.
|
|
361
|
+
*
|
|
362
|
+
* You can also provide the following optional information, which may allow to reduce the requests for secrets initially:
|
|
363
|
+
* - initialSecret: an initial secret (password, token, ...) that will be used to get new authentication tokens as needed. If it is expired it will be ignored.
|
|
364
|
+
* - initialAuthToken: an initial authentication token used on each request. If it is expired it will be ignored.
|
|
365
|
+
* - initialRefreshToken: an initial refresh token used to get new authentication tokens as needed. If it is expired it will be ignored.
|
|
366
|
+
*/
|
|
367
|
+
export type SmartAuthenticationDetails = {
|
|
368
|
+
username: string;
|
|
369
|
+
secretProvider: AuthSecretProvider;
|
|
370
|
+
initialSecret?: string;
|
|
371
|
+
initialAuthToken?: string;
|
|
372
|
+
initialRefreshToken?: string;
|
|
339
373
|
};
|
|
340
374
|
/**
|
|
341
375
|
* Main entry point for the iCure API. Provides entity-specific sub-apis and some general methods which are not related to a specific entity.
|
|
@@ -357,6 +391,26 @@ export interface IcureApi extends Apis {
|
|
|
357
391
|
*/
|
|
358
392
|
switchGroup(newGroupId: string): Promise<IcureApi>;
|
|
359
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Main entry point for the iCure API. Provides entity-specific sub-apis and some general methods which are not related to a specific entity.
|
|
396
|
+
*/
|
|
397
|
+
export interface IcureApi extends Apis {
|
|
398
|
+
/**
|
|
399
|
+
* Get the information on groups that the current user can access and the current group that this api instance is working on.
|
|
400
|
+
* Note that the values you will get for `availableGroups` may differ from the values you would get if you call {@link IccUserApi.getMatchingUsers}
|
|
401
|
+
* on {@link Apis.userApi}, since the latter is specialised on the specific instance of the user in `currentGroup`.
|
|
402
|
+
*/
|
|
403
|
+
getGroupsInfo(): Promise<{
|
|
404
|
+
currentGroup: UserGroup;
|
|
405
|
+
availableGroups: UserGroup[];
|
|
406
|
+
}>;
|
|
407
|
+
/**
|
|
408
|
+
* Switches the api to allow the user to work on a different group.
|
|
409
|
+
* @param newGroupId the id of the group to switch to.
|
|
410
|
+
* @return a new api for the specified group.
|
|
411
|
+
*/
|
|
412
|
+
switchGroup(newGroupId: string): Promise<IcureApi>;
|
|
413
|
+
}
|
|
360
414
|
export declare namespace IcureApi {
|
|
361
415
|
/**
|
|
362
416
|
* Initialises a new instance of the iCure API.
|
package/icc-x-api/index.js
CHANGED
|
@@ -65,6 +65,7 @@ const icc_doctemplate_x_api_1 = require("./icc-doctemplate-x-api");
|
|
|
65
65
|
const icc_device_x_api_1 = require("./icc-device-x-api");
|
|
66
66
|
const IccRoleApi_1 = require("../icc-api/api/IccRoleApi");
|
|
67
67
|
const JwtBridgedAuthService_1 = require("./auth/JwtBridgedAuthService");
|
|
68
|
+
const SmartAuthProvider_1 = require("./auth/SmartAuthProvider");
|
|
68
69
|
__exportStar(require("./icc-accesslog-x-api"), exports);
|
|
69
70
|
__exportStar(require("./icc-bekmehr-x-api"), exports);
|
|
70
71
|
__exportStar(require("./icc-calendar-item-x-api"), exports);
|
|
@@ -134,16 +135,9 @@ function getAuthenticationProvider(host, authenticationOptions, headers, fetchIm
|
|
|
134
135
|
return __awaiter(this, void 0, void 0, function* () {
|
|
135
136
|
let authenticationProvider;
|
|
136
137
|
if ('getIcureTokens' in authenticationOptions && 'switchGroup' in authenticationOptions && 'getAuthService' in authenticationOptions) {
|
|
137
|
-
|
|
138
|
-
if (!!tokens && !!tokens.token && !!tokens.refreshToken) {
|
|
139
|
-
authenticationProvider = new AuthenticationProvider_1.JwtAuthenticationProvider(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), undefined, undefined, undefined, tokens);
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
authenticationProvider = authenticationOptions;
|
|
143
|
-
}
|
|
138
|
+
authenticationProvider = authenticationOptions;
|
|
144
139
|
}
|
|
145
|
-
else if ('icureTokens' in authenticationOptions &&
|
|
146
|
-
!!authenticationOptions.icureTokens) {
|
|
140
|
+
else if ('icureTokens' in authenticationOptions && !!authenticationOptions.icureTokens) {
|
|
147
141
|
authenticationProvider = new AuthenticationProvider_1.JwtAuthenticationProvider(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), undefined, undefined, undefined, authenticationOptions.icureTokens);
|
|
148
142
|
}
|
|
149
143
|
else if ('username' in authenticationOptions &&
|
|
@@ -152,10 +146,16 @@ function getAuthenticationProvider(host, authenticationOptions, headers, fetchIm
|
|
|
152
146
|
!!authenticationOptions.password) {
|
|
153
147
|
authenticationProvider = new AuthenticationProvider_1.EnsembleAuthenticationProvider(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), authenticationOptions.username, authenticationOptions.password, 3600, undefined, undefined, authenticationOptions.thirdPartyTokens);
|
|
154
148
|
}
|
|
155
|
-
else if ('thirdPartyTokens' in authenticationOptions &&
|
|
156
|
-
!!authenticationOptions.thirdPartyTokens) {
|
|
149
|
+
else if ('thirdPartyTokens' in authenticationOptions && !!authenticationOptions.thirdPartyTokens) {
|
|
157
150
|
authenticationProvider = new AuthenticationProvider_1.JwtAuthenticationProvider(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), undefined, undefined, new JwtBridgedAuthService_1.JwtBridgedAuthService(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), undefined, undefined, authenticationOptions.thirdPartyTokens));
|
|
158
151
|
}
|
|
152
|
+
else if ('username' in authenticationOptions && 'secretProvider' in authenticationOptions) {
|
|
153
|
+
authenticationProvider = SmartAuthProvider_1.SmartAuthProvider.initialise(new icc_api_1.IccAuthApi(host, headers, new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl), authenticationOptions.username, authenticationOptions.secretProvider, {
|
|
154
|
+
initialSecret: authenticationOptions.initialSecret,
|
|
155
|
+
initialAuthToken: authenticationOptions.initialAuthToken,
|
|
156
|
+
initialRefreshToken: authenticationOptions.initialRefreshToken,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
159
|
else {
|
|
160
160
|
throw new Error('Invalid authentication options provided');
|
|
161
161
|
}
|