@idp.global/sdk 1.2.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.
Files changed (46) hide show
  1. package/changelog.md +25 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/index.d.ts +1 -0
  5. package/dist_ts/index.js +2 -0
  6. package/dist_ts_browser/classes.idpclient.d.ts +85 -0
  7. package/dist_ts_browser/classes.idpclient.js +278 -0
  8. package/dist_ts_browser/classes.idprequests.d.ts +59 -0
  9. package/dist_ts_browser/classes.idprequests.js +167 -0
  10. package/dist_ts_browser/index.d.ts +2 -0
  11. package/dist_ts_browser/index.js +3 -0
  12. package/dist_ts_browser/plugins.d.ts +13 -0
  13. package/dist_ts_browser/plugins.js +14 -0
  14. package/dist_ts_server/classes.account-auth-service.d.ts +12 -0
  15. package/dist_ts_server/classes.account-auth-service.js +55 -0
  16. package/dist_ts_server/classes.account-doc.d.ts +21 -0
  17. package/dist_ts_server/classes.account-doc.js +166 -0
  18. package/dist_ts_server/classes.account-store.d.ts +19 -0
  19. package/dist_ts_server/classes.account-store.js +84 -0
  20. package/dist_ts_server/classes.idp-global-server-client.d.ts +23 -0
  21. package/dist_ts_server/classes.idp-global-server-client.js +57 -0
  22. package/dist_ts_server/classes.password-hasher.d.ts +5 -0
  23. package/dist_ts_server/classes.password-hasher.js +36 -0
  24. package/dist_ts_server/index.d.ts +6 -0
  25. package/dist_ts_server/index.js +7 -0
  26. package/dist_ts_server/interfaces.d.ts +37 -0
  27. package/dist_ts_server/interfaces.js +2 -0
  28. package/dist_ts_server/plugins.d.ts +10 -0
  29. package/dist_ts_server/plugins.js +11 -0
  30. package/license +21 -0
  31. package/package.json +70 -0
  32. package/readme.md +10 -0
  33. package/ts/00_commitinfo_data.ts +8 -0
  34. package/ts/index.ts +1 -0
  35. package/ts_browser/classes.idpclient.ts +318 -0
  36. package/ts_browser/classes.idprequests.ts +218 -0
  37. package/ts_browser/index.ts +2 -0
  38. package/ts_browser/plugins.ts +18 -0
  39. package/ts_server/classes.account-auth-service.ts +61 -0
  40. package/ts_server/classes.account-doc.ts +84 -0
  41. package/ts_server/classes.account-store.ts +94 -0
  42. package/ts_server/classes.idp-global-server-client.ts +74 -0
  43. package/ts_server/classes.password-hasher.ts +39 -0
  44. package/ts_server/index.ts +6 -0
  45. package/ts_server/interfaces.ts +41 -0
  46. package/ts_server/plugins.ts +17 -0
@@ -0,0 +1,57 @@
1
+ import * as plugins from './plugins.js';
2
+ export class IdpGlobalServerClient {
3
+ optionsArg;
4
+ typedrouter = new plugins.typedrequest.TypedRouter();
5
+ typedsocket;
6
+ typedsocketDeferred = plugins.smartpromise.defer();
7
+ constructor(optionsArg) {
8
+ this.optionsArg = optionsArg;
9
+ }
10
+ async connect() {
11
+ if (this.typedsocketDeferred.claimed) {
12
+ return this.typedsocketDeferred.promise;
13
+ }
14
+ this.typedsocketDeferred.claim();
15
+ this.typedsocket = await plugins.typedsocket.TypedSocket.createClient(this.typedrouter, this.getTypedRequestUrl());
16
+ this.typedsocketDeferred.resolve(this.typedsocket);
17
+ return this.typedsocketDeferred.promise;
18
+ }
19
+ async stop() {
20
+ await this.typedsocket?.stop();
21
+ this.typedsocket = undefined;
22
+ }
23
+ async loginWithEmailAndPassword(optionsArg) {
24
+ const socket = await this.connect();
25
+ const loginRequest = socket.createTypedRequest('loginWithEmailOrUsernameAndPassword');
26
+ const loginResponse = await loginRequest.fire({
27
+ username: optionsArg.email,
28
+ password: optionsArg.password,
29
+ });
30
+ if (!loginResponse.refreshToken || loginResponse.twoFaNeeded) {
31
+ throw new Error(loginResponse.twoFaNeeded ? 'Two-factor authentication is required' : 'IdP login failed');
32
+ }
33
+ const refreshRequest = socket.createTypedRequest('refreshJwt');
34
+ const refreshResponse = await refreshRequest.fire({ refreshToken: loginResponse.refreshToken });
35
+ if (!refreshResponse.jwt) {
36
+ throw new Error('IdP did not return a JWT');
37
+ }
38
+ const whoIsRequest = socket.createTypedRequest('whoIs');
39
+ const whoIsResponse = await whoIsRequest.fire({ jwt: refreshResponse.jwt });
40
+ return {
41
+ user: whoIsResponse.user,
42
+ jwt: refreshResponse.jwt,
43
+ refreshToken: refreshResponse.refreshToken || loginResponse.refreshToken,
44
+ };
45
+ }
46
+ getTypedRequestUrl() {
47
+ let baseUrl = this.optionsArg.baseUrl.trim();
48
+ if (baseUrl.endsWith('/')) {
49
+ baseUrl = baseUrl.slice(0, -1);
50
+ }
51
+ if (!baseUrl.endsWith('/typedrequest')) {
52
+ baseUrl = `${baseUrl}/typedrequest`;
53
+ }
54
+ return baseUrl;
55
+ }
56
+ }
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5pZHAtZ2xvYmFsLXNlcnZlci1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zZXJ2ZXIvY2xhc3Nlcy5pZHAtZ2xvYmFsLXNlcnZlci1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFZeEMsTUFBTSxPQUFPLHFCQUFxQjtJQUtaO0lBSlosV0FBVyxHQUFRLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUMxRCxXQUFXLENBQW1DO0lBQzlDLG1CQUFtQixHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFtQyxDQUFDO0lBRTVGLFlBQW9CLFVBQXlDO1FBQXpDLGVBQVUsR0FBVixVQUFVLENBQStCO0lBQUcsQ0FBQztJQUUxRCxLQUFLLENBQUMsT0FBTztRQUNsQixJQUFJLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNyQyxPQUFPLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUM7UUFDMUMsQ0FBQztRQUNELElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNqQyxJQUFJLENBQUMsV0FBVyxHQUFHLE1BQU0sT0FBTyxDQUFDLFdBQVcsQ0FBQyxXQUFXLENBQUMsWUFBWSxDQUNuRSxJQUFJLENBQUMsV0FBVyxFQUNoQixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FDMUIsQ0FBQztRQUNGLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ25ELE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQztJQUMxQyxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZixNQUFNLElBQUksQ0FBQyxXQUFXLEVBQUUsSUFBSSxFQUFFLENBQUM7UUFDL0IsSUFBSSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUM7SUFDL0IsQ0FBQztJQUVNLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxVQUErQztRQUNwRixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNwQyxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQXlFLHFDQUFxQyxDQUFDLENBQUM7UUFDOUosTUFBTSxhQUFhLEdBQUcsTUFBTSxZQUFZLENBQUMsSUFBSSxDQUFDO1lBQzVDLFFBQVEsRUFBRSxVQUFVLENBQUMsS0FBSztZQUMxQixRQUFRLEVBQUUsVUFBVSxDQUFDLFFBQVE7U0FDOUIsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLElBQUksYUFBYSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQzdELE1BQU0sSUFBSSxLQUFLLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsdUNBQXVDLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFDNUcsQ0FBQztRQUVELE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBZ0QsWUFBWSxDQUFDLENBQUM7UUFDOUcsTUFBTSxlQUFlLEdBQUcsTUFBTSxjQUFjLENBQUMsSUFBSSxDQUFDLEVBQUUsWUFBWSxFQUFFLGFBQWEsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUFDO1FBQ2hHLElBQUksQ0FBQyxlQUFlLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDekIsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQzlDLENBQUM7UUFFRCxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQTJDLE9BQU8sQ0FBQyxDQUFDO1FBQ2xHLE1BQU0sYUFBYSxHQUFHLE1BQU0sWUFBWSxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUcsRUFBRSxlQUFlLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUM1RSxPQUFPO1lBQ0wsSUFBSSxFQUFFLGFBQWEsQ0FBQyxJQUFJO1lBQ3hCLEdBQUcsRUFBRSxlQUFlLENBQUMsR0FBRztZQUN4QixZQUFZLEVBQUUsZUFBZSxDQUFDLFlBQVksSUFBSSxhQUFhLENBQUMsWUFBWTtTQUN6RSxDQUFDO0lBQ0osQ0FBQztJQUVPLGtCQUFrQjtRQUN4QixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUM3QyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMxQixPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNqQyxDQUFDO1FBQ0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztZQUN2QyxPQUFPLEdBQUcsR0FBRyxPQUFPLGVBQWUsQ0FBQztRQUN0QyxDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztDQUNGIn0=
@@ -0,0 +1,5 @@
1
+ export declare class PasswordHasher {
2
+ static hashPassword(passwordArg: string): Promise<string>;
3
+ static verifyPassword(passwordArg: string, passwordHashArg?: string): Promise<boolean>;
4
+ private static scrypt;
5
+ }
@@ -0,0 +1,36 @@
1
+ import * as plugins from './plugins.js';
2
+ const HASH_PREFIX = 'scrypt:v1';
3
+ export class PasswordHasher {
4
+ static async hashPassword(passwordArg) {
5
+ const salt = plugins.crypto.randomBytes(16).toString('base64url');
6
+ const key = await this.scrypt(passwordArg, salt);
7
+ return `${HASH_PREFIX}:${salt}:${key.toString('base64url')}`;
8
+ }
9
+ static async verifyPassword(passwordArg, passwordHashArg) {
10
+ if (!passwordHashArg) {
11
+ return false;
12
+ }
13
+ const [prefix, version, salt, storedKey] = passwordHashArg.split(':');
14
+ if (`${prefix}:${version}` !== HASH_PREFIX || !salt || !storedKey) {
15
+ return false;
16
+ }
17
+ const candidate = await this.scrypt(passwordArg, salt);
18
+ const stored = Buffer.from(storedKey, 'base64url');
19
+ if (candidate.byteLength !== stored.byteLength) {
20
+ return false;
21
+ }
22
+ return plugins.crypto.timingSafeEqual(candidate, stored);
23
+ }
24
+ static async scrypt(passwordArg, saltArg) {
25
+ return new Promise((resolve, reject) => {
26
+ plugins.crypto.scrypt(passwordArg, saltArg, 64, (error, derivedKey) => {
27
+ if (error) {
28
+ reject(error);
29
+ return;
30
+ }
31
+ resolve(derivedKey);
32
+ });
33
+ });
34
+ }
35
+ }
36
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5wYXNzd29yZC1oYXNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zZXJ2ZXIvY2xhc3Nlcy5wYXNzd29yZC1oYXNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFFeEMsTUFBTSxXQUFXLEdBQUcsV0FBVyxDQUFDO0FBRWhDLE1BQU0sT0FBTyxjQUFjO0lBQ2xCLE1BQU0sQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLFdBQW1CO1FBQ2xELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsRSxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ2pELE9BQU8sR0FBRyxXQUFXLElBQUksSUFBSSxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztJQUMvRCxDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsV0FBbUIsRUFBRSxlQUF3QjtRQUM5RSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDckIsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsTUFBTSxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsQ0FBQyxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdEUsSUFBSSxHQUFHLE1BQU0sSUFBSSxPQUFPLEVBQUUsS0FBSyxXQUFXLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNsRSxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFDRCxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQ3ZELE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQ25ELElBQUksU0FBUyxDQUFDLFVBQVUsS0FBSyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDL0MsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDM0QsQ0FBQztJQUVPLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFdBQW1CLEVBQUUsT0FBZTtRQUM5RCxPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFO1lBQ3JDLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFdBQVcsRUFBRSxPQUFPLEVBQUUsRUFBRSxFQUFFLENBQUMsS0FBSyxFQUFFLFVBQVUsRUFBRSxFQUFFO2dCQUNwRSxJQUFJLEtBQUssRUFBRSxDQUFDO29CQUNWLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztvQkFDZCxPQUFPO2dCQUNULENBQUM7Z0JBQ0QsT0FBTyxDQUFDLFVBQW9CLENBQUMsQ0FBQztZQUNoQyxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztDQUNGIn0=
@@ -0,0 +1,6 @@
1
+ export * from './interfaces.js';
2
+ export * from './classes.account-auth-service.js';
3
+ export * from './classes.account-doc.js';
4
+ export * from './classes.account-store.js';
5
+ export * from './classes.idp-global-server-client.js';
6
+ export * from './classes.password-hasher.js';
@@ -0,0 +1,7 @@
1
+ export * from './interfaces.js';
2
+ export * from './classes.account-auth-service.js';
3
+ export * from './classes.account-doc.js';
4
+ export * from './classes.account-store.js';
5
+ export * from './classes.idp-global-server-client.js';
6
+ export * from './classes.password-hasher.js';
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19zZXJ2ZXIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxpQkFBaUIsQ0FBQztBQUNoQyxjQUFjLG1DQUFtQyxDQUFDO0FBQ2xELGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyw0QkFBNEIsQ0FBQztBQUMzQyxjQUFjLHVDQUF1QyxDQUFDO0FBQ3RELGNBQWMsOEJBQThCLENBQUMifQ==
@@ -0,0 +1,37 @@
1
+ export type TIdpAccountAuthSource = 'local' | 'idp.global';
2
+ export type TIdpAccountRole = 'admin' | 'user';
3
+ export type TIdpAccountStatus = 'active' | 'disabled';
4
+ export interface IIdpSdkAccount {
5
+ id: string;
6
+ email: string;
7
+ emailNormalized: string;
8
+ name: string;
9
+ role: TIdpAccountRole;
10
+ status: TIdpAccountStatus;
11
+ authSources: TIdpAccountAuthSource[];
12
+ passwordHash?: string;
13
+ idpSubject?: string;
14
+ createdAt: number;
15
+ updatedAt: number;
16
+ lastLoginAt?: number;
17
+ }
18
+ export interface ICreateIdpSdkAccountOptions {
19
+ email: string;
20
+ name: string;
21
+ role: TIdpAccountRole;
22
+ status?: TIdpAccountStatus;
23
+ authSources: TIdpAccountAuthSource[];
24
+ password?: string;
25
+ idpSubject?: string;
26
+ }
27
+ export interface IAuthenticateAccountOptions {
28
+ email: string;
29
+ password: string;
30
+ authSource?: TIdpAccountAuthSource | 'auto';
31
+ }
32
+ export interface IAuthenticatedAccountResult {
33
+ account: IIdpSdkAccount;
34
+ authSource: TIdpAccountAuthSource;
35
+ idpJwt?: string;
36
+ idpRefreshToken?: string;
37
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3NlcnZlci9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
@@ -0,0 +1,10 @@
1
+ import * as crypto from 'crypto';
2
+ export { crypto };
3
+ import * as idpInterfaces from '@idp.global/interfaces';
4
+ export { idpInterfaces };
5
+ import * as typedrequest from '@api.global/typedrequest';
6
+ import * as typedsocket from '@api.global/typedsocket';
7
+ export { typedrequest, typedsocket };
8
+ import * as smartdata from '@push.rocks/smartdata';
9
+ import * as smartpromise from '@push.rocks/smartpromise';
10
+ export { smartdata, smartpromise };
@@ -0,0 +1,11 @@
1
+ import * as crypto from 'crypto';
2
+ export { crypto };
3
+ import * as idpInterfaces from '@idp.global/interfaces';
4
+ export { idpInterfaces };
5
+ import * as typedrequest from '@api.global/typedrequest';
6
+ import * as typedsocket from '@api.global/typedsocket';
7
+ export { typedrequest, typedsocket };
8
+ import * as smartdata from '@push.rocks/smartdata';
9
+ import * as smartpromise from '@push.rocks/smartpromise';
10
+ export { smartdata, smartpromise };
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3NlcnZlci9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBRWpDLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQztBQUVsQixPQUFPLEtBQUssYUFBYSxNQUFNLHdCQUF3QixDQUFDO0FBRXhELE9BQU8sRUFBRSxhQUFhLEVBQUUsQ0FBQztBQUV6QixPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUVyQyxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFFekQsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsQ0FBQyJ9
package/license ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Task Venture Capital GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@idp.global/sdk",
3
+ "version": "1.2.0",
4
+ "private": false,
5
+ "description": "Reusable browser and server SDK for idp.global authentication.",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./dist_ts/index.js",
9
+ "./browser": "./dist_ts_browser/index.js",
10
+ "./server": "./dist_ts_server/index.js"
11
+ },
12
+ "main": "dist_ts/index.js",
13
+ "typings": "dist_ts/index.d.ts",
14
+ "author": "Task Venture Capital GmbH",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@api.global/typedrequest": "^3.3.0",
18
+ "@api.global/typedsocket": "^4.1.2",
19
+ "@idp.global/interfaces": "file:../interfaces",
20
+ "@push.rocks/smartdata": "^7.1.7",
21
+ "@push.rocks/smartjson": "^6.0.1",
22
+ "@push.rocks/smartpromise": "^4.2.4",
23
+ "@push.rocks/smartrx": "^3.0.10",
24
+ "@push.rocks/smarttime": "^4.2.3",
25
+ "@push.rocks/smarturl": "^3.1.0",
26
+ "@push.rocks/webjwt": "^1.0.10",
27
+ "@push.rocks/webstore": "^2.0.22"
28
+ },
29
+ "devDependencies": {
30
+ "@git.zone/tsbuild": "^4.4.0",
31
+ "@git.zone/tsdoc": "^2.0.3",
32
+ "@git.zone/tsrun": "^2.0.3",
33
+ "@git.zone/tstest": "^3.6.3",
34
+ "@push.rocks/smartdb": "^2.10.0",
35
+ "@types/node": "^25.6.0"
36
+ },
37
+ "files": [
38
+ "ts/**/*",
39
+ "ts_browser/**/*",
40
+ "ts_server/**/*",
41
+ "dist/**/*",
42
+ "dist_*/**/*",
43
+ "readme.md",
44
+ "changelog.md",
45
+ "license"
46
+ ],
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+ssh://git@code.foss.global:29419/idp.global/sdk.git"
50
+ },
51
+ "bugs": {
52
+ "url": "https://code.foss.global/idp.global/sdk/issues"
53
+ },
54
+ "homepage": "https://code.foss.global/idp.global/sdk#readme",
55
+ "keywords": [
56
+ "idp.global",
57
+ "sdk",
58
+ "authentication",
59
+ "oidc",
60
+ "smartdata"
61
+ ],
62
+ "browserslist": [
63
+ "last 1 Chrome versions"
64
+ ],
65
+ "scripts": {
66
+ "test": "pnpm run build && tstest test/ --verbose --logfile --timeout 60",
67
+ "build": "tsbuild tsfolders --web --allowimplicitany",
68
+ "buildDocs": "tsdoc"
69
+ }
70
+ }
package/readme.md ADDED
@@ -0,0 +1,10 @@
1
+ # @idp.global/sdk
2
+
3
+ Reusable SDK for idp.global browser clients and server-side account authentication.
4
+
5
+ Use explicit runtime exports:
6
+
7
+ - `@idp.global/sdk/browser`
8
+ - `@idp.global/sdk/server`
9
+
10
+ The server account store never creates accounts automatically. Consumers must explicitly create persisted accounts.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@idp.global/sdk',
6
+ version: '1.2.0',
7
+ description: 'Reusable browser and server SDK for idp.global authentication.'
8
+ }
package/ts/index.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,318 @@
1
+ import { IdpRequests } from './classes.idprequests.js';
2
+ import * as plugins from './plugins.js';
3
+
4
+ export class IdpClient {
5
+ private helpers = {
6
+ async extractDataFromJwtString(jwtString: string): Promise<plugins.idpInterfaces.data.IJwt> {
7
+ return plugins.webjwt.getDataFromJwtString(jwtString);
8
+ },
9
+ };
10
+
11
+ public appData: plugins.idpInterfaces.data.IAppLegacy;
12
+ public rolesReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
13
+ public organizationsReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
14
+
15
+ public parsedReceptionUrl: plugins.smarturl.Smarturl;
16
+
17
+ constructor(receptionBaseUrlArg: string, appDataArg?: plugins.idpInterfaces.data.IAppLegacy) {
18
+ if (receptionBaseUrlArg.endsWith('/')) {
19
+ receptionBaseUrlArg = receptionBaseUrlArg.slice(0, -1);
20
+ }
21
+ if (!receptionBaseUrlArg.endsWith('/typedrequest')) {
22
+ receptionBaseUrlArg = `${receptionBaseUrlArg}/typedrequest`;
23
+ }
24
+ this.parsedReceptionUrl = plugins.smarturl.Smarturl.createFromUrl(receptionBaseUrlArg);
25
+ if (!appDataArg) {
26
+ appDataArg = {
27
+ id: '',
28
+ appUrl: typeof window !== 'undefined' ? `https://${window.location.host}/` : '',
29
+ description: '',
30
+ logoUrl: '',
31
+ name: '',
32
+ };
33
+ }
34
+ this.appData = appDataArg;
35
+ }
36
+
37
+ public requests = new IdpRequests(this);
38
+
39
+ public checkWetherOnReceptionDomain() {
40
+ return plugins.smarturl.Smarturl.createFromUrl(window.location.href).hostname ===
41
+ this.parsedReceptionUrl.hostname;
42
+ }
43
+
44
+ public async getAppDataOnSsoDomain() {
45
+ if (!window.location.href.startsWith('https://sso.workspace.global/')) {
46
+ console.error('You are trying to access SSO appData on a non sso domain.');
47
+ return null;
48
+ }
49
+ const appDataString = plugins.smarturl.Smarturl.createFromUrl(window.location.href).searchParams
50
+ .appdata;
51
+ if (!appDataString) {
52
+ console.error('no appdata query arg detected');
53
+ return null;
54
+ }
55
+ return plugins.smartjson.parseBase64(appDataString);
56
+ }
57
+
58
+ public async setJwt(jwtStringArg: string) {
59
+ await this.storeJwt(jwtStringArg);
60
+ }
61
+
62
+ public async setRefreshToken(refreshTokenArg: string) {
63
+ await this.storeRefreshToken(refreshTokenArg);
64
+ }
65
+
66
+ public typedsocket!: plugins.typedsocket.TypedSocket;
67
+ public typedrouter: any = new plugins.typedrequest.TypedRouter();
68
+ public statusObservable = new plugins.smartrx.rxjs.Subject<plugins.idpInterfaces.data.TLoginStatus>();
69
+
70
+ public ssoStore = new plugins.webstore.WebStore({
71
+ storeName: 'idpglobalStore',
72
+ dbName: 'main',
73
+ });
74
+
75
+ public async storeJwt(jwtString: string) {
76
+ await this.ssoStore.set('idpJwt', jwtString);
77
+ }
78
+
79
+ public async storeRefreshToken(refreshToken: string) {
80
+ await this.ssoStore.set('idpRefreshToken', refreshToken);
81
+ }
82
+
83
+ public async getJwt(): Promise<string> {
84
+ return await this.ssoStore.get('idpJwt');
85
+ }
86
+
87
+ public async getRefreshToken(): Promise<string> {
88
+ return await this.ssoStore.get('idpRefreshToken');
89
+ }
90
+
91
+ public async getJwtData(): Promise<plugins.idpInterfaces.data.IJwt> {
92
+ return this.helpers.extractDataFromJwtString(await this.getJwt());
93
+ }
94
+
95
+ public async deleteJwt() {
96
+ await this.ssoStore.delete('idpJwt');
97
+ }
98
+
99
+ public async deleteRefreshToken() {
100
+ await this.ssoStore.delete('idpRefreshToken');
101
+ }
102
+
103
+ public async clearAuthState() {
104
+ await Promise.all([this.deleteJwt(), this.deleteRefreshToken()]);
105
+ }
106
+
107
+ public async performJwtHousekeeping() {
108
+ let jwt = await this.getJwt();
109
+ if (!jwt) {
110
+ return null;
111
+ }
112
+ const extractedJwt = await this.helpers.extractDataFromJwtString(jwt);
113
+ if (extractedJwt.data.refreshFrom < Date.now() && Date.now() < extractedJwt.data.validUntil) {
114
+ jwt = await this.refreshJwt();
115
+ } else if (Date.now() > extractedJwt.data.validUntil) {
116
+ await this.deleteJwt();
117
+ jwt = await this.refreshJwt();
118
+ }
119
+ return jwt;
120
+ }
121
+
122
+ public async refreshJwt(refreshTokenArg?: string): Promise<string | null> {
123
+ const refreshToken = refreshTokenArg || (await this.getRefreshToken());
124
+
125
+ if (!refreshToken) {
126
+ return null;
127
+ }
128
+
129
+ await this.typedsocketDeferred.promise;
130
+ const refreshJwtReq = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>('refreshJwt');
131
+ const response = await refreshJwtReq
132
+ .fire({ refreshToken })
133
+ .catch(async () => {
134
+ await this.clearAuthState();
135
+ return null;
136
+ });
137
+
138
+ if (!response?.jwt) {
139
+ await this.clearAuthState();
140
+ this.statusObservable.next(response?.status || 'loggedOut');
141
+ return null;
142
+ }
143
+
144
+ if (response.refreshToken) {
145
+ await this.storeRefreshToken(response.refreshToken);
146
+ }
147
+ await this.storeJwt(response.jwt);
148
+ this.statusObservable.next(response.status);
149
+ return response.jwt;
150
+ }
151
+
152
+ public async getTransferToken(appDataArg?: plugins.idpInterfaces.data.IAppLegacy): Promise<string | null> {
153
+ await this.performJwtHousekeeping();
154
+ const refreshToken = await this.getRefreshToken();
155
+ if (!refreshToken) {
156
+ return null;
157
+ }
158
+ await this.typedsocketDeferred.promise;
159
+ const getTransferToken = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>('exchangeRefreshTokenAndTransferToken');
160
+ const response = await getTransferToken.fire({
161
+ refreshToken,
162
+ appData: appDataArg || this.appData,
163
+ });
164
+ return response.transferToken;
165
+ }
166
+
167
+ public async getTransferTokenAndSwitchToLocation(newLocationArg: string): Promise<void> {
168
+ const transferToken = await this.getTransferToken();
169
+ if (!transferToken) {
170
+ alert('failed to get transfer token!');
171
+ }
172
+ const urlInstance = plugins.smarturl.Smarturl.createFromUrl(newLocationArg, {
173
+ searchParams: { transfertoken: transferToken },
174
+ });
175
+ window.location.href = urlInstance.toString();
176
+ }
177
+
178
+ public async processTransferToken(): Promise<boolean> {
179
+ const href = window.location.href;
180
+ const url = plugins.smarturl.Smarturl.createFromUrl(href);
181
+ const transferToken = url.searchParams['transfertoken'];
182
+ if (transferToken) {
183
+ await this.typedsocketDeferred.promise;
184
+ const getTransferToken = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>('exchangeRefreshTokenAndTransferToken');
185
+ const response = await getTransferToken.fire({
186
+ transferToken,
187
+ appData: this.appData,
188
+ });
189
+ if (response.refreshToken) {
190
+ await this.refreshJwt(response.refreshToken);
191
+ } else {
192
+ globalThis.alert?.('transfer token invalid');
193
+ return false;
194
+ }
195
+ return true;
196
+ }
197
+ return false;
198
+ }
199
+
200
+ public async checkJwtPresent() {
201
+ const jwt = await this.performJwtHousekeeping();
202
+ return !!jwt;
203
+ }
204
+
205
+ public async determineLoginStatus(requireLoginArg: boolean = false): Promise<boolean> {
206
+ const jwtPresent = await this.checkJwtPresent();
207
+ if (jwtPresent) {
208
+ const jwt = await this.performJwtHousekeeping();
209
+ return !!jwt;
210
+ }
211
+ const refreshToken = await this.getRefreshToken();
212
+ if (refreshToken) {
213
+ const jwt = await this.refreshJwt(refreshToken);
214
+ if (jwt) {
215
+ return true;
216
+ }
217
+ }
218
+ const transferTokenResult = await this.processTransferToken();
219
+ if (transferTokenResult) {
220
+ return true;
221
+ }
222
+ if (requireLoginArg) {
223
+ const urlInstance = plugins.smarturl.Smarturl.createFromUrl(
224
+ this.parsedReceptionUrl.clone().set('path', '/login').toString(),
225
+ {
226
+ searchParams: {
227
+ appdata: plugins.smartjson.stringifyBase64(this.appData),
228
+ },
229
+ },
230
+ );
231
+ if (!globalThis.location.href.startsWith(this.parsedReceptionUrl.toString())) {
232
+ globalThis.location.href = urlInstance.toString();
233
+ }
234
+ }
235
+ return false;
236
+ }
237
+
238
+ public async logout() {
239
+ const idpLogoutUrl = this.parsedReceptionUrl.clone().set('path', '/logout');
240
+ const refreshToken = await this.getRefreshToken();
241
+ if (!globalThis.location.href.startsWith(idpLogoutUrl.origin)) {
242
+ await this.clearAuthState();
243
+ globalThis.location.href = idpLogoutUrl.toString();
244
+ return;
245
+ }
246
+ if (!refreshToken) {
247
+ await this.clearAuthState();
248
+ window.location.href = this.parsedReceptionUrl.origin;
249
+ return;
250
+ }
251
+ await this.enableTypedSocket();
252
+ const logoutTr = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.ILogoutRequest>('logout');
253
+ await logoutTr.fire({ refreshToken });
254
+ await this.clearAuthState();
255
+ const appData = await this.getAppDataOnSsoDomain();
256
+ if (appData) {
257
+ window.location.href = appData.appUrl;
258
+ } else if (window.location.href.startsWith(idpLogoutUrl.origin)) {
259
+ window.location.href = this.parsedReceptionUrl.origin;
260
+ }
261
+ }
262
+
263
+ public typedsocketDeferred = plugins.smartpromise.defer<plugins.typedsocket.TypedSocket>();
264
+
265
+ public async enableTypedSocket() {
266
+ if (this.typedsocketDeferred.claimed) {
267
+ return this.typedsocketDeferred.promise;
268
+ }
269
+ this.typedsocketDeferred.claim();
270
+ this.typedsocket = await plugins.typedsocket.TypedSocket.createClient(
271
+ this.typedrouter,
272
+ this.parsedReceptionUrl.toString(),
273
+ );
274
+ this.typedsocketDeferred.resolve(this.typedsocket);
275
+ return this.typedsocketDeferred.promise;
276
+ }
277
+
278
+ public async stop() {
279
+ await this.typedsocket?.stop();
280
+ }
281
+
282
+ public async createOrganization(orgNameArg: string, orgSlugArg: string, modeArg: 'checkAvailability' | 'manifest') {
283
+ await this.typedsocketDeferred.promise;
284
+ const validateOrg = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateOrganization>('createOrganization');
285
+ return validateOrg.fire({
286
+ jwt: await this.getJwt(),
287
+ action: modeArg,
288
+ organizationName: orgNameArg,
289
+ organizationSlug: orgSlugArg,
290
+ userId: (await this.getJwtData()).id,
291
+ });
292
+ }
293
+
294
+ public async getRolesAndOrganizations() {
295
+ await this.typedsocketDeferred.promise;
296
+ const rolesAndOrganizationsForUserId = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetRolesAndOrganizationsForUserId>('getRolesAndOrganizationsForUserId');
297
+ return rolesAndOrganizationsForUserId.fire({
298
+ jwt: await this.getJwt(),
299
+ userId: (await this.getJwtData()).id,
300
+ });
301
+ }
302
+
303
+ public async updatePaddleCheckoutId(orgIdArg: string, checkoutIdArg: string) {
304
+ await this.typedsocketDeferred.promise;
305
+ const updateBillingPlan = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdatePaymentMethod>('updatePaymentMethod');
306
+ return updateBillingPlan.fire({
307
+ jwtString: await this.getJwt(),
308
+ orgId: orgIdArg,
309
+ paddle: { checkoutId: checkoutIdArg },
310
+ });
311
+ }
312
+
313
+ public async whoIs() {
314
+ await this.typedsocketDeferred.promise;
315
+ const whoIs = this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_WhoIs>('whoIs');
316
+ return whoIs.fire({ jwt: await this.getJwt() });
317
+ }
318
+ }