asp.net-web-api2 OAuth 2.0 in ASP.NET Web API Storing OAuth Profile Information


Example

I have found that the Web API template is broken - the default implementation relies on cookies in the final step, which you probably don't want to be using in a Rest API. Without a cookie, GetExternalLoginInfoAsync in RegisterExternal always returns null. I removed RegisterExternal entirely, instead creating the final user account in GetExternalLogin - called on return from the OAuth provider (in this case, Google):

[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
      if (error != null)
      {
            return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
      }

      if (!User.Identity.IsAuthenticated)
      {
            return new ChallengeResult(provider, this);
      }

      ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);

      if (externalLogin == null)
      {
            return InternalServerError();
      }

      if (externalLogin.LoginProvider != provider)
      {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            return new ChallengeResult(provider, this);
      }

      ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider,
                externalLogin.ProviderKey));

      bool hasRegistered = user != null;

      if (hasRegistered)
      {
            Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
                
            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
                    OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
            CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
                Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
      }
      else
      {
            var accessToken = Authentication.User.Claims.Where(c => c.Type.Equals("urn:google:accesstoken")).Select(c => c.Value).FirstOrDefault();
            Uri apiRequestUri = new Uri("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + accessToken);

            RegisterExternalBindingModel model = new RegisterExternalBindingModel();

            using (var webClient = new System.Net.WebClient())
            {
                  var json = webClient.DownloadString(apiRequestUri);
                  dynamic jsonResult = JsonConvert.DeserializeObject(json);
                  model.Email = jsonResult.email;
                  model.Picture = jsonResult.picture;
                  model.Family_name = jsonResult.family_name;
                  model.Given_name = jsonResult.given_name;
            }

            var appUser = new ApplicationUser
            {
                  UserName = model.Email,
                  Email = model.Email,
                  ImageUrl = model.Picture,
                  FirstName = model.Given_name,
                  Surname = model.Family_name,
                  DateCreated = DateTime.Now
            };

            var loginInfo = await Authentication.GetExternalLoginInfoAsync();

            IdentityResult result = await UserManager.CreateAsync(appUser);
            if (!result.Succeeded)
            {
                  return GetErrorResult(result);
            }

            result = await UserManager.AddLoginAsync(appUser.Id, loginInfo.Login);
            if (!result.Succeeded)
            {
                  return GetErrorResult(result);
            }

            ClaimsIdentity oAuthIdentity = await appUser.GenerateUserIdentityAsync(UserManager,
                   OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookieIdentity = await appUser.GenerateUserIdentityAsync(UserManager,
                    CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(appUser.UserName);
            Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
      }

      return Ok();
}

This removes the need for the client application to perform a needless POST request after receiving an external access token.