1

I'm building an ASP.NET MVC 5 (C#) web application utilizing Entity Framework, and Google OAuthV2 for login Authentication.

I need the following 3 things:

  1. Secure the web application so that only users with a Google Business Email within a specific domain is allowed to login (*@thisIsMyBusinessDomain.com).
  2. Hide all logged in Google accounts that are not applicable to the work domain that is required.
  3. Check on the Server side to verify the token submitted upon login and validate the token is for the user Google has assigned it for (which should be the user logging in and issuing it/attempting to use it).

Unfortunately the numerous links I have read through on Google's documentation either does not apply as it is outdated or I can't find the package for it, or it mentions a parameter hd (Hosted Domain) which I can actually see in an HTTP request upon business email account login procedure but do not know how to access/set/get/tap-into, or is not for ASP.NET (C#) but rather PHP/Python/Java libraries or scripts which I do not wish to utilize as I need to work with ASP.NET (C#).

Currently this is the code I have employed for a simplistic (and non-secure) approach to blocking a user from logging in with an email that is not part of the hosted work domain:

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using STCMonitoringPortal.Models;

...
...
...

public ApplicationSignInManager SignInManager
        {
            get
            {
                return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
            }
            private set 
            { 
                _signInManager = value; 
            }
        }
...
...
...  
        //
        // POST: /Account/ExternalLogin
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            // Request a redirect to the external login provider
            return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
        }
...
...
...
        //
        // GET: /Account/ExternalLoginCallback
        [AllowAnonymous]
        public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
        {
            var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                return RedirectToAction("Login");
            }

            string userName = loginInfo.ExternalIdentity.GetUserName();
            string userId = loginInfo.ExternalIdentity.GetUserId();
            string email = loginInfo.Email;
            string defaultUserName = loginInfo.DefaultUserName;
            string loginProvider = loginInfo.Login.LoginProvider;
            string loginProviderKey = loginInfo.Login.ProviderKey;

            //These are null if the account has no first AND last name and will throw an exception
            string actorName = "null";
            //if (loginInfo.ExternalIdentity.Actor.Name != null)
                //actorName = loginInfo.ExternalIdentity.Actor.Name;
            string actorAuthenticationType = "null";
            //loginInfo.ExternalIdentity.Actor.AuthenticationType;
            string actorNameClaimType = "null";
            //loginInfo.ExternalIdentity.Actor.NameClaimType;


            System.Diagnostics.Debug.Print(Environment.NewLine + "Username:  " + userName
                + Environment.NewLine + "UserID: " + userId
                + Environment.NewLine + "Email: " + email
                + Environment.NewLine + "Default Username: " + defaultUserName
                + Environment.NewLine + "Login Provider: " + loginProvider
                + Environment.NewLine + "Login Provider Key: " + loginProviderKey
                + Environment.NewLine + "Actor Name: " + actorName
                + Environment.NewLine + "Actor Authentication Type: " + actorAuthenticationType
                + Environment.NewLine + "Actor Name Claim Type: " + actorNameClaimType
                + Environment.NewLine);

            SignInStatus result;

            if (!loginInfo.Email.EndsWith("@thisIsMyBusinessDomain.com") ||
                !loginInfo.Login.LoginProvider.Equals("Google") ||
                !loginInfo.ExternalIdentity.GetUserId().Equals(loginInfo.Login.ProviderKey))
            {
                //Using the information gotten from the request
                //If the Email Address is not from the domain thisIsMyBusinessDomain.com
                //OR
                //If the Login Provider is not listed as Google
                //OR
                //If the provided UserID does not match the Login Provider (Google's) Key
                //Deny the user login access
                System.Diagnostics.Debug.Print(Environment.NewLine + "Email:  " + loginInfo.Email +
                    Environment.NewLine + "User Id:  " + loginInfo.ExternalIdentity.GetUserId()
                    + Environment.NewLine + "Provider Key: " + loginInfo.Login.ProviderKey);
                result = SignInStatus.LockedOut;               
            }
            else
            {
                System.Diagnostics.Debug.Print(Environment.NewLine + "SignInManagerRunning: True");
                // Sign in the user with this external login provider if the user already has a login
                result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
            }
            System.Diagnostics.Debug.Print(Environment.NewLine + result);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("AccessDenied");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
                case SignInStatus.Failure:
                    //return View("AccessDenied");
                default:
                    // If the user does not have an account, then prompt the user to create an account
                    ViewBag.ReturnUrl = returnUrl;
                    ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
                    await ExternalLoginConfirmation(new ExternalLoginConfirmationViewModel { Email = loginInfo.Email },
    returnUrl);
                    return RedirectToLocal(returnUrl);
                    //comment out, do not let user choose username, their email will be used.
                    //return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
            }
        }

How can I achieve the three goals above? Suggestions?

EDIT 1: I've already reviewed the following link: Restrict Login Email with Google OAuth2.0 to Specific Domain Name

As stated above, the above linked post (with its answers) is a solution for PHP, not ASP.NET. Additionally, no answer was officially accepted; users reported instances in which the "hd" parameter was not working as intended and that the suggested code was not secure (someone could bypass using tools like Fiddler, which I must prevent through server side validation).

Community
  • 1
  • 1
jat247
  • 73
  • 1
  • 7
  • Possible duplicate of [Restrict Login Email with Google OAuth2.0 to Specific Domain Name](http://stackoverflow.com/questions/10858813/restrict-login-email-with-google-oauth2-0-to-specific-domain-name) – Tieson T. Aug 10 '16 at 01:32
  • 1
    @TiesonT. I had already reviewed the link you cited, it is old and for PHP, not ASP.NET. Additionally no answer was officially accepted because of security concerns with the suggested implementation. – jat247 Aug 10 '16 at 02:02

1 Answers1

-2

The following blog may help you especially parts 2 & 3. However it is about Web Api 2 & OWIN and may need some tweeking ...

ASP.NET Identity 2.1 with ASP.NET Web API 2.2

  • Unfortunately the documentation at the site you listed is not what I'm looking for and is outdated for MVC 5 (as the author's work tries to create frameworks that exist now, and did not at the time of his work). Additionally I need to work with Google's APIs if possible (since this is what I'm using for login/authentication, and am not creating my own token server etc). – jat247 Aug 10 '16 at 16:21