Search code examples
asp.net-coreauthenticationasp.net-identity

How to restrict Microsoft Account external login to a list of emails, or to one domain, in ASP.NET Core


I have a web app that is intended for a list of people that use Outlook as their email provider. The web app uses Microsofts external login api. But anyone with a Microsoft email account can log in at the moment.

I am assuming this is an app setting within Microsoft dev portal, or an override somehow within may code. How can I filter the app's domain or have a strict list of accounts that can login?


Solution

  • AFAIK , portal doesn't have setting to control that , so the workaround may be restrict the account type after redirecting back to your client from Microsoft's login page .

    I assume you are using Microsoft.AspNetCore.Authentication.MicrosoftAccount:

    https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/microsoft-logins?view=aspnetcore-2.2

    You can check whether the email account is outlook account in ExternalLoginCallbackfunction of ASP.NET Identity :

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        if (remoteError != null)
        {
            ErrorMessage = $"Error from external provider: {remoteError}";
            return RedirectToAction(nameof(Login));
        }
        var info = await _signInManager.GetExternalLoginInfoAsync();
    
        var emailAddress = info.Principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email).Value;
    
        //Check whether this is outlook account .
        if (!"outlook.com".Equals(emailAddress.Split('@')[1]))
        {
            //return to error page and show error message 
        }
        if (info == null)
        {
            return RedirectToAction(nameof(Login));
        }
    
    
        // Sign in the user with this external login provider if the user already has a login.
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
            return RedirectToLocal(returnUrl);
        }
        if (result.IsLockedOut)
        {
            return RedirectToAction(nameof(Lockout));
        }
        else
        {
            // If the user does not have an account, then ask the user to create an account.
            ViewData["ReturnUrl"] = returnUrl;
            ViewData["LoginProvider"] = info.LoginProvider;
            var email = info.Principal.FindFirstValue(ClaimTypes.Email);
            return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
        }
    }
    

    Another way is checking in OnCreatingTicketevent in middleware ,if login user is not outlook account , redirect user to login page again :

    services.AddAuthentication().AddMicrosoftAccount(microsoftOptions =>
    {
        microsoftOptions.Events = new Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents
        {
                OnCreatingTicket = ctx =>
            {
                var email = ctx.Identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
                if (!"outlook.com".Equals(email.Value.Split('@')[1]))
                {
                    ctx.Response.Redirect("/");
    
                }
                return Task.FromResult(0);
            }
    
    
        };
        microsoftOptions.ClientId = Configuration["Authentication:Microsoft:ApplicationId"];
        microsoftOptions.ClientSecret = Configuration["Authentication:Microsoft:Password"];
    });
    

    Update :

    If you are creating .net core application with Individual User Accounts template that means you are working with ASP.NET Identity :

    https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio

    ASP.NET Core 2.1 and later provides ASP.NET Core Identity as a Razor Class Library. You can scaffold Identity in ASP.NET Core projects to modify the code and change the behavior. Or you can try the second solution above to modify the OpenID Connect middleware .