Base solution for your next web application

Activities of "harley.mcphee"

I am trying to prevent having to refresh the page after a user logs in but I am not sure how I can re-initialize the abp library once the user access token is set.

The current issue is that abp.auth.grantedPermissions is empty, I am assuming because abp initializes this on start up. How can I easily re-init abp once a user is logged in without refreshing my app?

Okay here it is https://github.com/aspnetzero/aspnet-zero-core/issues/5057

It seems when the access token is expired and signalr reconnects to our back-end, the TenantId and UserId is null when CreateClientForCurrentConnection is called.

During debugging, I checked AuthConfigurer.SetToken and it's still being sent the old expired access token, it seems the front-end is not updating the signalr object with the new refreshed access token stored in local storage, I debugged the front-end as well and when I inspect the connection object I can see that connection.qs still has the expired access token. When I refresh the browser this fixes it though, so I am guessing this is why the TenantId and UserId is null when a client reconnects, because the access token is no longer valid. Any idea on how to fix this?

I have been trying to update the access token in the connection but no luck, how can I fix this? We have front-ends that are connected for days without refreshing and sometimes we update the back-end or restart the server and this causes the client to reconnect but now when they reconnect they don't have a TenantId or ClientId so they are not getting the messages that they should be

import { Injectable, Injector, NgZone } from '@angular/core'; import { AppComponentBase } from '@shared/common/app-component-base'; import { HubConnection } from '@microsoft/signalr'; import { AppConsts } from '@shared/AppConsts'; import { LocalStorageService } from '@shared/utils/local-storage.service'; import { SignalRHelper } from '@shared/helpers/SignalRHelper';

@Injectable() export class TimerSignalrService extends AppComponentBase { timerHub: HubConnection; isConnected = false;

constructor(injector: Injector, public _zone: NgZone) {
    super(injector);
}

configureConnection(connection): void {
    // Set the common hub
    this.timerHub = connection;

    // Reconnect loop
    let reconnectTime = 5000;
    let tries = 1;
    function start() {
        return new Promise(function (resolve) {

            new LocalStorageService().getItem(AppConsts.authorization.encrptedAuthTokenName, function (err, value: {token: string, expireDate: string}) {

                if (new Date(value.expireDate) <= new Date()) {
                    console.log('Token expired, reconnecting in 5 seconds'); 
                    setTimeout(() => {
                        start().then(resolve);
                    }, 5000);
                    return;
                }

                SignalRHelper.updateEncryptedAuthToken(value.token);
                connection.qs = AppConsts.authorization.encrptedAuthTokenName + '=' + encodeURIComponent(value.token);

                /// this needs to be updated
                connection.connection.baseUrl = abp.appPath + 'signalr-timesheet?' + connection.qs;

                connection
                    .start()
                    .then(resolve)
                    .then(() => {
                        console.log('Reconnected on try ' + tries); // TODO: Remove console.log(
                        console.log('Reconnected with access token: ' + value.token); 
                        reconnectTime = 5000; // Reset reconnect time after a successful connection
                        tries = 1; // Reset tries after a successful connection
                    })
                    .catch(() => {
                        setTimeout(() => {
                            start().then(resolve);
                        }, reconnectTime);
                        console.log('Failed to reconnect on try ' + tries); // TODO: Remove console.log( (
                        tries += 1; // Increment tries
                    });

            });


        });
    }


    // Reconnect if hub disconnects
    connection.onclose((e) => {
        this.isConnected = false;

        if (e) {
            abp.log.debug('TimeSheet connection closed with error: ' + e);
        } else {
            abp.log.debug('TimeSheet disconnected');
        }

        start().then(() => {
            this.isConnected = true;
        });
    });

    // Register to get notifications
    this.registerTimerEvents(connection);
}

registerTimerEvents(connection): void {
    connection.on('getTimeEntry', (message) => {
        abp.event.trigger('app.timesheet.timeEntryUpdateReceived', message);
    });
}

init(): void {
    this._zone.runOutsideAngular(() => {
        abp.signalr.connect();
        abp.signalr
            .startConnection(abp.appPath + 'signalr-timesheet', (connection) => {
                console.log('Start connection to timesheet hub!'); // TODO: Remove console.log(
                this.configureConnection(connection);
            })
            .then(() => {
                console.log('Connected to time-sheet hub!'); // TODO: Remove console.log(
                abp.event.trigger('app.timesheet.connected');
                this.isConnected = true;
            });
    });
}

}

m.aliozkaya, the issue is the user needs to be associated with many tenants, not just one.

Yeah this could work but will be a bit janky, the ideal setup would be for a user to just have one email & password and to login to the app and be able to switch between tenants.

Many modern SAAS products employ a global account system where an email is unique throughout the entire platform. Tenants invite users via their email, and once authenticated, users can select which tenant they wish to access.

For instance, in Notion, I can switch between three different workspaces (or tenants) that I either own or have been invited to. This allows me to access tenant-specific data.

Here's how another SAAS product does it, when I login I need to select which account (tenant) I want to access

By default, it seems aspnetzero doesn't support this feature. Has any client managed to implement a similar system using the platform?

In essence, the modification should be straightforward. We'd need to update the TenantId in the AbpUsers table to accommodate a list of TenantIds or introduce a new table capturing which tenants a user can access. The login process would then display available tenants for users to choose from. Upon selection, users are directed to the respective tenant's subdomain or provided with a tenant-specific cookie header.

Showing 1 to 6 of 6 entries