@progalaxyelabs/ngx-stonescriptphp-client 1.14.0 → 1.16.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.
|
@@ -140,19 +140,36 @@ class StoneScriptPHPAuth {
|
|
|
140
140
|
const raw = this.resolvePath(data, this.responseMap.userPath);
|
|
141
141
|
return raw ? this.normalizeUser(raw) : undefined;
|
|
142
142
|
}
|
|
143
|
+
resolveMembership(data) {
|
|
144
|
+
const raw = this.resolvePath(data, 'data.membership');
|
|
145
|
+
return raw ? raw : undefined;
|
|
146
|
+
}
|
|
143
147
|
resolveErrorMessage(data, fallback) {
|
|
144
148
|
const path = this.responseMap.errorMessagePath ?? 'message';
|
|
145
149
|
return this.resolvePath(data, path) || fallback;
|
|
146
150
|
}
|
|
147
151
|
normalizeUser(raw) {
|
|
148
|
-
|
|
149
|
-
user_id: raw.user_id ?? (raw.id ? this.hashUUID(raw.id) : 0),
|
|
150
|
-
id: raw.id ?? String(raw.user_id),
|
|
152
|
+
const user = {
|
|
151
153
|
email: raw.email,
|
|
152
154
|
display_name: raw.display_name ?? raw.email?.split('@')[0] ?? '',
|
|
153
155
|
photo_url: raw.photo_url,
|
|
154
156
|
is_email_verified: raw.is_email_verified ?? false
|
|
155
157
|
};
|
|
158
|
+
// id and user_id are optional after auth response cleanup (task #1552)
|
|
159
|
+
// Only populate them if the raw data contains them
|
|
160
|
+
if (raw.id) {
|
|
161
|
+
user.id = raw.id;
|
|
162
|
+
}
|
|
163
|
+
else if (raw.user_id) {
|
|
164
|
+
user.id = String(raw.user_id);
|
|
165
|
+
}
|
|
166
|
+
if (raw.user_id) {
|
|
167
|
+
user.user_id = raw.user_id;
|
|
168
|
+
}
|
|
169
|
+
else if (raw.id) {
|
|
170
|
+
user.user_id = this.hashUUID(raw.id);
|
|
171
|
+
}
|
|
172
|
+
return user;
|
|
156
173
|
}
|
|
157
174
|
hashUUID(uuid) {
|
|
158
175
|
let hash = 0;
|
|
@@ -249,7 +266,8 @@ class StoneScriptPHPAuth {
|
|
|
249
266
|
success: true,
|
|
250
267
|
accessToken: this.resolveAccessToken(data),
|
|
251
268
|
refreshToken: this.resolveRefreshToken(data),
|
|
252
|
-
user: this.resolveUser(data)
|
|
269
|
+
user: this.resolveUser(data),
|
|
270
|
+
membership: this.resolveMembership(data)
|
|
253
271
|
};
|
|
254
272
|
}
|
|
255
273
|
return { success: false, message: this.resolveErrorMessage(data, 'Invalid credentials') };
|
|
@@ -399,22 +417,48 @@ class StoneScriptPHPAuth {
|
|
|
399
417
|
resolve({ success: false, message: 'Popup blocked. Please allow popups for this site.' });
|
|
400
418
|
return;
|
|
401
419
|
}
|
|
420
|
+
const cleanup = () => {
|
|
421
|
+
window.removeEventListener('message', messageHandler);
|
|
422
|
+
clearInterval(checkClosed);
|
|
423
|
+
if (popup && !popup.closed)
|
|
424
|
+
popup.close();
|
|
425
|
+
};
|
|
402
426
|
const messageHandler = (event) => {
|
|
403
427
|
if (event.origin !== new URL(accountsUrl).origin)
|
|
404
428
|
return;
|
|
405
|
-
if (event.data.type === '
|
|
406
|
-
|
|
407
|
-
|
|
429
|
+
if (event.data.type === 'oauth_new_identity') {
|
|
430
|
+
cleanup();
|
|
431
|
+
resolve({
|
|
432
|
+
success: true,
|
|
433
|
+
accessToken: event.data.access_token,
|
|
434
|
+
refreshToken: event.data.refresh_token,
|
|
435
|
+
isNewIdentity: true,
|
|
436
|
+
authMethod: event.data.auth_method,
|
|
437
|
+
oauthProvider: event.data.oauth_provider,
|
|
438
|
+
identity: event.data.identity,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
else if (event.data.type === 'oauth_success') {
|
|
442
|
+
cleanup();
|
|
408
443
|
const rawUser = event.data.user || this.resolveUser(event.data);
|
|
409
444
|
resolve({
|
|
410
445
|
success: true,
|
|
411
446
|
accessToken: event.data.access_token,
|
|
412
|
-
|
|
447
|
+
refreshToken: event.data.refresh_token,
|
|
448
|
+
user: rawUser ? this.normalizeUser(rawUser) : undefined,
|
|
449
|
+
membership: event.data.membership,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
else if (event.data.type === 'oauth_tenant_selection') {
|
|
453
|
+
cleanup();
|
|
454
|
+
resolve({
|
|
455
|
+
success: true,
|
|
456
|
+
accessToken: event.data.selection_token,
|
|
457
|
+
memberships: event.data.memberships,
|
|
413
458
|
});
|
|
414
459
|
}
|
|
415
460
|
else if (event.data.type === 'oauth_error') {
|
|
416
|
-
|
|
417
|
-
popup.close();
|
|
461
|
+
cleanup();
|
|
418
462
|
resolve({ success: false, message: event.data.message || 'OAuth login failed' });
|
|
419
463
|
}
|
|
420
464
|
};
|
|
@@ -454,7 +498,8 @@ class StoneScriptPHPAuth {
|
|
|
454
498
|
async getTenantMemberships(accessToken) {
|
|
455
499
|
try {
|
|
456
500
|
const accountsUrl = this.getAccountsUrl();
|
|
457
|
-
const
|
|
501
|
+
const platformCode = encodeURIComponent(this.config.platformCode ?? '');
|
|
502
|
+
const response = await fetch(`${accountsUrl}/api/auth/memberships?platform_code=${platformCode}`, {
|
|
458
503
|
method: 'GET',
|
|
459
504
|
headers: {
|
|
460
505
|
'Authorization': `Bearer ${accessToken}`,
|
|
@@ -590,6 +635,26 @@ class StoneScriptPHPAuth {
|
|
|
590
635
|
}
|
|
591
636
|
return response.json();
|
|
592
637
|
}
|
|
638
|
+
async provisionTenant(storeName, countryCode, accessToken) {
|
|
639
|
+
const apiUrl = this.getPlatformApiUrl();
|
|
640
|
+
const response = await fetch(`${apiUrl}/auth/provision-tenant`, {
|
|
641
|
+
method: 'POST',
|
|
642
|
+
headers: {
|
|
643
|
+
'Content-Type': 'application/json',
|
|
644
|
+
'Authorization': `Bearer ${accessToken}`
|
|
645
|
+
},
|
|
646
|
+
credentials: 'include',
|
|
647
|
+
body: JSON.stringify({
|
|
648
|
+
store_name: storeName,
|
|
649
|
+
country_code: countryCode,
|
|
650
|
+
})
|
|
651
|
+
});
|
|
652
|
+
if (!response.ok) {
|
|
653
|
+
const errorData = await response.json();
|
|
654
|
+
throw new Error(errorData.message || 'Failed to provision tenant');
|
|
655
|
+
}
|
|
656
|
+
return response.json();
|
|
657
|
+
}
|
|
593
658
|
async checkEmail(email) {
|
|
594
659
|
try {
|
|
595
660
|
const accountsUrl = this.getAccountsUrl();
|
|
@@ -1014,6 +1079,17 @@ class AuthService {
|
|
|
1014
1079
|
throw new Error('checkOnboardingStatus not supported');
|
|
1015
1080
|
return this.plugin.checkOnboardingStatus(identityId);
|
|
1016
1081
|
}
|
|
1082
|
+
async provisionTenant(storeName, countryCode = 'IN') {
|
|
1083
|
+
if (!this.plugin.provisionTenant) {
|
|
1084
|
+
throw new Error('provisionTenant not supported by the configured auth plugin');
|
|
1085
|
+
}
|
|
1086
|
+
const result = await this.plugin.provisionTenant(storeName, countryCode, this.tokens.getAccessToken());
|
|
1087
|
+
if (result?.access_token) {
|
|
1088
|
+
this.tokens.setAccessToken(result.access_token);
|
|
1089
|
+
this.signinStatus.setSigninStatus(true);
|
|
1090
|
+
}
|
|
1091
|
+
return result;
|
|
1092
|
+
}
|
|
1017
1093
|
async completeTenantOnboarding(countryCode, tenantName, serverName) {
|
|
1018
1094
|
if (!this.plugin.completeTenantOnboarding)
|
|
1019
1095
|
throw new Error('completeTenantOnboarding not supported');
|
|
@@ -1762,6 +1838,7 @@ class TenantLoginComponent {
|
|
|
1762
1838
|
createTenantLinkAction = 'Create New Organization';
|
|
1763
1839
|
// Outputs
|
|
1764
1840
|
tenantSelected = new EventEmitter();
|
|
1841
|
+
needsOnboarding = new EventEmitter();
|
|
1765
1842
|
createTenant = new EventEmitter();
|
|
1766
1843
|
// Form Fields
|
|
1767
1844
|
email = '';
|
|
@@ -1830,8 +1907,8 @@ class TenantLoginComponent {
|
|
|
1830
1907
|
this.error = result.message || 'Login failed';
|
|
1831
1908
|
return;
|
|
1832
1909
|
}
|
|
1833
|
-
// Authentication successful
|
|
1834
|
-
await this.handlePostAuthFlow();
|
|
1910
|
+
// Authentication successful — pass result so membership can be reused
|
|
1911
|
+
await this.handlePostAuthFlow(result);
|
|
1835
1912
|
}
|
|
1836
1913
|
catch (err) {
|
|
1837
1914
|
this.error = err.message || 'An unexpected error occurred';
|
|
@@ -1849,8 +1926,24 @@ class TenantLoginComponent {
|
|
|
1849
1926
|
this.error = result.message || 'OAuth login failed';
|
|
1850
1927
|
return;
|
|
1851
1928
|
}
|
|
1852
|
-
//
|
|
1853
|
-
|
|
1929
|
+
// New identity — user exists but has no tenant membership
|
|
1930
|
+
if (result.isNewIdentity && result.identity) {
|
|
1931
|
+
this.needsOnboarding.emit({
|
|
1932
|
+
auth_method: result.authMethod || 'oauth',
|
|
1933
|
+
oauth_provider: result.oauthProvider,
|
|
1934
|
+
is_new_identity: true,
|
|
1935
|
+
identity: result.identity,
|
|
1936
|
+
});
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
// Multi-tenant selection — user has multiple memberships
|
|
1940
|
+
if (result.memberships && result.memberships.length > 0) {
|
|
1941
|
+
this.memberships = result.memberships;
|
|
1942
|
+
this.showingTenantSelector = true;
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
// Standard success — pass result so membership can be reused if present
|
|
1946
|
+
await this.handlePostAuthFlow(result);
|
|
1854
1947
|
}
|
|
1855
1948
|
catch (err) {
|
|
1856
1949
|
this.error = err.message || 'An unexpected error occurred';
|
|
@@ -1859,7 +1952,7 @@ class TenantLoginComponent {
|
|
|
1859
1952
|
this.loading = false;
|
|
1860
1953
|
}
|
|
1861
1954
|
}
|
|
1862
|
-
async handlePostAuthFlow() {
|
|
1955
|
+
async handlePostAuthFlow(loginResult) {
|
|
1863
1956
|
if (!this.showTenantSelector) {
|
|
1864
1957
|
// Tenant selection is disabled, emit event immediately
|
|
1865
1958
|
this.tenantSelected.emit({
|
|
@@ -1869,11 +1962,20 @@ class TenantLoginComponent {
|
|
|
1869
1962
|
});
|
|
1870
1963
|
return;
|
|
1871
1964
|
}
|
|
1872
|
-
//
|
|
1965
|
+
// Resolve memberships — prefer data from login response to avoid extra API call
|
|
1873
1966
|
this.loading = true;
|
|
1874
1967
|
try {
|
|
1875
|
-
|
|
1876
|
-
if (
|
|
1968
|
+
let memberships;
|
|
1969
|
+
if (loginResult?.membership) {
|
|
1970
|
+
// Login response already included membership — use it directly
|
|
1971
|
+
memberships = [loginResult.membership];
|
|
1972
|
+
}
|
|
1973
|
+
else {
|
|
1974
|
+
// Fall back to fetching memberships from API (with platform_code param)
|
|
1975
|
+
const result = await this.auth.getTenantMemberships();
|
|
1976
|
+
memberships = result.memberships;
|
|
1977
|
+
}
|
|
1978
|
+
if (!memberships || memberships.length === 0) {
|
|
1877
1979
|
// User has no tenants, prompt to create one
|
|
1878
1980
|
this.error = 'You are not a member of any organization. Please create one.';
|
|
1879
1981
|
if (this.allowTenantCreation) {
|
|
@@ -1881,7 +1983,7 @@ class TenantLoginComponent {
|
|
|
1881
1983
|
}
|
|
1882
1984
|
return;
|
|
1883
1985
|
}
|
|
1884
|
-
this.memberships =
|
|
1986
|
+
this.memberships = memberships;
|
|
1885
1987
|
// Get user name if available
|
|
1886
1988
|
const currentUser = this.auth.getCurrentUser();
|
|
1887
1989
|
if (currentUser) {
|
|
@@ -1969,7 +2071,7 @@ class TenantLoginComponent {
|
|
|
1969
2071
|
this.createTenant.emit();
|
|
1970
2072
|
}
|
|
1971
2073
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TenantLoginComponent, deps: [{ token: AuthService }, { token: ProviderRegistryService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1972
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TenantLoginComponent, isStandalone: true, selector: "lib-tenant-login", inputs: { title: "title", providers: "providers", showTenantSelector: "showTenantSelector", autoSelectSingleTenant: "autoSelectSingleTenant", prefillEmail: "prefillEmail", allowTenantCreation: "allowTenantCreation", tenantSelectorTitle: "tenantSelectorTitle", tenantSelectorDescription: "tenantSelectorDescription", continueButtonText: "continueButtonText", registerLinkText: "registerLinkText", registerLinkAction: "registerLinkAction", createTenantLinkText: "createTenantLinkText", createTenantLinkAction: "createTenantLinkAction" }, outputs: { tenantSelected: "tenantSelected", createTenant: "createTenant" }, ngImport: i0, template: `
|
|
2074
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TenantLoginComponent, isStandalone: true, selector: "lib-tenant-login", inputs: { title: "title", providers: "providers", showTenantSelector: "showTenantSelector", autoSelectSingleTenant: "autoSelectSingleTenant", prefillEmail: "prefillEmail", allowTenantCreation: "allowTenantCreation", tenantSelectorTitle: "tenantSelectorTitle", tenantSelectorDescription: "tenantSelectorDescription", continueButtonText: "continueButtonText", registerLinkText: "registerLinkText", registerLinkAction: "registerLinkAction", createTenantLinkText: "createTenantLinkText", createTenantLinkAction: "createTenantLinkAction" }, outputs: { tenantSelected: "tenantSelected", needsOnboarding: "needsOnboarding", createTenant: "createTenant" }, ngImport: i0, template: `
|
|
1973
2075
|
<div class="tenant-login-dialog">
|
|
1974
2076
|
@if (!showingTenantSelector) {
|
|
1975
2077
|
<!-- Step 1: Authentication -->
|
|
@@ -2334,6 +2436,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2334
2436
|
type: Input
|
|
2335
2437
|
}], tenantSelected: [{
|
|
2336
2438
|
type: Output
|
|
2439
|
+
}], needsOnboarding: [{
|
|
2440
|
+
type: Output
|
|
2337
2441
|
}], createTenant: [{
|
|
2338
2442
|
type: Output
|
|
2339
2443
|
}] } });
|
|
@@ -2784,7 +2888,7 @@ class AuthPageComponent {
|
|
|
2784
2888
|
}
|
|
2785
2889
|
</div>
|
|
2786
2890
|
</div>
|
|
2787
|
-
`, isInline: true, styles: [".auth-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;background:linear-gradient(135deg,#667eea,#764ba2)}.auth-card{background:#fff;border-radius:12px;box-shadow:0 10px 40px #0000001a;padding:40px;width:100%;max-width:480px}.logo{display:block;max-width:200px;max-height:80px;margin:0 auto 24px}.app-name{margin:0 0 12px;font-size:28px;font-weight:600;text-align:center;color:#1a202c}.subtitle{margin:0 0 32px;font-size:16px;text-align:center;color:#718096}:host ::ng-deep .tenant-login-dialog,:host ::ng-deep .register-dialog{padding:0;max-width:none}:host ::ng-deep .login-title,:host ::ng-deep .register-title{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "createTenant"] }, { kind: "component", type: RegisterComponent, selector: "lib-register", outputs: ["navigateToLogin"] }] });
|
|
2891
|
+
`, isInline: true, styles: [".auth-container{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;background:linear-gradient(135deg,#667eea,#764ba2)}.auth-card{background:#fff;border-radius:12px;box-shadow:0 10px 40px #0000001a;padding:40px;width:100%;max-width:480px}.logo{display:block;max-width:200px;max-height:80px;margin:0 auto 24px}.app-name{margin:0 0 12px;font-size:28px;font-weight:600;text-align:center;color:#1a202c}.subtitle{margin:0 0 32px;font-size:16px;text-align:center;color:#718096}:host ::ng-deep .tenant-login-dialog,:host ::ng-deep .register-dialog{padding:0;max-width:none}:host ::ng-deep .login-title,:host ::ng-deep .register-title{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }, { kind: "component", type: RegisterComponent, selector: "lib-register", outputs: ["navigateToLogin"] }] });
|
|
2788
2892
|
}
|
|
2789
2893
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: AuthPageComponent, decorators: [{
|
|
2790
2894
|
type: Component,
|
|
@@ -3839,7 +3943,7 @@ class TenantLoginDialogComponent {
|
|
|
3839
3943
|
(createTenant)="onCreateTenant()">
|
|
3840
3944
|
</lib-tenant-login>
|
|
3841
3945
|
</div>
|
|
3842
|
-
`, isInline: true, styles: [".dialog-wrapper{padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "createTenant"] }] });
|
|
3946
|
+
`, isInline: true, styles: [".dialog-wrapper{padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: TenantLoginComponent, selector: "lib-tenant-login", inputs: ["title", "providers", "showTenantSelector", "autoSelectSingleTenant", "prefillEmail", "allowTenantCreation", "tenantSelectorTitle", "tenantSelectorDescription", "continueButtonText", "registerLinkText", "registerLinkAction", "createTenantLinkText", "createTenantLinkAction"], outputs: ["tenantSelected", "needsOnboarding", "createTenant"] }] });
|
|
3843
3947
|
}
|
|
3844
3948
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TenantLoginDialogComponent, decorators: [{
|
|
3845
3949
|
type: Component,
|