Base solution for your next web application
Open Closed

Social login issues #12604


User avatar
0
niengineering created

Dear Support,

I have an app (angular+.NET core) working with per tenant settings of social logins and I have a couple of questions.

  1. We see that with disconnect button we don't disconnect from the social. E.g. with Azure AD we don't signout and the second time we try to login we login without credentials request. How can we solve this issue?

  2. Is there the possibility to inhibit the local login only for the users that has a social login enabled?

Thank you in advance for you support

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

1 Answer(s)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @niengineering

    This behavior is expected and by design.

    In ASP.NET Zero, the Disconnect button only removes the association between the local user and the external login provider from the database (the UserLogins table). It does not terminate the session on the external identity provider (Azure AD, Google, Facebook, etc.).

    This aligns with standard OAuth 2.0 / OpenID Connect behavior.

    • External providers (Azure AD, Google, Facebook) manage their own sessions
    • Disconnect only deletes the local user provider mapping
    • The provider’s authentication cookie remains active in the browser
    • On the next login attempt, the provider silently re authenticates the user

    Explicit External Provider Logout

    You can extend the logout flow to redirect users to the provider’s logout endpoint.

    Example *.Web.Core:

    public async Task<IActionResult> ExternalLogout(string providerName)
    {
        await _signInManager.SignOutAsync();
    
        var logoutUrl = providerName switch
        {
            "Microsoft" => 
                $"https://login.microsoftonline.com/common/oauth2/v2.0/logout?post_logout_redirect_uri={_appConfiguration["App:ClientRootAddress"]}",
    
            "Google" => 
                $"https://accounts.google.com/Logout?continue={_appConfiguration["App:ClientRootAddress"]}",
    
            _ => Url.Action("Login")
        };
    
        return Redirect(logoutUrl);
    }
    

    This ensures the external provider session is also terminated.

    Force Account Selection (Azure AD)

    If full logout is not desired, you can force Azure AD to always prompt the user to select an account:

    options.Events.OnRedirectToIdentityProvider = context =>
    {
        context.ProtocolMessage.SetParameter("prompt", "select_account");
        return Task.CompletedTask;
    };
    

    This prevents silent re login using an existing Azure AD session.

    Extend Disconnect Logic on the Angular Side

    After unlinking the social login, you can open the provider’s logout URL in a new tab:

    disconnectFromSocialProvider(providerName: string): void {
        this._userLinkService.unlinkUser(
            new UnlinkUserInput({ providerName })
        ).subscribe(() => {
    
            const logoutUrls = {
                'Microsoft': 'https://login.microsoftonline.com/common/oauth2/v2.0/logout',
                'Google': 'https://accounts.google.com/Logout'
            };
    
            if (logoutUrls[providerName]) {
                window.open(logoutUrls[providerName], '_blank');
            }
    
            this.notify.success(this.l('SuccessfullyDisconnected'));
            this.getUserLoginAttempts();
        });
    }
    

    Disabling Local Login for Users with Social Login Enabled

    ASP.NET Zero does not provide this feature out of the box, but it can be implemented with a small customization.

    Customize Authentication Logic

    Extend TokenAuthController or AccountAppService:

    public async Task<AuthenticateResultModel> Authenticate(AuthenticateModel model)
    {
        var user = await _userManager.FindByNameOrEmailAsync(model.UserNameOrEmailAddress);
    
        if (user != null)
        {
            var externalLogins = await _userManager.GetLoginsAsync(user);
    
            if (externalLogins.Any())
            {
                throw new UserFriendlyException(
                    L("UserHasSocialLoginEnabled",
                      string.Join(", ", externalLogins.Select(x => x.LoginProvider)))
                );
            }
        }
    
        return await base.Authenticate(model);
    }
    

    (Optional) Tenant Level Setting

    If you want this behavior to be configurable per tenant:

    public const string DisableLocalLoginForSocialUsers =
        "App.UserManagement.DisableLocalLoginForSocialUsers";
    
    context.Create(
        DisableLocalLoginForSocialUsers,
        "false",
        scopes: SettingScopes.Tenant,
        isInherited: false
    );
    

    Usage:

    var disableLocalLogin =
        await SettingManager.GetSettingValueAsync<bool>(
            AppSettings.UserManagement.DisableLocalLoginForSocialUsers);
    
    if (disableLocalLogin && externalLogins.Any())
    {
        throw new UserFriendlyException("...");
    }
    

    Angular UI

    authenticate(): void {
        this._tokenAuthService.authenticate(this.loginModel)
            .pipe(finalize(() => this.saving = false))
            .subscribe({
                next: () => { /* success */ },
                error: (error) => {
                    if (error.error?.message?.includes('UserHasSocialLoginEnabled')) {
                        this.message.warn(error.error.message);
                    } else {
                        this.message.error(error.error.message);
                    }
                }
            });
    }
    

    If you have further questions, feel free to ask. Thank you

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)