@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.
- package/changelog.md +25 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -0
- package/dist_ts_browser/classes.idpclient.d.ts +85 -0
- package/dist_ts_browser/classes.idpclient.js +278 -0
- package/dist_ts_browser/classes.idprequests.d.ts +59 -0
- package/dist_ts_browser/classes.idprequests.js +167 -0
- package/dist_ts_browser/index.d.ts +2 -0
- package/dist_ts_browser/index.js +3 -0
- package/dist_ts_browser/plugins.d.ts +13 -0
- package/dist_ts_browser/plugins.js +14 -0
- package/dist_ts_server/classes.account-auth-service.d.ts +12 -0
- package/dist_ts_server/classes.account-auth-service.js +55 -0
- package/dist_ts_server/classes.account-doc.d.ts +21 -0
- package/dist_ts_server/classes.account-doc.js +166 -0
- package/dist_ts_server/classes.account-store.d.ts +19 -0
- package/dist_ts_server/classes.account-store.js +84 -0
- package/dist_ts_server/classes.idp-global-server-client.d.ts +23 -0
- package/dist_ts_server/classes.idp-global-server-client.js +57 -0
- package/dist_ts_server/classes.password-hasher.d.ts +5 -0
- package/dist_ts_server/classes.password-hasher.js +36 -0
- package/dist_ts_server/index.d.ts +6 -0
- package/dist_ts_server/index.js +7 -0
- package/dist_ts_server/interfaces.d.ts +37 -0
- package/dist_ts_server/interfaces.js +2 -0
- package/dist_ts_server/plugins.d.ts +10 -0
- package/dist_ts_server/plugins.js +11 -0
- package/license +21 -0
- package/package.json +70 -0
- package/readme.md +10 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +1 -0
- package/ts_browser/classes.idpclient.ts +318 -0
- package/ts_browser/classes.idprequests.ts +218 -0
- package/ts_browser/index.ts +2 -0
- package/ts_browser/plugins.ts +18 -0
- package/ts_server/classes.account-auth-service.ts +61 -0
- package/ts_server/classes.account-doc.ts +84 -0
- package/ts_server/classes.account-store.ts +94 -0
- package/ts_server/classes.idp-global-server-client.ts +74 -0
- package/ts_server/classes.password-hasher.ts +39 -0
- package/ts_server/index.ts +6 -0
- package/ts_server/interfaces.ts +41 -0
- package/ts_server/plugins.ts +17 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IdpClient } from './classes.idpclient.js';
|
|
3
|
+
|
|
4
|
+
export class IdpRequests {
|
|
5
|
+
constructor(private idpClientArg: IdpClient) {}
|
|
6
|
+
|
|
7
|
+
public get afterRegistrationEmailClicked() {
|
|
8
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AfterRegistrationEmailClicked>('afterRegistrationEmailClicked');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public get setData() {
|
|
12
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetDataForRegistration>('setDataForRegistration');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public get mobileNumberVerification() {
|
|
16
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_MobileVerificationForRegistration>('mobileVerificationForRegistration');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public get finishRegistration() {
|
|
20
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_FinishRegistration>('finishRegistration');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public get loginWithUserNameAndPassword() {
|
|
24
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>('loginWithEmailOrUsernameAndPassword');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public get obtainJwt() {
|
|
28
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>('refreshJwt');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public get obtainOneTimeToken() {
|
|
32
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>('exchangeRefreshTokenAndTransferToken');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public get loginWithEmail() {
|
|
36
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmail>('loginWithEmail');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public get loginWithEmailAfterToken() {
|
|
40
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailAfterEmailTokenAquired>('loginWithEmailAfterEmailTokenAquired');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public get loginWithApiToken() {
|
|
44
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithApiToken>('loginWithApiToken');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public get completeOidcAuthorization() {
|
|
48
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CompleteOidcAuthorization>('completeOidcAuthorization');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public get prepareOidcAuthorization() {
|
|
52
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PrepareOidcAuthorization>('prepareOidcAuthorization');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public get resetPassword() {
|
|
56
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ResetPassword>('resetPassword');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public get setNewPassword() {
|
|
60
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetNewPassword>('setNewPassword');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public get obtainDeviceId() {
|
|
64
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ObtainDeviceId>('obtainDeviceId');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public get attachDeviceId() {
|
|
68
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AttachDeviceId>('attachDeviceId');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public get firstRegistration() {
|
|
72
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_FirstRegistration>('firstRegistrationRequest');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public get getUserData() {
|
|
76
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserData>('getUserData');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public get setUserData() {
|
|
80
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetUserData>('setUserData');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public get getUserSessions() {
|
|
84
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserSessions>('getUserSessions');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public get revokeSession() {
|
|
88
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RevokeSession>('revokeSession');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public get getUserActivity() {
|
|
92
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserActivity>('getUserActivity');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public get getOrganizationById() {
|
|
96
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrganizationById>('getOrganizationById');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public get updateOrganization() {
|
|
100
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateOrganization>('updateOrganization');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public get deleteOrganization() {
|
|
104
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrganization>('deleteOrganization');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public get getOrgRoleDefinitions() {
|
|
108
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgRoleDefinitions>('getOrgRoleDefinitions');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public get upsertOrgRoleDefinition() {
|
|
112
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpsertOrgRoleDefinition>('upsertOrgRoleDefinition');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public get deleteOrgRoleDefinition() {
|
|
116
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrgRoleDefinition>('deleteOrgRoleDefinition');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public get createInvitation() {
|
|
120
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateInvitation>('createInvitation');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public get getOrgInvitations() {
|
|
124
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgInvitations>('getOrgInvitations');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public get getOrgMembers() {
|
|
128
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgMembers>('getOrgMembers');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public get cancelInvitation() {
|
|
132
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CancelInvitation>('cancelInvitation');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public get resendInvitation() {
|
|
136
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ResendInvitation>('resendInvitation');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public get removeMember() {
|
|
140
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RemoveMember>('removeMember');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public get updateMemberRoles() {
|
|
144
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateMemberRoles>('updateMemberRoles');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public get transferOwnership() {
|
|
148
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_TransferOwnership>('transferOwnership');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public get getInvitationByToken() {
|
|
152
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetInvitationByToken>('getInvitationByToken');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public get acceptInvitation() {
|
|
156
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AcceptInvitation>('acceptInvitation');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public get bulkCreateInvitations() {
|
|
160
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_BulkCreateInvitations>('bulkCreateInvitations');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public get updateAppRoleMappings() {
|
|
164
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateAppRoleMappings>('updateAppRoleMappings');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public get getBillingPlan() {
|
|
168
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetBillingPlan>('getBillingPlan');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public get getPaddleConfig() {
|
|
172
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPaddleConfig>('getPaddleConfig');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public get getPublicKeyForValidation() {
|
|
176
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPublicKeyForValidation>('getPublicKeyForValidation');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public get pushPublicKeyForValidation() {
|
|
180
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushPublicKeyForValidation>('pushPublicKeyForValidation');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public get pushOrGetJwtIdBlocklist() {
|
|
184
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushOrGetJwtIdBlocklist>('pushOrGetJwtIdBlocklist');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public get suspendUser() {
|
|
188
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SuspendUser>('suspendUser');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public get deleteSuspendedUser() {
|
|
192
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IDeleteSuspendedUser>('deleteSuspendedUser');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
public get checkGlobalAdmin() {
|
|
196
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CheckGlobalAdmin>('checkGlobalAdmin');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public get getGlobalAppStats() {
|
|
200
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetGlobalAppStats>('getGlobalAppStats');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public get createGlobalApp() {
|
|
204
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateGlobalApp>('createGlobalApp');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public get updateGlobalApp() {
|
|
208
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateGlobalApp>('updateGlobalApp');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public get deleteGlobalApp() {
|
|
212
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteGlobalApp>('deleteGlobalApp');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public get regenerateAppCredentials() {
|
|
216
|
+
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RegenerateAppCredentials>('regenerateAppCredentials');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as idpInterfaces from '@idp.global/interfaces';
|
|
2
|
+
|
|
3
|
+
export { idpInterfaces };
|
|
4
|
+
|
|
5
|
+
import * as typedrequest from '@api.global/typedrequest';
|
|
6
|
+
import * as typedsocket from '@api.global/typedsocket';
|
|
7
|
+
|
|
8
|
+
export { typedrequest, typedsocket };
|
|
9
|
+
|
|
10
|
+
import * as smartjson from '@push.rocks/smartjson';
|
|
11
|
+
import * as smartpromise from '@push.rocks/smartpromise';
|
|
12
|
+
import * as smartrx from '@push.rocks/smartrx';
|
|
13
|
+
import * as smarttime from '@push.rocks/smarttime';
|
|
14
|
+
import * as smarturl from '@push.rocks/smarturl';
|
|
15
|
+
import * as webjwt from '@push.rocks/webjwt';
|
|
16
|
+
import * as webstore from '@push.rocks/webstore';
|
|
17
|
+
|
|
18
|
+
export { smartjson, smartpromise, smartrx, smarttime, smarturl, webjwt, webstore };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { IdpGlobalServerClient } from './classes.idp-global-server-client.js';
|
|
2
|
+
import { SmartdataAccountStore } from './classes.account-store.js';
|
|
3
|
+
import type { IAuthenticateAccountOptions, IAuthenticatedAccountResult, IIdpSdkAccount } from './interfaces.js';
|
|
4
|
+
|
|
5
|
+
export class AccountAuthService {
|
|
6
|
+
constructor(private optionsArg: {
|
|
7
|
+
store: SmartdataAccountStore;
|
|
8
|
+
idpClient?: IdpGlobalServerClient;
|
|
9
|
+
}) {}
|
|
10
|
+
|
|
11
|
+
public async authenticate(optionsArg: IAuthenticateAccountOptions): Promise<IAuthenticatedAccountResult | null> {
|
|
12
|
+
const account = await this.optionsArg.store.getAccountByEmail(optionsArg.email);
|
|
13
|
+
if (!account || account.status !== 'active') {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const authSource = optionsArg.authSource || 'auto';
|
|
18
|
+
if ((authSource === 'local' || authSource === 'auto') && account.authSources.includes('local')) {
|
|
19
|
+
const localOk = await this.optionsArg.store.verifyLocalPassword(account, optionsArg.password);
|
|
20
|
+
if (localOk) {
|
|
21
|
+
const updatedAccount = await this.optionsArg.store.updateLoginState(account.id, {});
|
|
22
|
+
return { account: updatedAccount || account, authSource: 'local' };
|
|
23
|
+
}
|
|
24
|
+
if (authSource === 'local') {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ((authSource === 'idp.global' || authSource === 'auto') && account.authSources.includes('idp.global')) {
|
|
30
|
+
return this.authenticateWithIdp(account, optionsArg.password);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async authenticateWithIdp(accountArg: IIdpSdkAccount, passwordArg: string): Promise<IAuthenticatedAccountResult | null> {
|
|
37
|
+
if (!this.optionsArg.idpClient) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const idpResult = await this.optionsArg.idpClient.loginWithEmailAndPassword({
|
|
41
|
+
email: accountArg.email,
|
|
42
|
+
password: passwordArg,
|
|
43
|
+
});
|
|
44
|
+
const idpEmail = this.optionsArg.store.normalizeEmail(idpResult.user.data.email);
|
|
45
|
+
if (idpEmail !== accountArg.emailNormalized) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (accountArg.idpSubject && accountArg.idpSubject !== idpResult.user.id) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const updatedAccount = await this.optionsArg.store.updateLoginState(accountArg.id, {
|
|
52
|
+
idpSubject: accountArg.idpSubject || idpResult.user.id,
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
account: updatedAccount || accountArg,
|
|
56
|
+
authSource: 'idp.global',
|
|
57
|
+
idpJwt: idpResult.jwt,
|
|
58
|
+
idpRefreshToken: idpResult.refreshToken,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IIdpSdkAccount, TIdpAccountAuthSource, TIdpAccountRole, TIdpAccountStatus } from './interfaces.js';
|
|
3
|
+
|
|
4
|
+
let activeSmartdataDb: plugins.smartdata.SmartdataDb | null = null;
|
|
5
|
+
|
|
6
|
+
export const setAccountDocSmartdataDb = (smartdataDbArg: plugins.smartdata.SmartdataDb) => {
|
|
7
|
+
activeSmartdataDb = smartdataDbArg;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const getDb = () => {
|
|
11
|
+
if (!activeSmartdataDb) {
|
|
12
|
+
throw new Error('IdpSdkAccountDoc has no SmartdataDb configured');
|
|
13
|
+
}
|
|
14
|
+
return activeSmartdataDb;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
@plugins.smartdata.Collection(() => getDb())
|
|
18
|
+
export class IdpSdkAccountDoc extends plugins.smartdata.SmartDataDbDoc<IdpSdkAccountDoc, IdpSdkAccountDoc> {
|
|
19
|
+
@plugins.smartdata.unI()
|
|
20
|
+
@plugins.smartdata.svDb()
|
|
21
|
+
public id!: string;
|
|
22
|
+
|
|
23
|
+
@plugins.smartdata.svDb()
|
|
24
|
+
public email!: string;
|
|
25
|
+
|
|
26
|
+
@plugins.smartdata.svDb()
|
|
27
|
+
public emailNormalized!: string;
|
|
28
|
+
|
|
29
|
+
@plugins.smartdata.svDb()
|
|
30
|
+
public name: string = '';
|
|
31
|
+
|
|
32
|
+
@plugins.smartdata.svDb()
|
|
33
|
+
public role!: TIdpAccountRole;
|
|
34
|
+
|
|
35
|
+
@plugins.smartdata.svDb()
|
|
36
|
+
public status!: TIdpAccountStatus;
|
|
37
|
+
|
|
38
|
+
@plugins.smartdata.svDb()
|
|
39
|
+
public authSources!: TIdpAccountAuthSource[];
|
|
40
|
+
|
|
41
|
+
@plugins.smartdata.svDb()
|
|
42
|
+
public passwordHash?: string;
|
|
43
|
+
|
|
44
|
+
@plugins.smartdata.svDb()
|
|
45
|
+
public idpSubject?: string;
|
|
46
|
+
|
|
47
|
+
@plugins.smartdata.svDb()
|
|
48
|
+
public createdAt!: number;
|
|
49
|
+
|
|
50
|
+
@plugins.smartdata.svDb()
|
|
51
|
+
public updatedAt!: number;
|
|
52
|
+
|
|
53
|
+
@plugins.smartdata.svDb()
|
|
54
|
+
public lastLoginAt?: number;
|
|
55
|
+
|
|
56
|
+
public toAccount(): IIdpSdkAccount {
|
|
57
|
+
return {
|
|
58
|
+
id: this.id,
|
|
59
|
+
email: this.email,
|
|
60
|
+
emailNormalized: this.emailNormalized,
|
|
61
|
+
name: this.name,
|
|
62
|
+
role: this.role,
|
|
63
|
+
status: this.status,
|
|
64
|
+
authSources: this.authSources || [],
|
|
65
|
+
passwordHash: this.passwordHash,
|
|
66
|
+
idpSubject: this.idpSubject,
|
|
67
|
+
createdAt: this.createdAt,
|
|
68
|
+
updatedAt: this.updatedAt,
|
|
69
|
+
lastLoginAt: this.lastLoginAt,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static async findById(idArg: string): Promise<IdpSdkAccountDoc | null> {
|
|
74
|
+
return IdpSdkAccountDoc.getInstance({ id: idArg });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public static async findByEmailNormalized(emailNormalizedArg: string): Promise<IdpSdkAccountDoc | null> {
|
|
78
|
+
return IdpSdkAccountDoc.getInstance({ emailNormalized: emailNormalizedArg });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public static async findAdmins(): Promise<IdpSdkAccountDoc[]> {
|
|
82
|
+
return IdpSdkAccountDoc.getInstances({ role: 'admin', status: 'active' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { IdpSdkAccountDoc, setAccountDocSmartdataDb } from './classes.account-doc.js';
|
|
3
|
+
import { PasswordHasher } from './classes.password-hasher.js';
|
|
4
|
+
import type { ICreateIdpSdkAccountOptions, IIdpSdkAccount, TIdpAccountAuthSource } from './interfaces.js';
|
|
5
|
+
|
|
6
|
+
export class SmartdataAccountStore {
|
|
7
|
+
constructor(private optionsArg: { smartdataDb: plugins.smartdata.SmartdataDb }) {
|
|
8
|
+
setAccountDocSmartdataDb(optionsArg.smartdataDb);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public normalizeEmail(emailArg: string): string {
|
|
12
|
+
return emailArg.trim().toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async createAccount(optionsArg: ICreateIdpSdkAccountOptions): Promise<IIdpSdkAccount> {
|
|
16
|
+
const emailNormalized = this.normalizeEmail(optionsArg.email);
|
|
17
|
+
if (!emailNormalized || !emailNormalized.includes('@')) {
|
|
18
|
+
throw new Error('A valid account email is required');
|
|
19
|
+
}
|
|
20
|
+
const existing = await IdpSdkAccountDoc.findByEmailNormalized(emailNormalized);
|
|
21
|
+
if (existing) {
|
|
22
|
+
throw new Error(`Account already exists for ${emailNormalized}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const authSources = this.normalizeAuthSources(optionsArg.authSources);
|
|
26
|
+
if (authSources.length === 0) {
|
|
27
|
+
throw new Error('At least one auth source is required');
|
|
28
|
+
}
|
|
29
|
+
if (authSources.includes('local') && !optionsArg.password) {
|
|
30
|
+
throw new Error('A local password is required for local auth');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
const doc = new IdpSdkAccountDoc();
|
|
35
|
+
doc.id = plugins.crypto.randomUUID();
|
|
36
|
+
doc.email = optionsArg.email.trim();
|
|
37
|
+
doc.emailNormalized = emailNormalized;
|
|
38
|
+
doc.name = optionsArg.name.trim() || doc.email;
|
|
39
|
+
doc.role = optionsArg.role;
|
|
40
|
+
doc.status = optionsArg.status || 'active';
|
|
41
|
+
doc.authSources = authSources;
|
|
42
|
+
doc.passwordHash = optionsArg.password ? await PasswordHasher.hashPassword(optionsArg.password) : undefined;
|
|
43
|
+
doc.idpSubject = optionsArg.idpSubject;
|
|
44
|
+
doc.createdAt = now;
|
|
45
|
+
doc.updatedAt = now;
|
|
46
|
+
await doc.save();
|
|
47
|
+
return doc.toAccount();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public async getAccountByEmail(emailArg: string): Promise<IIdpSdkAccount | null> {
|
|
51
|
+
const doc = await IdpSdkAccountDoc.findByEmailNormalized(this.normalizeEmail(emailArg));
|
|
52
|
+
return doc?.toAccount() || null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async getAccountById(idArg: string): Promise<IIdpSdkAccount | null> {
|
|
56
|
+
const doc = await IdpSdkAccountDoc.findById(idArg);
|
|
57
|
+
return doc?.toAccount() || null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async listAccounts(): Promise<IIdpSdkAccount[]> {
|
|
61
|
+
const docs = await IdpSdkAccountDoc.getInstances({});
|
|
62
|
+
return docs.map((docArg) => docArg.toAccount());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async hasActiveAdminAccount(): Promise<boolean> {
|
|
66
|
+
const admins = await IdpSdkAccountDoc.findAdmins();
|
|
67
|
+
return admins.length > 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public async verifyLocalPassword(accountArg: IIdpSdkAccount, passwordArg: string): Promise<boolean> {
|
|
71
|
+
if (accountArg.status !== 'active' || !accountArg.authSources.includes('local')) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return PasswordHasher.verifyPassword(passwordArg, accountArg.passwordHash);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public async updateLoginState(accountIdArg: string, patchArg: { idpSubject?: string }): Promise<IIdpSdkAccount | null> {
|
|
78
|
+
const doc = await IdpSdkAccountDoc.findById(accountIdArg);
|
|
79
|
+
if (!doc) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (patchArg.idpSubject !== undefined) {
|
|
83
|
+
doc.idpSubject = patchArg.idpSubject;
|
|
84
|
+
}
|
|
85
|
+
doc.lastLoginAt = Date.now();
|
|
86
|
+
doc.updatedAt = Date.now();
|
|
87
|
+
await doc.save();
|
|
88
|
+
return doc.toAccount();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private normalizeAuthSources(authSourcesArg: TIdpAccountAuthSource[]): TIdpAccountAuthSource[] {
|
|
92
|
+
return [...new Set(authSourcesArg.filter((sourceArg) => sourceArg === 'local' || sourceArg === 'idp.global'))];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
export interface IIdpGlobalServerClientOptions {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface IIdpPasswordAuthResult {
|
|
8
|
+
user: plugins.idpInterfaces.data.IUser;
|
|
9
|
+
jwt: string;
|
|
10
|
+
refreshToken: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class IdpGlobalServerClient {
|
|
14
|
+
private typedrouter: any = new plugins.typedrequest.TypedRouter();
|
|
15
|
+
private typedsocket?: plugins.typedsocket.TypedSocket;
|
|
16
|
+
private typedsocketDeferred = plugins.smartpromise.defer<plugins.typedsocket.TypedSocket>();
|
|
17
|
+
|
|
18
|
+
constructor(private optionsArg: IIdpGlobalServerClientOptions) {}
|
|
19
|
+
|
|
20
|
+
public async connect(): Promise<plugins.typedsocket.TypedSocket> {
|
|
21
|
+
if (this.typedsocketDeferred.claimed) {
|
|
22
|
+
return this.typedsocketDeferred.promise;
|
|
23
|
+
}
|
|
24
|
+
this.typedsocketDeferred.claim();
|
|
25
|
+
this.typedsocket = await plugins.typedsocket.TypedSocket.createClient(
|
|
26
|
+
this.typedrouter,
|
|
27
|
+
this.getTypedRequestUrl(),
|
|
28
|
+
);
|
|
29
|
+
this.typedsocketDeferred.resolve(this.typedsocket);
|
|
30
|
+
return this.typedsocketDeferred.promise;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async stop(): Promise<void> {
|
|
34
|
+
await this.typedsocket?.stop();
|
|
35
|
+
this.typedsocket = undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async loginWithEmailAndPassword(optionsArg: { email: string; password: string }): Promise<IIdpPasswordAuthResult> {
|
|
39
|
+
const socket = await this.connect();
|
|
40
|
+
const loginRequest = socket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>('loginWithEmailOrUsernameAndPassword');
|
|
41
|
+
const loginResponse = await loginRequest.fire({
|
|
42
|
+
username: optionsArg.email,
|
|
43
|
+
password: optionsArg.password,
|
|
44
|
+
});
|
|
45
|
+
if (!loginResponse.refreshToken || loginResponse.twoFaNeeded) {
|
|
46
|
+
throw new Error(loginResponse.twoFaNeeded ? 'Two-factor authentication is required' : 'IdP login failed');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const refreshRequest = socket.createTypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>('refreshJwt');
|
|
50
|
+
const refreshResponse = await refreshRequest.fire({ refreshToken: loginResponse.refreshToken });
|
|
51
|
+
if (!refreshResponse.jwt) {
|
|
52
|
+
throw new Error('IdP did not return a JWT');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const whoIsRequest = socket.createTypedRequest<plugins.idpInterfaces.request.IReq_WhoIs>('whoIs');
|
|
56
|
+
const whoIsResponse = await whoIsRequest.fire({ jwt: refreshResponse.jwt });
|
|
57
|
+
return {
|
|
58
|
+
user: whoIsResponse.user,
|
|
59
|
+
jwt: refreshResponse.jwt,
|
|
60
|
+
refreshToken: refreshResponse.refreshToken || loginResponse.refreshToken,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private getTypedRequestUrl(): string {
|
|
65
|
+
let baseUrl = this.optionsArg.baseUrl.trim();
|
|
66
|
+
if (baseUrl.endsWith('/')) {
|
|
67
|
+
baseUrl = baseUrl.slice(0, -1);
|
|
68
|
+
}
|
|
69
|
+
if (!baseUrl.endsWith('/typedrequest')) {
|
|
70
|
+
baseUrl = `${baseUrl}/typedrequest`;
|
|
71
|
+
}
|
|
72
|
+
return baseUrl;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
|
|
3
|
+
const HASH_PREFIX = 'scrypt:v1';
|
|
4
|
+
|
|
5
|
+
export class PasswordHasher {
|
|
6
|
+
public static async hashPassword(passwordArg: string): Promise<string> {
|
|
7
|
+
const salt = plugins.crypto.randomBytes(16).toString('base64url');
|
|
8
|
+
const key = await this.scrypt(passwordArg, salt);
|
|
9
|
+
return `${HASH_PREFIX}:${salt}:${key.toString('base64url')}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public static async verifyPassword(passwordArg: string, passwordHashArg?: string): Promise<boolean> {
|
|
13
|
+
if (!passwordHashArg) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const [prefix, version, salt, storedKey] = passwordHashArg.split(':');
|
|
17
|
+
if (`${prefix}:${version}` !== HASH_PREFIX || !salt || !storedKey) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const candidate = await this.scrypt(passwordArg, salt);
|
|
21
|
+
const stored = Buffer.from(storedKey, 'base64url');
|
|
22
|
+
if (candidate.byteLength !== stored.byteLength) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return plugins.crypto.timingSafeEqual(candidate, stored);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private static async scrypt(passwordArg: string, saltArg: string): Promise<Buffer> {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
plugins.crypto.scrypt(passwordArg, saltArg, 64, (error, derivedKey) => {
|
|
31
|
+
if (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
resolve(derivedKey as Buffer);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -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,41 @@
|
|
|
1
|
+
export type TIdpAccountAuthSource = 'local' | 'idp.global';
|
|
2
|
+
export type TIdpAccountRole = 'admin' | 'user';
|
|
3
|
+
export type TIdpAccountStatus = 'active' | 'disabled';
|
|
4
|
+
|
|
5
|
+
export interface IIdpSdkAccount {
|
|
6
|
+
id: string;
|
|
7
|
+
email: string;
|
|
8
|
+
emailNormalized: string;
|
|
9
|
+
name: string;
|
|
10
|
+
role: TIdpAccountRole;
|
|
11
|
+
status: TIdpAccountStatus;
|
|
12
|
+
authSources: TIdpAccountAuthSource[];
|
|
13
|
+
passwordHash?: string;
|
|
14
|
+
idpSubject?: string;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
updatedAt: number;
|
|
17
|
+
lastLoginAt?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ICreateIdpSdkAccountOptions {
|
|
21
|
+
email: string;
|
|
22
|
+
name: string;
|
|
23
|
+
role: TIdpAccountRole;
|
|
24
|
+
status?: TIdpAccountStatus;
|
|
25
|
+
authSources: TIdpAccountAuthSource[];
|
|
26
|
+
password?: string;
|
|
27
|
+
idpSubject?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IAuthenticateAccountOptions {
|
|
31
|
+
email: string;
|
|
32
|
+
password: string;
|
|
33
|
+
authSource?: TIdpAccountAuthSource | 'auto';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IAuthenticatedAccountResult {
|
|
37
|
+
account: IIdpSdkAccount;
|
|
38
|
+
authSource: TIdpAccountAuthSource;
|
|
39
|
+
idpJwt?: string;
|
|
40
|
+
idpRefreshToken?: string;
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
export { crypto };
|
|
4
|
+
|
|
5
|
+
import * as idpInterfaces from '@idp.global/interfaces';
|
|
6
|
+
|
|
7
|
+
export { idpInterfaces };
|
|
8
|
+
|
|
9
|
+
import * as typedrequest from '@api.global/typedrequest';
|
|
10
|
+
import * as typedsocket from '@api.global/typedsocket';
|
|
11
|
+
|
|
12
|
+
export { typedrequest, typedsocket };
|
|
13
|
+
|
|
14
|
+
import * as smartdata from '@push.rocks/smartdata';
|
|
15
|
+
import * as smartpromise from '@push.rocks/smartpromise';
|
|
16
|
+
|
|
17
|
+
export { smartdata, smartpromise };
|