Comparing version 1.4.2 to 1.5.0
@@ -0,1 +1,8 @@ | ||
# [1.5.0](https://github.com/philips-software/javascript-hsdp-sdk/compare/v1.4.2...v1.5.0) (2024-03-20) | ||
### Features | ||
* add IAM calls and IDM User calls ([f489317](https://github.com/philips-software/javascript-hsdp-sdk/commit/f48931727f8d0d83838665e3cbcd25803fd872be)) | ||
## [1.4.2](https://github.com/philips-software/javascript-hsdp-sdk/compare/v1.4.1...v1.4.2) (2024-03-12) | ||
@@ -2,0 +9,0 @@ |
@@ -5,3 +5,3 @@ import { FhirResource, Bundle, BundleEntry } from 'fhir/r4'; | ||
}; | ||
export type R4ResourceType = typeof SUPPORTED_R4_RESOURCE_TYPES[number]; | ||
export type R4ResourceType = (typeof SUPPORTED_R4_RESOURCE_TYPES)[number]; | ||
export type R4Bundle<T> = Omit<Bundle, 'entry'> & { | ||
@@ -8,0 +8,0 @@ entry?: Omit<BundleEntry, 'resource'> & { |
@@ -13,2 +13,17 @@ export type LoginServiceResponse = { | ||
export declare function loginWithServiceAccount(hsdpIamUrl: string, serviceId: string, privateKey: string, params?: Partial<LoginServiceParams>): Promise<LoginServiceResponse>; | ||
interface LoginUserParams { | ||
clientId: string; | ||
clientSecret: string; | ||
username: string; | ||
password: string; | ||
scope?: string; | ||
} | ||
export type LoginUserResponse = { | ||
access_token: string; | ||
refresh_token: string; | ||
scope: string; | ||
expires_in: number; | ||
token_type: string; | ||
}; | ||
export declare function loginAsUser(hsdpIamUrl: string, params: LoginUserParams): Promise<LoginUserResponse>; | ||
export declare type IntrospectResponse = { | ||
@@ -41,3 +56,22 @@ active: boolean; | ||
export declare function introspect(hsdpIamUrl: string, params: IntrospectParams): Promise<IntrospectResponse>; | ||
type RefreshAccessTokenParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
refreshToken: string; | ||
}; | ||
export declare function refreshAccessToken(hsdpIamUrl: string, params: RefreshAccessTokenParams): Promise<LoginUserResponse>; | ||
type LogoutParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
accessToken: string; | ||
}; | ||
export declare function logout(hsdpIamUrl: string, params: LogoutParams): Promise<void>; | ||
type AuthorizeCodeParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
code: string; | ||
redirect_uri: string; | ||
}; | ||
export declare function authorizeCode(hsdpIamUrl: string, params: AuthorizeCodeParams): Promise<LoginUserResponse>; | ||
export {}; | ||
//# sourceMappingURL=oauth2.d.ts.map |
@@ -175,2 +175,4 @@ import { ClientOptions } from './common'; | ||
createUser: (params: import("./users").CreateUserParams) => Promise<import("./users").CreateUserResponse>; | ||
requestPasswordReset: (params: import("./users").RequestPasswordResetParams) => Promise<void>; | ||
setPassword: (params: import("./users").SetPasswordParams) => Promise<void>; | ||
}; | ||
@@ -177,0 +179,0 @@ scimGroups: { |
@@ -7,2 +7,6 @@ export type AuthParams = { | ||
}; | ||
export type HSDPRootOrgKeys = { | ||
sharedKey: string; | ||
secretKey: string; | ||
}; | ||
//# sourceMappingURL=common.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { AuthParams, ClientOptions } from './common'; | ||
import { AuthParams, ClientOptions, HSDPRootOrgKeys } from './common'; | ||
type BaseSearchUsersParams = AuthParams & { | ||
@@ -72,2 +72,11 @@ userId: string; | ||
}; | ||
export type RequestPasswordResetParams = AuthParams & { | ||
loginId: string; | ||
}; | ||
export type SetPasswordParams = AuthParams & HSDPRootOrgKeys & { | ||
loginId: string; | ||
confirmationCode: string; | ||
newPassword: string; | ||
context: 'userCreate' | 'recoverPassword'; | ||
}; | ||
type SearchUsersResponse<ProfileType extends SearchUsersParams['profileType']> = (User & (ProfileType extends 'membership' ? UserMembership : ProfileType extends 'accountStatus' ? UserAccountStatus : ProfileType extends 'passwordStatus' ? UserPasswordStatus : ProfileType extends 'consentedApps' ? UserConsentedApps : never))[]; | ||
@@ -82,4 +91,6 @@ export type CreateUserResponse = { | ||
createUser: (params: CreateUserParams) => Promise<CreateUserResponse>; | ||
requestPasswordReset: (params: RequestPasswordResetParams) => Promise<void>; | ||
setPassword: (params: SetPasswordParams) => Promise<void>; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=users.d.ts.map |
export declare const omitKeys: <T extends Record<string, any>>(object: T, keys: (keyof T)[]) => {}; | ||
export declare const generateHSDPApiSignature: (sharedKey: string, secretKey: string) => { | ||
'HSDP-API-Signature': string; | ||
SignedDate: string; | ||
}; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -9,7 +9,8 @@ # IAM | ||
- [ ] OAuth2 | ||
- [ ] Login as User | ||
- [x] Login as User | ||
- [x] Login as Service | ||
- [ ] Refresh Access Token | ||
- [ ] Introspect | ||
- [ ] Logout | ||
- [x] Refresh Access Token | ||
- [x] Introspect | ||
- [x] Logout | ||
- [x] Authorization code | ||
- [ ] Token | ||
@@ -16,0 +17,0 @@ |
@@ -15,4 +15,4 @@ # IDM | ||
- [ ] Service | ||
- [ ] User | ||
- [ ] SCIM User | ||
- [x] User | ||
- [x] SCIM User | ||
- [ ] Email Template | ||
@@ -19,0 +19,0 @@ - [ ] Password Policy |
{ | ||
"name": "hsdp-sdk", | ||
"version": "1.4.2", | ||
"version": "1.5.0", | ||
"main": "dist/index.js", | ||
@@ -49,4 +49,4 @@ "author": "Gertjan Maas <gertjan.maas@philips.com>", | ||
"pre-commit": "lint-staged", | ||
"format": "prettier --write --no-error-on-unmatched-pattern **/*.{js,json,yml,yaml,css,scss,ts,md}", | ||
"format-check": "prettier --check --no-error-on-unmatched-pattern **/*.{js,json,yml,yaml,css,scss,ts,md}", | ||
"format": "prettier --write --no-error-on-unmatched-pattern \"**/*.{js,json,yml,yaml,css,scss,ts,md}\"", | ||
"format-check": "prettier --check --no-error-on-unmatched-pattern\" **/*.{js,json,yml,yaml,css,scss,ts,md}\"", | ||
"test": "NODE_ENV=test jest", | ||
@@ -68,3 +68,3 @@ "test:coverage": "yarn test --coverage --watchAll", | ||
"dependencies": { | ||
"axios": "^1.6.7", | ||
"axios": "^1.6.8", | ||
"jsrsasign": "^11.1.0", | ||
@@ -71,0 +71,0 @@ "zod": "^3.22.4" |
@@ -90,3 +90,3 @@ import nock from 'nock'; | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -119,3 +119,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -149,3 +149,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -179,3 +179,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -182,0 +182,0 @@ ); |
@@ -5,3 +5,3 @@ import { FhirResource, Bundle, BundleEntry } from 'fhir/r4'; | ||
export type R4ResourceType = typeof SUPPORTED_R4_RESOURCE_TYPES[number]; | ||
export type R4ResourceType = (typeof SUPPORTED_R4_RESOURCE_TYPES)[number]; | ||
@@ -8,0 +8,0 @@ export type R4Bundle<T> = Omit<Bundle, 'entry'> & { |
@@ -164,3 +164,3 @@ import { | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -194,3 +194,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -225,3 +225,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -255,3 +255,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -291,3 +291,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -294,0 +294,0 @@ ); |
@@ -78,2 +78,40 @@ import axios from 'axios'; | ||
interface LoginUserParams { | ||
clientId: string; | ||
clientSecret: string; | ||
username: string; | ||
password: string; | ||
scope?: string; | ||
} | ||
export type LoginUserResponse = { | ||
access_token: string; | ||
refresh_token: string; | ||
scope: string; | ||
expires_in: number; | ||
token_type: string; | ||
}; | ||
export async function loginAsUser(hsdpIamUrl: string, params: LoginUserParams) { | ||
const searchParams = new URLSearchParams({ | ||
grant_type: 'password', | ||
username: params.username, | ||
password: params.password, | ||
...(params.scope ? { scope: params.scope } : {}), | ||
}); | ||
const response = await axios.post<LoginUserResponse>( | ||
`${hsdpIamUrl}/authorize/oauth2/token`, | ||
searchParams.toString(), | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '2', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
}, | ||
); | ||
return response.data; | ||
} | ||
export declare type IntrospectResponse = { | ||
@@ -112,15 +150,14 @@ active: boolean; | ||
return axios | ||
.post<IntrospectResponse>( | ||
`${hsdpIamUrl}/authorize/oauth2/introspect`, | ||
searchParams.toString(), | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '4', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
const response = await axios.post<IntrospectResponse>( | ||
`${hsdpIamUrl}/authorize/oauth2/introspect`, | ||
searchParams.toString(), | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '4', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
) | ||
.then((r) => r.data); | ||
}, | ||
); | ||
return response.data; | ||
} | ||
@@ -132,1 +169,74 @@ | ||
} | ||
type RefreshAccessTokenParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
refreshToken: string; | ||
}; | ||
export async function refreshAccessToken(hsdpIamUrl: string, params: RefreshAccessTokenParams) { | ||
const searchParams = new URLSearchParams({ | ||
grant_type: 'refresh_token', | ||
refresh_token: params.refreshToken, | ||
}); | ||
const response = await axios.post<LoginUserResponse>( | ||
`${hsdpIamUrl}/authorize/oauth2/token`, | ||
searchParams.toString(), | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '2', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
}, | ||
); | ||
return response.data; | ||
} | ||
type LogoutParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
accessToken: string; | ||
}; | ||
export async function logout(hsdpIamUrl: string, params: LogoutParams) { | ||
const searchParams = new URLSearchParams({ | ||
token: params.accessToken, | ||
}); | ||
await axios.post(`${hsdpIamUrl}/authorize/oauth2/revoke`, searchParams.toString(), { | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '2', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
}); | ||
} | ||
type AuthorizeCodeParams = { | ||
clientId: string; | ||
clientSecret: string; | ||
code: string; | ||
redirect_uri: string; | ||
}; | ||
export async function authorizeCode(hsdpIamUrl: string, params: AuthorizeCodeParams) { | ||
const searchParams = new URLSearchParams({ | ||
grant_type: 'authorization_code', | ||
code: params.code, | ||
redirect_uri: params.redirect_uri, | ||
}); | ||
const response = await axios.post<LoginUserResponse>( | ||
`${hsdpIamUrl}/authorize/oauth2/token`, | ||
searchParams.toString(), | ||
{ | ||
headers: { | ||
Accept: 'application/json', | ||
'Api-version': '2', | ||
...encodeCredentials(params.clientId, params.clientSecret), | ||
}, | ||
}, | ||
); | ||
return response.data; | ||
} |
@@ -8,1 +8,6 @@ export type AuthParams = { | ||
}; | ||
export type HSDPRootOrgKeys = { | ||
sharedKey: string; | ||
secretKey: string; | ||
}; |
@@ -150,3 +150,3 @@ import nock from 'nock'; | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -178,3 +178,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -203,3 +203,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -229,3 +229,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -256,3 +256,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -282,3 +282,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -309,3 +309,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -336,3 +336,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -339,0 +339,0 @@ ); |
@@ -155,3 +155,3 @@ import nock from 'nock'; | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -191,3 +191,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -217,3 +217,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -242,3 +242,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -245,0 +245,0 @@ ); |
@@ -111,3 +111,3 @@ import nock from 'nock'; | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -145,3 +145,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -170,3 +170,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -196,3 +196,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -227,3 +227,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -258,3 +258,3 @@ ); | ||
}), | ||
).rejects.toThrowError(expectedException); | ||
).rejects.toThrow(expectedException); | ||
}, | ||
@@ -261,0 +261,0 @@ ); |
@@ -70,13 +70,13 @@ import axios from 'axios'; | ||
: T extends 'DEVICE' | ||
? DeviceInGroup | ||
: T extends 'SERVICE' | ||
? ServiceInGroup | ||
: never; | ||
? DeviceInGroup | ||
: T extends 'SERVICE' | ||
? ServiceInGroup | ||
: never; | ||
type ResponseTypeObject<T> = T extends 'USER' | ||
? ScimUser | ||
: T extends 'DEVICE' | ||
? ScimDevice | ||
: T extends 'SERVICE' | ||
? ScimService | ||
: never; | ||
? ScimDevice | ||
: T extends 'SERVICE' | ||
? ScimService | ||
: never; | ||
@@ -83,0 +83,0 @@ export type Group<T> = { |
import axios from 'axios'; | ||
import { AuthParams, ClientOptions } from './common'; | ||
import { omitKeys } from './utils'; | ||
import { AuthParams, ClientOptions, HSDPRootOrgKeys } from './common'; | ||
import { generateHSDPApiSignature, omitKeys } from './utils'; | ||
@@ -87,2 +87,14 @@ type BaseSearchUsersParams = AuthParams & { | ||
export type RequestPasswordResetParams = AuthParams & { | ||
loginId: string; | ||
}; | ||
export type SetPasswordParams = AuthParams & | ||
HSDPRootOrgKeys & { | ||
loginId: string; | ||
confirmationCode: string; | ||
newPassword: string; | ||
context: 'userCreate' | 'recoverPassword'; | ||
}; | ||
type SearchUsersResponse<ProfileType extends SearchUsersParams['profileType']> = (User & | ||
@@ -92,8 +104,8 @@ (ProfileType extends 'membership' | ||
: ProfileType extends 'accountStatus' | ||
? UserAccountStatus | ||
: ProfileType extends 'passwordStatus' | ||
? UserPasswordStatus | ||
: ProfileType extends 'consentedApps' | ||
? UserConsentedApps | ||
: never))[]; | ||
? UserAccountStatus | ||
: ProfileType extends 'passwordStatus' | ||
? UserPasswordStatus | ||
: ProfileType extends 'consentedApps' | ||
? UserConsentedApps | ||
: never))[]; | ||
@@ -180,7 +192,51 @@ type OperationOutcome = { | ||
async function requestPasswordReset(params: RequestPasswordResetParams) { | ||
await axiosInstance.post( | ||
'/$reset-password', | ||
{ | ||
loginId: params.loginId, | ||
}, | ||
{ | ||
headers: { | ||
Authorization: `Bearer ${params.accessToken}`, | ||
'API-Version': 1, | ||
}, | ||
}, | ||
); | ||
} | ||
async function setPassword(params: SetPasswordParams) { | ||
await axiosInstance.post( | ||
'/$set-password', | ||
{ | ||
resourceType: 'Parameters', | ||
parameter: [ | ||
{ | ||
name: 'setPassword', | ||
resource: { | ||
loginId: params.loginId, | ||
confirmationCode: params.confirmationCode, | ||
newPassword: params.newPassword, | ||
context: params.context, | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
headers: { | ||
Authorization: `Bearer ${params.accessToken}`, | ||
'API-Version': 3, | ||
...generateHSDPApiSignature(params.sharedKey, params.secretKey), | ||
}, | ||
}, | ||
); | ||
} | ||
const client = { | ||
searchUsers, | ||
createUser, | ||
requestPasswordReset, | ||
setPassword, | ||
}; | ||
return client; | ||
} |
@@ -0,1 +1,3 @@ | ||
import { BinaryLike, createHmac } from 'crypto'; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -6,1 +8,19 @@ export const omitKeys = <T extends Record<string, any>>(object: T, keys: (keyof T)[]) => | ||
.reduce((result, key) => ({ ...result, [key]: object[key] }), {}); | ||
const sign = (secret: BinaryLike, challenge: BinaryLike) => { | ||
const signHmac = createHmac('sha256', secret); | ||
signHmac.update(challenge); | ||
return signHmac.digest(); | ||
}; | ||
export const generateHSDPApiSignature = (sharedKey: string, secretKey: string) => { | ||
const secretString = `DHPWS${secretKey}`; | ||
const now = new Date(); | ||
const dateString = `${now.getUTCFullYear()}-${now.getUTCMonth() + 1}-${now.getUTCDate()}T${now.getUTCHours()}:${now.getUTCMinutes()}:${now.getUTCSeconds()}Z`; | ||
const seed = Buffer.from(dateString, 'utf8'); | ||
const signature = sign(secretString, seed.toString('base64')); | ||
return { | ||
'HSDP-API-Signature': `HmacSHA256;Credential:${sharedKey};SignedHeaders:SignedDate;Signature:${signature.toString('base64')}`, | ||
SignedDate: dateString, | ||
}; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
468465
108
7331
Updatedaxios@^1.6.8