@progalaxyelabs/ngx-stonescriptphp-client 1.17.1 → 1.18.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.
@@ -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;
@@ -3925,5 +4208,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
3925
4208
  * Generated bundle index. Do not edit.
3926
4209
  */
3927
4210
 
3928
- export { AUTH_PLUGIN, ApiConnectionService, ApiResponse, AuthPageComponent, AuthService, CsrfService, DbService, FilesService, LoginDialogComponent, MyEnvironmentModel, ProviderRegistryService, RegisterComponent, SigninStatusService, StoneScriptPHPAuth, TenantLoginComponent, TenantLoginDialogComponent, TenantRegisterComponent, TenantRegisterDialogComponent, TokenService, VerifyStatus, provideNgxStoneScriptPhpClient };
4211
+ 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
4212
  //# sourceMappingURL=progalaxyelabs-ngx-stonescriptphp-client.mjs.map