@progalaxyelabs/ngx-stonescriptphp-client 1.17.1 → 1.18.1
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.
|
@@ -602,6 +602,289 @@ function provideNgxStoneScriptPhpClient(environment, plugin) {
|
|
|
602
602
|
]);
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Auth plugin for progalaxyelabs-auth (Rust/Axum).
|
|
607
|
+
*
|
|
608
|
+
* Speaks the Rust auth server's native format:
|
|
609
|
+
* - Login: { access_token, refresh_token, identity, membership, ... }
|
|
610
|
+
* - Tenant selection: { requires_tenant_selection, selection_token, memberships }
|
|
611
|
+
* - New identity: { access_token, identity, is_new_identity, memberships:[] }
|
|
612
|
+
* - select-tenant: Bearer header + { tenant_id } body
|
|
613
|
+
* - refresh: { access_token, refresh_token } body mode
|
|
614
|
+
*/
|
|
615
|
+
class ProgalaxyElabsAuth {
|
|
616
|
+
config;
|
|
617
|
+
constructor(config) {
|
|
618
|
+
this.config = config;
|
|
619
|
+
}
|
|
620
|
+
get host() {
|
|
621
|
+
return this.config.host;
|
|
622
|
+
}
|
|
623
|
+
// -- Login ----------------------------------------------------------------
|
|
624
|
+
async login(email, password) {
|
|
625
|
+
try {
|
|
626
|
+
const response = await fetch(`${this.host}/api/auth/login`, {
|
|
627
|
+
method: 'POST',
|
|
628
|
+
headers: { 'Content-Type': 'application/json' },
|
|
629
|
+
body: JSON.stringify({ email, password, platform: this.config.platformCode })
|
|
630
|
+
});
|
|
631
|
+
const data = await response.json();
|
|
632
|
+
if (!response.ok) {
|
|
633
|
+
return { success: false, message: data.error || data.message || 'Login failed' };
|
|
634
|
+
}
|
|
635
|
+
return this.handleLoginResponse(data);
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
return { success: false, message: 'Network error. Please try again.' };
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
async register(email, password, displayName) {
|
|
642
|
+
try {
|
|
643
|
+
const response = await fetch(`${this.host}/api/auth/register`, {
|
|
644
|
+
method: 'POST',
|
|
645
|
+
headers: { 'Content-Type': 'application/json' },
|
|
646
|
+
body: JSON.stringify({
|
|
647
|
+
email,
|
|
648
|
+
password,
|
|
649
|
+
display_name: displayName,
|
|
650
|
+
platform: this.config.platformCode
|
|
651
|
+
})
|
|
652
|
+
});
|
|
653
|
+
const data = await response.json();
|
|
654
|
+
if (!response.ok) {
|
|
655
|
+
return { success: false, message: data.error || data.message || 'Registration failed' };
|
|
656
|
+
}
|
|
657
|
+
return this.handleLoginResponse(data);
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
return { success: false, message: 'Network error. Please try again.' };
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
// -- Logout ---------------------------------------------------------------
|
|
664
|
+
async logout(refreshToken) {
|
|
665
|
+
try {
|
|
666
|
+
await fetch(`${this.host}/api/auth/logout`, {
|
|
667
|
+
method: 'POST',
|
|
668
|
+
headers: { 'Content-Type': 'application/json' },
|
|
669
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
catch { /* ignore */ }
|
|
673
|
+
}
|
|
674
|
+
// -- Session & Refresh ----------------------------------------------------
|
|
675
|
+
async checkSession() {
|
|
676
|
+
return { success: false };
|
|
677
|
+
}
|
|
678
|
+
async refresh(accessToken, refreshToken) {
|
|
679
|
+
if (!refreshToken)
|
|
680
|
+
return null;
|
|
681
|
+
try {
|
|
682
|
+
const response = await fetch(`${this.host}/api/auth/refresh`, {
|
|
683
|
+
method: 'POST',
|
|
684
|
+
headers: { 'Content-Type': 'application/json' },
|
|
685
|
+
body: JSON.stringify({ access_token: accessToken, refresh_token: refreshToken })
|
|
686
|
+
});
|
|
687
|
+
if (!response.ok)
|
|
688
|
+
return null;
|
|
689
|
+
const data = await response.json();
|
|
690
|
+
// Refresh can also return tenant_selection if memberships changed
|
|
691
|
+
if (data.requires_tenant_selection)
|
|
692
|
+
return null;
|
|
693
|
+
return data.access_token ?? null;
|
|
694
|
+
}
|
|
695
|
+
catch {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// -- Tenant operations ----------------------------------------------------
|
|
700
|
+
async selectTenant(tenantId, selectionToken) {
|
|
701
|
+
try {
|
|
702
|
+
const response = await fetch(`${this.host}/api/auth/select-tenant`, {
|
|
703
|
+
method: 'POST',
|
|
704
|
+
headers: {
|
|
705
|
+
'Authorization': `Bearer ${selectionToken}`,
|
|
706
|
+
'Content-Type': 'application/json'
|
|
707
|
+
},
|
|
708
|
+
body: JSON.stringify({ tenant_id: tenantId })
|
|
709
|
+
});
|
|
710
|
+
const data = await response.json();
|
|
711
|
+
if (!response.ok) {
|
|
712
|
+
return { success: false, message: data.error || data.message || 'Tenant selection failed' };
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
success: true,
|
|
716
|
+
accessToken: data.access_token,
|
|
717
|
+
refreshToken: data.refresh_token,
|
|
718
|
+
user: this.toUser(data.identity),
|
|
719
|
+
membership: this.toMembership(data.membership),
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
catch {
|
|
723
|
+
return { success: false, message: 'Network error. Please try again.' };
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async getTenantMemberships(accessToken) {
|
|
727
|
+
try {
|
|
728
|
+
const platformCode = encodeURIComponent(this.config.platformCode);
|
|
729
|
+
const response = await fetch(`${this.host}/api/auth/memberships?platform_code=${platformCode}`, {
|
|
730
|
+
method: 'GET',
|
|
731
|
+
headers: { 'Authorization': `Bearer ${accessToken}` }
|
|
732
|
+
});
|
|
733
|
+
if (!response.ok)
|
|
734
|
+
return [];
|
|
735
|
+
const data = await response.json();
|
|
736
|
+
return (data.memberships || []).map((m) => this.toMembership(m));
|
|
737
|
+
}
|
|
738
|
+
catch {
|
|
739
|
+
return [];
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async checkTenantSlugAvailable(slug) {
|
|
743
|
+
try {
|
|
744
|
+
const response = await fetch(`${this.host}/api/auth/check-tenant-slug/${slug}`);
|
|
745
|
+
const data = await response.json();
|
|
746
|
+
return { available: data.available || false, suggestion: data.suggestion };
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
return { available: true };
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
async checkOnboardingStatus(identityId, platformCode) {
|
|
753
|
+
const platform = platformCode ?? this.config.platformCode;
|
|
754
|
+
const response = await fetch(`${this.host}/api/auth/onboarding/status?platform_code=${platform}&identity_id=${identityId}`);
|
|
755
|
+
if (!response.ok)
|
|
756
|
+
throw new Error('Failed to check onboarding status');
|
|
757
|
+
return response.json();
|
|
758
|
+
}
|
|
759
|
+
async checkEmail(email) {
|
|
760
|
+
try {
|
|
761
|
+
const response = await fetch(`${this.host}/api/auth/check-email`, {
|
|
762
|
+
method: 'POST',
|
|
763
|
+
headers: { 'Content-Type': 'application/json' },
|
|
764
|
+
body: JSON.stringify({ email })
|
|
765
|
+
});
|
|
766
|
+
const data = await response.json();
|
|
767
|
+
return { exists: data.exists, user: data.user };
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
return { exists: false };
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
// -- OAuth ----------------------------------------------------------------
|
|
774
|
+
async loginWithProvider(provider) {
|
|
775
|
+
return new Promise((resolve) => {
|
|
776
|
+
const width = 500, height = 600;
|
|
777
|
+
const left = (window.screen.width - width) / 2;
|
|
778
|
+
const top = (window.screen.height - height) / 2;
|
|
779
|
+
const oauthUrl = `${this.host}/oauth/${provider}?platform=${this.config.platformCode}&mode=popup`;
|
|
780
|
+
const popup = window.open(oauthUrl, `${provider}_login`, `width=${width},height=${height},left=${left},top=${top}`);
|
|
781
|
+
if (!popup) {
|
|
782
|
+
resolve({ success: false, message: 'Popup blocked. Please allow popups for this site.' });
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const cleanup = () => {
|
|
786
|
+
window.removeEventListener('message', messageHandler);
|
|
787
|
+
clearInterval(checkClosed);
|
|
788
|
+
if (popup && !popup.closed)
|
|
789
|
+
popup.close();
|
|
790
|
+
};
|
|
791
|
+
const messageHandler = (event) => {
|
|
792
|
+
if (event.origin !== new URL(this.host).origin)
|
|
793
|
+
return;
|
|
794
|
+
cleanup();
|
|
795
|
+
if (event.data.type === 'oauth_success') {
|
|
796
|
+
resolve({
|
|
797
|
+
success: true,
|
|
798
|
+
accessToken: event.data.access_token,
|
|
799
|
+
refreshToken: event.data.refresh_token,
|
|
800
|
+
user: this.toUser(event.data.user || event.data.identity),
|
|
801
|
+
membership: event.data.membership ? this.toMembership(event.data.membership) : undefined,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
else if (event.data.type === 'oauth_tenant_selection') {
|
|
805
|
+
resolve({
|
|
806
|
+
success: true,
|
|
807
|
+
accessToken: event.data.selection_token,
|
|
808
|
+
memberships: (event.data.memberships || []).map((m) => this.toMembership(m)),
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
else if (event.data.type === 'oauth_new_identity') {
|
|
812
|
+
resolve({
|
|
813
|
+
success: true,
|
|
814
|
+
accessToken: event.data.access_token,
|
|
815
|
+
refreshToken: event.data.refresh_token,
|
|
816
|
+
isNewIdentity: true,
|
|
817
|
+
authMethod: event.data.auth_method,
|
|
818
|
+
oauthProvider: event.data.oauth_provider,
|
|
819
|
+
identity: event.data.identity,
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
else if (event.data.type === 'oauth_error') {
|
|
823
|
+
resolve({ success: false, message: event.data.message || 'OAuth login failed' });
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
window.addEventListener('message', messageHandler);
|
|
827
|
+
const checkClosed = setInterval(() => {
|
|
828
|
+
if (popup.closed) {
|
|
829
|
+
clearInterval(checkClosed);
|
|
830
|
+
window.removeEventListener('message', messageHandler);
|
|
831
|
+
resolve({ success: false, message: 'Login cancelled' });
|
|
832
|
+
}
|
|
833
|
+
}, 500);
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
// -- Internal helpers -----------------------------------------------------
|
|
837
|
+
handleLoginResponse(data) {
|
|
838
|
+
// New identity — needs onboarding
|
|
839
|
+
if (data.is_new_identity) {
|
|
840
|
+
return {
|
|
841
|
+
success: true,
|
|
842
|
+
accessToken: data.access_token,
|
|
843
|
+
refreshToken: data.refresh_token,
|
|
844
|
+
isNewIdentity: true,
|
|
845
|
+
authMethod: data.auth_method,
|
|
846
|
+
oauthProvider: data.oauth_provider,
|
|
847
|
+
identity: data.identity,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
// Multi-tenant selection required
|
|
851
|
+
if (data.requires_tenant_selection) {
|
|
852
|
+
return {
|
|
853
|
+
success: true,
|
|
854
|
+
accessToken: data.selection_token,
|
|
855
|
+
memberships: (data.memberships || []).map((m) => this.toMembership(m)),
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
// Standard success (single tenant auto-selected or tenant specified)
|
|
859
|
+
return {
|
|
860
|
+
success: true,
|
|
861
|
+
accessToken: data.access_token,
|
|
862
|
+
refreshToken: data.refresh_token,
|
|
863
|
+
user: this.toUser(data.identity),
|
|
864
|
+
membership: this.toMembership(data.membership),
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
toUser(raw) {
|
|
868
|
+
if (!raw)
|
|
869
|
+
return undefined;
|
|
870
|
+
return {
|
|
871
|
+
email: raw.email,
|
|
872
|
+
display_name: raw.display_name ?? raw.email?.split('@')[0] ?? '',
|
|
873
|
+
photo_url: raw.photo_url ?? raw.picture,
|
|
874
|
+
is_email_verified: raw.is_email_verified ?? false
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
toMembership(raw) {
|
|
878
|
+
return {
|
|
879
|
+
tenant_id: raw.tenant_id,
|
|
880
|
+
slug: raw.tenant_slug ?? raw.slug ?? '',
|
|
881
|
+
name: raw.tenant_name ?? raw.name ?? '',
|
|
882
|
+
role: raw.role ?? '',
|
|
883
|
+
status: raw.status ?? 'active',
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
605
888
|
class ApiResponse {
|
|
606
889
|
status;
|
|
607
890
|
data;
|
|
@@ -1827,7 +2110,14 @@ class TenantLoginComponent {
|
|
|
1827
2110
|
}
|
|
1828
2111
|
// Auto-select if user has only one tenant
|
|
1829
2112
|
if (this.memberships.length === 1 && this.autoSelectSingleTenant) {
|
|
1830
|
-
|
|
2113
|
+
const m = this.memberships[0];
|
|
2114
|
+
// If login already returned a tenant-scoped token (via membership in response),
|
|
2115
|
+
// just emit — no need to call select-tenant again.
|
|
2116
|
+
if (loginResult?.membership) {
|
|
2117
|
+
this.tenantSelected.emit({ tenantId: m.tenant_id, tenantSlug: m.slug, role: m.role });
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
await this.selectAndContinue(m);
|
|
1831
2121
|
}
|
|
1832
2122
|
else {
|
|
1833
2123
|
// Show tenant selector
|
|
@@ -3925,5 +4215,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3925
4215
|
* Generated bundle index. Do not edit.
|
|
3926
4216
|
*/
|
|
3927
4217
|
|
|
3928
|
-
export { AUTH_PLUGIN, ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, FilesService, LoginDialogComponent, MyEnvironmentModel, ProviderRegistryService, RegisterComponent, SigninStatusService, StoneScriptPHPAuth, TenantLoginComponent, TenantLoginDialogComponent, TenantRegisterComponent, TenantRegisterDialogComponent, TokenService, VerifyStatus, provideNgxStoneScriptPhpClient };
|
|
4218
|
+
export { AUTH_PLUGIN, ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, FilesService, LoginDialogComponent, MyEnvironmentModel, ProgalaxyElabsAuth, ProviderRegistryService, RegisterComponent, SigninStatusService, StoneScriptPHPAuth, TenantLoginComponent, TenantLoginDialogComponent, TenantRegisterComponent, TenantRegisterDialogComponent, TokenService, VerifyStatus, provideNgxStoneScriptPhpClient };
|
|
3929
4219
|
//# sourceMappingURL=progalaxyelabs-ngx-stonescriptphp-client.mjs.map
|