oro-sdk-apis 1.11.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers/hash.d.ts +6 -0
- package/dist/helpers/index.d.ts +2 -0
- package/dist/helpers/init.d.ts +9 -0
- package/dist/index.d.ts +2 -24
- package/dist/models/guard.d.ts +10 -1
- package/dist/models/index.d.ts +3 -0
- package/dist/models/init.d.ts +26 -0
- package/dist/models/user.d.ts +15 -0
- package/dist/models/workflow.d.ts +3 -1
- package/dist/oro-sdk-apis.cjs.development.js +399 -110
- package/dist/oro-sdk-apis.cjs.development.js.map +1 -1
- package/dist/oro-sdk-apis.cjs.production.min.js +1 -1
- package/dist/oro-sdk-apis.cjs.production.min.js.map +1 -1
- package/dist/oro-sdk-apis.esm.js +397 -111
- package/dist/oro-sdk-apis.esm.js.map +1 -1
- package/dist/services/api.d.ts +9 -1
- package/dist/services/apisPracticeManager.d.ts +25 -0
- package/dist/services/guard.d.ts +22 -1
- package/dist/services/index.d.ts +1 -0
- package/package.json +4 -9
- package/src/helpers/hash.ts +11 -0
- package/src/helpers/index.ts +2 -0
- package/src/helpers/init.ts +47 -0
- package/src/index.ts +2 -52
- package/src/models/guard.ts +13 -4
- package/src/models/index.ts +4 -1
- package/src/models/init.ts +37 -0
- package/src/models/user.ts +13 -0
- package/src/models/workflow.ts +4 -9
- package/src/services/api.ts +51 -34
- package/src/services/apisPracticeManager.ts +52 -0
- package/src/services/guard.ts +72 -2
- package/src/services/index.ts +1 -0
- package/src/services/practice.ts +2 -3
- package/LICENSE +0 -21
package/dist/services/api.d.ts
CHANGED
|
@@ -2,9 +2,17 @@ import type { AxiosRequestConfig } from 'axios';
|
|
|
2
2
|
import { AuthRefreshFunc, Tokens } from '../models';
|
|
3
3
|
import { AxiosService } from './axios';
|
|
4
4
|
export declare class APIService extends AxiosService {
|
|
5
|
+
private useLocalStorage;
|
|
5
6
|
private tokenRefreshFailureCallback?;
|
|
6
7
|
private authRefreshFn?;
|
|
7
|
-
|
|
8
|
+
private tokens;
|
|
9
|
+
/**
|
|
10
|
+
* The API Service lets you use an axios API and handles oro backend services authentification via JWT tokens
|
|
11
|
+
* @param useLocalStorage if set to true, tokens will be stored in localStorage
|
|
12
|
+
* @param config (optional) an axios config
|
|
13
|
+
* @param tokenRefreshFailureCallback (optional) callback to call when failing to refresh the auth token
|
|
14
|
+
*/
|
|
15
|
+
constructor(useLocalStorage: boolean, config?: AxiosRequestConfig, tokenRefreshFailureCallback?: ((err: Error) => void) | undefined);
|
|
8
16
|
setAuthRefreshFn(fn: AuthRefreshFunc): void;
|
|
9
17
|
setTokens(tokens: Tokens): void;
|
|
10
18
|
getTokens(): Tokens;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AuthTokenResponse, ServiceCollection, ServiceCollectionRequest } from '../models';
|
|
2
|
+
import { GuardService } from './guard';
|
|
3
|
+
/**
|
|
4
|
+
* This service enables you to handle one authentication token per practice
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApisPracticeManager {
|
|
7
|
+
private serviceCollReq;
|
|
8
|
+
private getAuthTokenCbk;
|
|
9
|
+
private useLocalStorage;
|
|
10
|
+
private practiceInstances;
|
|
11
|
+
/**
|
|
12
|
+
* The constructor
|
|
13
|
+
* @param serviceCollReq the services to initialize. Only filled urls will get corresponding service to be initialized.
|
|
14
|
+
* It will be used each time a new practices needs a `ServiceCollection`
|
|
15
|
+
* @param getAuthTokenCbk the callback function used to get a new JWT token
|
|
16
|
+
* @param useLocalStorage (default: false) if true store tokens into local storage (only for browsers)
|
|
17
|
+
*/
|
|
18
|
+
constructor(serviceCollReq: ServiceCollectionRequest, getAuthTokenCbk: (guard: GuardService, practiceUuid: string) => Promise<AuthTokenResponse>, useLocalStorage?: boolean);
|
|
19
|
+
/**
|
|
20
|
+
* This function is used to get a `ServiceCollection` associated to a practice. If missing, it will initialize a new `ServiceCollection`.
|
|
21
|
+
* @param practiceUuid the uuid of the practice
|
|
22
|
+
* @returns a promise holding a `ServiceCollection`
|
|
23
|
+
*/
|
|
24
|
+
get(practiceUuid: string): Promise<ServiceCollection>;
|
|
25
|
+
}
|
package/dist/services/guard.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AxiosAuthRefreshRequestConfig } from 'axios-auth-refresh';
|
|
2
|
-
import { AuthRecoverRequest, AuthTokenRequest, AuthTokenResponse, Base64String, IdentityCreateRequest, IdentityResendConfirmEmailRequest, IdentityResponse, IdentityUpdateRequest, QRCodeResponse, Tokens, Uuid, WhoAmIResponse } from '../models';
|
|
2
|
+
import { AuthRecoverRequest, AuthTokenRequest, AuthTokenResponse, Base64String, IdentityCreateRequest, IdentityResendConfirmEmailRequest, IdentityResponse, IdentityUpdateRequest, M2MTokenRequest, QRCodeResponse, Tokens, Uuid, WhoAmIResponse } from '../models';
|
|
3
3
|
import { APIService } from './api';
|
|
4
4
|
export interface GuardRequestConfig extends AxiosAuthRefreshRequestConfig {
|
|
5
5
|
useRefreshToken: boolean;
|
|
@@ -22,6 +22,13 @@ export declare class GuardService {
|
|
|
22
22
|
* @param tokens
|
|
23
23
|
*/
|
|
24
24
|
setTokens(tokens: Tokens): void;
|
|
25
|
+
/**
|
|
26
|
+
* Allow to retrieve a M2M token for a service
|
|
27
|
+
*
|
|
28
|
+
* @param req The credentials required to get an access token
|
|
29
|
+
* @returns AuthTokenResponse
|
|
30
|
+
*/
|
|
31
|
+
m2mToken(req: M2MTokenRequest): Promise<AuthTokenResponse>;
|
|
25
32
|
/**
|
|
26
33
|
* Allow to retrieve an access token and a refresh token in order
|
|
27
34
|
* to do authenticated request afterward
|
|
@@ -97,4 +104,18 @@ export declare class GuardService {
|
|
|
97
104
|
* @return void
|
|
98
105
|
*/
|
|
99
106
|
identitySendConfirmEmail(req: IdentityResendConfirmEmailRequest): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Get an identity using a customer email (format: customer+[b64Hash]@orohealth.me)
|
|
109
|
+
*
|
|
110
|
+
* @param email the customer email
|
|
111
|
+
* @returns IdentityResponse
|
|
112
|
+
*/
|
|
113
|
+
identityGetByCustomerEmail(email: string): Promise<IdentityResponse>;
|
|
114
|
+
/**
|
|
115
|
+
* Get an identity using a base64 hash
|
|
116
|
+
*
|
|
117
|
+
* @param b64Hash base64 hash of the identity
|
|
118
|
+
* @returns IdentityResponse
|
|
119
|
+
*/
|
|
120
|
+
identityGetByHash(b64Hash: string): Promise<IdentityResponse>;
|
|
100
121
|
}
|
package/dist/services/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.15.0",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
4
|
"typings": "dist/index.d.ts",
|
|
5
5
|
"files": [
|
|
@@ -19,12 +19,8 @@
|
|
|
19
19
|
"prepare": "tsdx build",
|
|
20
20
|
"size": "size-limit",
|
|
21
21
|
"analyze": "size-limit --why",
|
|
22
|
-
"package": "tsdx build && npm publish"
|
|
23
|
-
|
|
24
|
-
"husky": {
|
|
25
|
-
"hooks": {
|
|
26
|
-
"pre-commit": "tsdx lint"
|
|
27
|
-
}
|
|
22
|
+
"package": "tsdx build && npm publish",
|
|
23
|
+
"pretty": "prettier --config ../../.prettierrc.yaml --write './src/**/*.{ts,js,json,md}' && prettier --write './*.md'"
|
|
28
24
|
},
|
|
29
25
|
"name": "oro-sdk-apis",
|
|
30
26
|
"author": "Antoine Jaouën <antoine@orohealth.me>",
|
|
@@ -45,8 +41,7 @@
|
|
|
45
41
|
"@types/jest": "^27.4.1",
|
|
46
42
|
"@types/sha.js": "^2.4.0",
|
|
47
43
|
"@types/uuid": "^8.3.0",
|
|
48
|
-
"
|
|
49
|
-
"prettier": "^2.5.1",
|
|
44
|
+
"prettier": "^2.6.1",
|
|
50
45
|
"prettier-plugin-svelte": "^2.3.0",
|
|
51
46
|
"size-limit": "^4.10.2",
|
|
52
47
|
"tsdx": "^0.14.1",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { sha256 } from 'hash.js'
|
|
2
|
+
import { Buffer } from 'buffer/'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This function return a base64 string representation of a hashed string
|
|
6
|
+
* @param value the string to hash
|
|
7
|
+
* @returns a base64 string representation of a hashed value
|
|
8
|
+
*/
|
|
9
|
+
export function hashToBase64String(value: string): string {
|
|
10
|
+
return Buffer.from(sha256().update(value).digest('hex'), 'hex').toString('base64')
|
|
11
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ServiceCollection, ServiceCollectionRequest } from '../models'
|
|
2
|
+
import {
|
|
3
|
+
APIService,
|
|
4
|
+
ConsultService,
|
|
5
|
+
DiagnosisService,
|
|
6
|
+
GuardService,
|
|
7
|
+
PracticeService,
|
|
8
|
+
TellerService,
|
|
9
|
+
VaultService,
|
|
10
|
+
WorkflowService,
|
|
11
|
+
} from '../services'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This function is used to initialize services with a provided url
|
|
15
|
+
* @param services an object containing the url of the services to init
|
|
16
|
+
* @param authenticationCallback (optional) the authentification callback. Called when the token were not able to be refreshed.
|
|
17
|
+
* @param useLocalStorage (default: true) if true store tokens into local storage (only for browsers)
|
|
18
|
+
* @returns an instance of each services with a provided url
|
|
19
|
+
*/
|
|
20
|
+
export const init = (
|
|
21
|
+
services: ServiceCollectionRequest,
|
|
22
|
+
authenticationCallback?: (err: Error, practiceUuid?: string) => void,
|
|
23
|
+
useLocalStorage = true
|
|
24
|
+
): ServiceCollection => {
|
|
25
|
+
const {
|
|
26
|
+
tellerBaseURL,
|
|
27
|
+
practiceBaseURL,
|
|
28
|
+
consultBaseURL,
|
|
29
|
+
vaultBaseURL,
|
|
30
|
+
guardBaseURL,
|
|
31
|
+
workflowBaseURL,
|
|
32
|
+
diagnosisBaseURL,
|
|
33
|
+
} = services
|
|
34
|
+
|
|
35
|
+
const apiService = new APIService(useLocalStorage, undefined, authenticationCallback)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
apiService,
|
|
39
|
+
tellerService: tellerBaseURL ? new TellerService(apiService, tellerBaseURL) : undefined,
|
|
40
|
+
practiceService: practiceBaseURL ? new PracticeService(apiService, practiceBaseURL) : undefined,
|
|
41
|
+
consultService: consultBaseURL ? new ConsultService(apiService, consultBaseURL) : undefined,
|
|
42
|
+
vaultService: vaultBaseURL ? new VaultService(apiService, vaultBaseURL) : undefined,
|
|
43
|
+
guardService: guardBaseURL ? new GuardService(apiService, guardBaseURL) : undefined,
|
|
44
|
+
workflowService: workflowBaseURL ? new WorkflowService(apiService, workflowBaseURL) : undefined,
|
|
45
|
+
diagnosisService: diagnosisBaseURL ? new DiagnosisService(apiService, diagnosisBaseURL) : undefined,
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,56 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
APIService,
|
|
3
|
-
TellerService,
|
|
4
|
-
VaultService,
|
|
5
|
-
GuardService,
|
|
6
|
-
PracticeService,
|
|
7
|
-
ConsultService,
|
|
8
|
-
WorkflowService,
|
|
9
|
-
DiagnosisService,
|
|
10
|
-
} from './services'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* This function is used to initialize services with a provided url
|
|
14
|
-
* @param services an object containing the url of the services to init
|
|
15
|
-
* @param (optional) authenticationCallback the authentification callback
|
|
16
|
-
* @returns an instance of each services with a provided url
|
|
17
|
-
*/
|
|
18
|
-
const init = (
|
|
19
|
-
services: {
|
|
20
|
-
tellerBaseURL?: string
|
|
21
|
-
vaultBaseURL?: string
|
|
22
|
-
guardBaseURL?: string
|
|
23
|
-
practiceBaseURL?: string
|
|
24
|
-
consultBaseURL?: string
|
|
25
|
-
workflowBaseURL?: string
|
|
26
|
-
diagnosisBaseURL?: string
|
|
27
|
-
},
|
|
28
|
-
authenticationCallback?: (err: Error) => void
|
|
29
|
-
) => {
|
|
30
|
-
const {
|
|
31
|
-
tellerBaseURL,
|
|
32
|
-
practiceBaseURL,
|
|
33
|
-
consultBaseURL,
|
|
34
|
-
vaultBaseURL,
|
|
35
|
-
guardBaseURL,
|
|
36
|
-
workflowBaseURL,
|
|
37
|
-
diagnosisBaseURL,
|
|
38
|
-
} = services
|
|
39
|
-
|
|
40
|
-
const apiService = new APIService(undefined, authenticationCallback)
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
tellerService: tellerBaseURL ? new TellerService(apiService, tellerBaseURL) : undefined,
|
|
44
|
-
practiceService: practiceBaseURL ? new PracticeService(apiService, practiceBaseURL) : undefined,
|
|
45
|
-
consultService: consultBaseURL ? new ConsultService(apiService, consultBaseURL) : undefined,
|
|
46
|
-
vaultService: vaultBaseURL ? new VaultService(apiService, vaultBaseURL) : undefined,
|
|
47
|
-
guardService: guardBaseURL ? new GuardService(apiService, guardBaseURL) : undefined,
|
|
48
|
-
workflowService: workflowBaseURL ? new WorkflowService(apiService, workflowBaseURL) : undefined,
|
|
49
|
-
diagnosisService: diagnosisBaseURL ? new DiagnosisService(apiService, diagnosisBaseURL) : undefined,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
1
|
+
import { init } from './helpers'
|
|
53
2
|
|
|
3
|
+
export * from './helpers'
|
|
54
4
|
export * from './models'
|
|
55
5
|
export * from './services'
|
|
56
6
|
export default init
|
package/src/models/guard.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Uuid, Base64String, TokenData, RFC3339Date, Url } from './'
|
|
2
2
|
|
|
3
|
-
export type AuthRefreshFunc = (
|
|
4
|
-
refreshToken?: string
|
|
5
|
-
) => Promise<AuthTokenResponse>
|
|
3
|
+
export type AuthRefreshFunc = (refreshToken?: string) => Promise<AuthTokenResponse>
|
|
6
4
|
export interface Tokens {
|
|
7
5
|
accessToken?: string
|
|
8
6
|
refreshToken?: string
|
|
@@ -17,10 +15,21 @@ export interface AuthTokenRequest {
|
|
|
17
15
|
export interface AuthTokenResponse {
|
|
18
16
|
accessToken: string
|
|
19
17
|
tokenType: string
|
|
20
|
-
refreshToken
|
|
18
|
+
refreshToken?: string
|
|
21
19
|
expiresIn: number
|
|
22
20
|
}
|
|
23
21
|
|
|
22
|
+
/**
|
|
23
|
+
* This interface is used to request a M2M token as a service
|
|
24
|
+
*/
|
|
25
|
+
export interface M2MTokenRequest {
|
|
26
|
+
clientId: string
|
|
27
|
+
clientSecret: string
|
|
28
|
+
requestedScopes: string[]
|
|
29
|
+
practiceUuid: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
24
33
|
export interface AuthRecoverRequest {
|
|
25
34
|
practiceUuid: string
|
|
26
35
|
email: string
|
package/src/models/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export * from './consult'
|
|
2
2
|
export * from './diagnosis'
|
|
3
3
|
export * from './error'
|
|
4
|
+
export * from './external'
|
|
4
5
|
export * from './guard'
|
|
6
|
+
export * from './init'
|
|
5
7
|
export * from './practice'
|
|
6
8
|
export * from './shared'
|
|
7
9
|
export * from './vault'
|
|
8
10
|
export * from './workflow'
|
|
9
|
-
export * from './external'
|
|
11
|
+
export * from './external'
|
|
12
|
+
export * from './user'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
APIService,
|
|
3
|
+
ConsultService,
|
|
4
|
+
DiagnosisService,
|
|
5
|
+
GuardService,
|
|
6
|
+
PracticeService,
|
|
7
|
+
TellerService,
|
|
8
|
+
VaultService,
|
|
9
|
+
WorkflowService,
|
|
10
|
+
} from '../services'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This interface represents a collection of service urls you need to initialize
|
|
14
|
+
*/
|
|
15
|
+
export interface ServiceCollectionRequest {
|
|
16
|
+
tellerBaseURL?: string
|
|
17
|
+
vaultBaseURL?: string
|
|
18
|
+
guardBaseURL?: string
|
|
19
|
+
practiceBaseURL?: string
|
|
20
|
+
consultBaseURL?: string
|
|
21
|
+
workflowBaseURL?: string
|
|
22
|
+
diagnosisBaseURL?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This interface represents a collection of service
|
|
27
|
+
*/
|
|
28
|
+
export interface ServiceCollection {
|
|
29
|
+
apiService: APIService
|
|
30
|
+
tellerService?: TellerService
|
|
31
|
+
practiceService?: PracticeService
|
|
32
|
+
consultService?: ConsultService
|
|
33
|
+
vaultService?: VaultService
|
|
34
|
+
guardService?: GuardService
|
|
35
|
+
workflowService?: WorkflowService
|
|
36
|
+
diagnosisService?: DiagnosisService
|
|
37
|
+
}
|
package/src/models/workflow.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface LanguagePickerData extends ChoiceInputData {
|
|
|
32
32
|
export interface EntryData {
|
|
33
33
|
id?: number
|
|
34
34
|
label?: string
|
|
35
|
+
hideLabel?: boolean
|
|
36
|
+
minorLabel?: string
|
|
35
37
|
summaryLabel?: string
|
|
36
38
|
summaryHidden?: boolean
|
|
37
39
|
className?: string
|
|
@@ -78,15 +80,8 @@ export interface GroupedGenericQuestionData<T, A = IndexedData<ChoiceInputData>>
|
|
|
78
80
|
order?: number
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
export type QuestionData =
|
|
82
|
-
|
|
83
|
-
| GenericQuestionData<
|
|
84
|
-
'text' | 'date' | 'number' | 'images' | 'images-alias' | 'body-parts' | 'pharmacy-picker' | 'place-address'
|
|
85
|
-
>
|
|
86
|
-
| GenericQuestionData<'checkbox-group' | 'select' | 'multiple', IndexedData<ChoiceInputData>>
|
|
87
|
-
| GroupedGenericQuestionData<'radio', IndexedData<RadioInputData>>
|
|
88
|
-
| GroupedGenericQuestionData<'radio-card', IndexedData<RadioCardInputData>>
|
|
89
|
-
| GroupedGenericQuestionData<'language-picker', IndexedData<LanguagePickerData>>
|
|
83
|
+
export declare type QuestionData = GenericQuestionData<'title' | 'paragraph' | 'checkbox', void> | GenericQuestionData<'text' | 'text-area' | 'date' | 'number' | 'images' | 'images-alias' | 'body-parts' | 'pharmacy-picker' | 'place-address'> | GenericQuestionData<'checkbox-group' | 'select' | 'multiple' | 'text-select-group', IndexedData<ChoiceInputData>> | GroupedGenericQuestionData<'radio', IndexedData<RadioInputData>> | GroupedGenericQuestionData<'radio-card', IndexedData<RadioCardInputData>> | GroupedGenericQuestionData<'language-picker', IndexedData<LanguagePickerData>>;
|
|
84
|
+
|
|
90
85
|
|
|
91
86
|
export interface FieldData {
|
|
92
87
|
type: 'field'
|
package/src/services/api.ts
CHANGED
|
@@ -4,11 +4,18 @@ import { AuthRefreshFunc, Tokens } from '../models'
|
|
|
4
4
|
import { AxiosService } from './axios'
|
|
5
5
|
import { GuardRequestConfig } from './guard'
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
export class APIService extends AxiosService {
|
|
9
8
|
private authRefreshFn?: AuthRefreshFunc
|
|
9
|
+
private tokens: Tokens = {}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* The API Service lets you use an axios API and handles oro backend services authentification via JWT tokens
|
|
13
|
+
* @param useLocalStorage if set to true, tokens will be stored in localStorage
|
|
14
|
+
* @param config (optional) an axios config
|
|
15
|
+
* @param tokenRefreshFailureCallback (optional) callback to call when failing to refresh the auth token
|
|
16
|
+
*/
|
|
11
17
|
constructor(
|
|
18
|
+
private useLocalStorage: boolean,
|
|
12
19
|
config?: AxiosRequestConfig,
|
|
13
20
|
private tokenRefreshFailureCallback?: (err: Error) => void
|
|
14
21
|
) {
|
|
@@ -16,7 +23,7 @@ export class APIService extends AxiosService {
|
|
|
16
23
|
const self = this
|
|
17
24
|
|
|
18
25
|
this.axios.interceptors.request.use(
|
|
19
|
-
config => {
|
|
26
|
+
(config) => {
|
|
20
27
|
const token = (config as GuardRequestConfig).useRefreshToken
|
|
21
28
|
? self.getTokens().refreshToken
|
|
22
29
|
: self.getTokens().accessToken
|
|
@@ -25,37 +32,40 @@ export class APIService extends AxiosService {
|
|
|
25
32
|
...config.headers,
|
|
26
33
|
Authorization: `Bearer ${token}`,
|
|
27
34
|
}
|
|
28
|
-
return config
|
|
35
|
+
return config
|
|
29
36
|
},
|
|
30
|
-
error => {
|
|
37
|
+
(error) => {
|
|
31
38
|
Promise.reject(error)
|
|
32
39
|
}
|
|
33
|
-
)
|
|
40
|
+
)
|
|
34
41
|
|
|
35
|
-
createAuthRefreshInterceptor(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
createAuthRefreshInterceptor(
|
|
43
|
+
this.axios,
|
|
44
|
+
async function (failedRequest) {
|
|
45
|
+
if (self.authRefreshFn) {
|
|
46
|
+
try {
|
|
47
|
+
let tokenResp = await self.authRefreshFn(self.getTokens().refreshToken)
|
|
48
|
+
self.setTokens({
|
|
49
|
+
accessToken: tokenResp.accessToken,
|
|
50
|
+
refreshToken: tokenResp.refreshToken,
|
|
51
|
+
})
|
|
52
|
+
failedRequest.response.config.headers['Authorization'] = `Bearer ${
|
|
53
|
+
self.getTokens().accessToken
|
|
54
|
+
}`
|
|
55
|
+
return Promise.resolve()
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.error('an error occured while refreshing tokens (notifying callback)', e)
|
|
58
|
+
if (self.tokenRefreshFailureCallback) self.tokenRefreshFailureCallback(failedRequest)
|
|
59
|
+
return Promise.resolve() // We keep it like that. Otherwise, it seems to break the api service will it is not needed
|
|
60
|
+
// return Promise.reject(e)
|
|
61
|
+
}
|
|
52
62
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
console.error('The request could not refresh the token (authRefreshFn was not set)', failedRequest)
|
|
64
|
+
return Promise.resolve() // We keep it like that. Otherwise, it seems to break the api service will it is not needed
|
|
65
|
+
// return Promise.reject(failedRequest)
|
|
66
|
+
},
|
|
67
|
+
{ statusCodes: [401, 403] }
|
|
68
|
+
)
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
public setAuthRefreshFn(fn: AuthRefreshFunc) {
|
|
@@ -63,15 +73,22 @@ export class APIService extends AxiosService {
|
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
public setTokens(tokens: Tokens) {
|
|
66
|
-
|
|
76
|
+
if (this.useLocalStorage) {
|
|
77
|
+
localStorage.setItem('tokens', JSON.stringify(tokens))
|
|
78
|
+
}
|
|
79
|
+
this.tokens = tokens
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
public getTokens(): Tokens {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
if (this.useLocalStorage) {
|
|
84
|
+
let tokens: Tokens = {}
|
|
85
|
+
const item = localStorage.getItem('tokens')
|
|
86
|
+
if (item) {
|
|
87
|
+
tokens = JSON.parse(item)
|
|
88
|
+
}
|
|
89
|
+
return tokens
|
|
90
|
+
} else {
|
|
91
|
+
return this.tokens
|
|
74
92
|
}
|
|
75
|
-
return tokens
|
|
76
93
|
}
|
|
77
94
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { init } from '../helpers'
|
|
2
|
+
import { AuthTokenResponse, ServiceCollection, ServiceCollectionRequest } from '../models'
|
|
3
|
+
import { GuardService } from './guard'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This service enables you to handle one authentication token per practice
|
|
7
|
+
*/
|
|
8
|
+
export class ApisPracticeManager {
|
|
9
|
+
private practiceInstances = new Map<string, ServiceCollection>()
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The constructor
|
|
13
|
+
* @param serviceCollReq the services to initialize. Only filled urls will get corresponding service to be initialized.
|
|
14
|
+
* It will be used each time a new practices needs a `ServiceCollection`
|
|
15
|
+
* @param getAuthTokenCbk the callback function used to get a new JWT token
|
|
16
|
+
* @param useLocalStorage (default: false) if true store tokens into local storage (only for browsers)
|
|
17
|
+
*/
|
|
18
|
+
constructor(
|
|
19
|
+
private serviceCollReq: ServiceCollectionRequest,
|
|
20
|
+
private getAuthTokenCbk: (guard: GuardService, practiceUuid: string) => Promise<AuthTokenResponse>,
|
|
21
|
+
private useLocalStorage = false
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This function is used to get a `ServiceCollection` associated to a practice. If missing, it will initialize a new `ServiceCollection`.
|
|
26
|
+
* @param practiceUuid the uuid of the practice
|
|
27
|
+
* @returns a promise holding a `ServiceCollection`
|
|
28
|
+
*/
|
|
29
|
+
public async get(practiceUuid: string): Promise<ServiceCollection> {
|
|
30
|
+
const practiceInstance = this.practiceInstances.get(practiceUuid)
|
|
31
|
+
if (practiceInstance) return practiceInstance
|
|
32
|
+
|
|
33
|
+
const newPracticeInstance = init(this.serviceCollReq, undefined, this.useLocalStorage)
|
|
34
|
+
|
|
35
|
+
// Create one auth token callback per practice since the practice uuid needs to change
|
|
36
|
+
const authTokenFunc = async () => {
|
|
37
|
+
if (newPracticeInstance.guardService) {
|
|
38
|
+
console.log(`\x1b[36m[Auth] Refresh auth called (practiceUuid: ${practiceUuid})\x1b[36m`)
|
|
39
|
+
return await this.getAuthTokenCbk(newPracticeInstance.guardService, practiceUuid)
|
|
40
|
+
} else {
|
|
41
|
+
throw Error('[Auth] Unable to refresh token guard service is undefined')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Set the refresh tokens callback
|
|
46
|
+
newPracticeInstance.apiService.setAuthRefreshFn(authTokenFunc)
|
|
47
|
+
|
|
48
|
+
this.practiceInstances.set(practiceUuid, newPracticeInstance)
|
|
49
|
+
|
|
50
|
+
return newPracticeInstance
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/services/guard.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
IdentityResendConfirmEmailRequest,
|
|
17
17
|
IdentityResponse,
|
|
18
18
|
IdentityUpdateRequest,
|
|
19
|
+
M2MTokenRequest,
|
|
19
20
|
QRCodeRequest,
|
|
20
21
|
QRCodeResponse,
|
|
21
22
|
Tokens,
|
|
@@ -32,7 +33,7 @@ export class GuardService {
|
|
|
32
33
|
private whoAmICache: Record<string, WhoAmIResponse>
|
|
33
34
|
|
|
34
35
|
constructor(private api: APIService, private baseURL: string) {
|
|
35
|
-
this.api.setAuthRefreshFn(this.authRefresh.bind(this))
|
|
36
|
+
this.api.setAuthRefreshFn(this.authRefresh.bind(this)) // This is the default behavior for User JWT tokens. If you want other kind of refresh you shall overwrite this call
|
|
36
37
|
this.identityCache = {}
|
|
37
38
|
this.whoAmICache = {}
|
|
38
39
|
}
|
|
@@ -52,6 +53,46 @@ export class GuardService {
|
|
|
52
53
|
this.api.setTokens({ ...this.api.getTokens(), ...tokens })
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Allow to retrieve a M2M token for a service
|
|
58
|
+
*
|
|
59
|
+
* @param req The credentials required to get an access token
|
|
60
|
+
* @returns AuthTokenResponse
|
|
61
|
+
*/
|
|
62
|
+
public async m2mToken(req: M2MTokenRequest): Promise<AuthTokenResponse> {
|
|
63
|
+
let resp: AuthTokenResponse | undefined
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
let config: AxiosAuthRefreshRequestConfig = {
|
|
67
|
+
skipAuthRefresh: true,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
resp = await this.api.post<AuthTokenResponse>(`${this.baseURL}/v1/m2m/token`, req, config)
|
|
71
|
+
|
|
72
|
+
this.api.setTokens({
|
|
73
|
+
accessToken: resp.accessToken,
|
|
74
|
+
})
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error('Error while posting m2m token:', e)
|
|
77
|
+
|
|
78
|
+
if ((e as any).isAxiosError) {
|
|
79
|
+
const code = (e as AxiosError).response?.status
|
|
80
|
+
switch (code) {
|
|
81
|
+
case 400:
|
|
82
|
+
throw new AuthenticationBadRequest()
|
|
83
|
+
case 500:
|
|
84
|
+
throw new AuthenticationServerError()
|
|
85
|
+
case 401:
|
|
86
|
+
default:
|
|
87
|
+
throw new AuthenticationFailed()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
throw new AuthenticationFailed()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return resp
|
|
94
|
+
}
|
|
95
|
+
|
|
55
96
|
/**
|
|
56
97
|
* Allow to retrieve an access token and a refresh token in order
|
|
57
98
|
* to do authenticated request afterward
|
|
@@ -74,6 +115,8 @@ export class GuardService {
|
|
|
74
115
|
refreshToken: resp.refreshToken,
|
|
75
116
|
})
|
|
76
117
|
} catch (e) {
|
|
118
|
+
console.error('Error while posting auth token:', e)
|
|
119
|
+
|
|
77
120
|
if ((e as any).isAxiosError) {
|
|
78
121
|
const code = (e as AxiosError).response?.status
|
|
79
122
|
switch (code) {
|
|
@@ -157,7 +200,7 @@ export class GuardService {
|
|
|
157
200
|
}
|
|
158
201
|
return resp
|
|
159
202
|
}
|
|
160
|
-
|
|
203
|
+
|
|
161
204
|
/**
|
|
162
205
|
* Retrieve an identity. Will return public fields only when requested
|
|
163
206
|
* without authentication
|
|
@@ -229,4 +272,31 @@ export class GuardService {
|
|
|
229
272
|
public async identitySendConfirmEmail(req: IdentityResendConfirmEmailRequest): Promise<void> {
|
|
230
273
|
return this.api.post<void>(`${this.baseURL}/v1/identity/confirm`, req)
|
|
231
274
|
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get an identity using a customer email (format: customer+[b64Hash]@orohealth.me)
|
|
278
|
+
*
|
|
279
|
+
* @param email the customer email
|
|
280
|
+
* @returns IdentityResponse
|
|
281
|
+
*/
|
|
282
|
+
public async identityGetByCustomerEmail(email: string): Promise<IdentityResponse> {
|
|
283
|
+
return this.identityGetByHash(email.substring(email.indexOf('+') + 1, email.indexOf('@')))
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get an identity using a base64 hash
|
|
288
|
+
*
|
|
289
|
+
* @param b64Hash base64 hash of the identity
|
|
290
|
+
* @returns IdentityResponse
|
|
291
|
+
*/
|
|
292
|
+
public async identityGetByHash(b64Hash: string): Promise<IdentityResponse> {
|
|
293
|
+
//TODO: Right now this maps directly to the IdentityGet call.
|
|
294
|
+
//Eventually, with the mapping table method, this would lead to another
|
|
295
|
+
//call (ie: /v1/mapping/[b64Hash]) which would return a blob to decrypt
|
|
296
|
+
//which would contain the real identityID to call IdentityGet with.
|
|
297
|
+
|
|
298
|
+
//The hash comes in base64 format but it isn't URL safe soe we have to convert
|
|
299
|
+
//to base64URL (see https://en.wikipedia.org/wiki/Base64#The_URL_applications)
|
|
300
|
+
return this.identityGet(b64Hash.replace('+', '-').replace('/', '_'))
|
|
301
|
+
}
|
|
232
302
|
}
|