Search code examples
aspnetboilerplate

Sign-in user via remote services and about TokenAuthController


I need to sign-in the user using only remote services. I think using TokenAuthController in Web.Core application

I really can't understand why the snippet given below doesn't work. I have added a new method called Login in TokenAuthController .

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.MultiTenancy;
using Abp.Runtime.Security;
using Abp.UI;
using Abp.Web.Models;
using Microsoft.AspNetCore.Authorization;
using TSE.DergiAbone.Authentication.External;
using TSE.DergiAbone.Authentication.JwtBearer;
using TSE.DergiAbone.Authorization;
using TSE.DergiAbone.Authorization.Users;
using TSE.DergiAbone.Identity;
using TSE.DergiAbone.Models.TokenAuth;
using TSE.DergiAbone.MultiTenancy;

namespace TSE.DergiAbone.Controllers
{
    [Route("api/[controller]/[action]")]
    public class TokenAuthController : DergiAboneControllerBase
    {
        private readonly LogInManager _logInManager;
        private readonly SignInManager _signInManager;

        private readonly ITenantCache _tenantCache;
        private readonly AbpLoginResultTypeHelper _abpLoginResultTypeHelper;
        private readonly TokenAuthConfiguration _configuration;
        private readonly IExternalAuthConfiguration _externalAuthConfiguration;
        private readonly IExternalAuthManager _externalAuthManager;
        private readonly UserRegistrationManager _userRegistrationManager;

        public TokenAuthController(
            LogInManager logInManager,
            SignInManager signInManager,
            ITenantCache tenantCache,
            AbpLoginResultTypeHelper abpLoginResultTypeHelper,
            TokenAuthConfiguration configuration,
            IExternalAuthConfiguration externalAuthConfiguration,
            IExternalAuthManager externalAuthManager,
            UserRegistrationManager userRegistrationManager)
        {
            _logInManager = logInManager;
            _tenantCache = tenantCache;
            _abpLoginResultTypeHelper = abpLoginResultTypeHelper;
            _configuration = configuration;
            _externalAuthConfiguration = externalAuthConfiguration;
            _externalAuthManager = externalAuthManager;
            _userRegistrationManager = userRegistrationManager;
            _signInManager = signInManager;
        }
        ***[HttpPost]
        public virtual async Task<JsonResult> Login(string UserName, string password,bool IsPersistent )
        {

            var loginResult = await GetLoginResultAsync(UserName, password, GetTenancyNameOrNull());

            //var result = await _signInManager.SignInAsync(loginResult.Identity, IsPersistent);
            var result = await _signInManager.PasswordSignInAsync(UserName, password, true, false);
            if (result.Succeeded)
           {
              long bak= User.Identity.GetUserId().Value;
                string res = "User signed in";
           }
            await UnitOfWorkManager.Current.SaveChangesAsync();
            bool chk = User.Identity.IsAuthenticated;
            return Json(new Abp.Web.Models.AjaxResponse { TargetUrl = "" });
        }***







        [HttpPost]
        public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
        {
            var loginResult = await GetLoginResultAsync(
                model.UserNameOrEmailAddress,
                model.Password,
                GetTenancyNameOrNull()
            );




            //var chk = _logInManager.LoginAsync("[email protected]", "123qwe", "TSEDergi").Result;
            //var chk2 = _logInManager.Login("[email protected]", "123qwe", "TSEDergi");

            //var name = User.Identity.Name;
            //bool bak0 = User.IsInRole("admin");

            //var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
            //var loginResult = await GetLoginResultAsync("[email protected]", "123qwe", "TSEDergi");

            //await _signInManager.SignInAsync(loginResult.Identity, model.RememberClient);//_logInManager.LoginAsync("[email protected]", "123qwe", "TSEDergi").Result;

            //var name = User.Identity.Name;
            //bool bak0 = User.IsInRole("admin");





            var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
            return new AuthenticateResultModel
            {
                AccessToken = accessToken,
                EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
                ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
                UserId = loginResult.User.Id
            };
        }

        [HttpGet]
        public List<ExternalLoginProviderInfoModel> GetExternalAuthenticationProviders()
        {
            return ObjectMapper.Map<List<ExternalLoginProviderInfoModel>>(_externalAuthConfiguration.Providers);
        }

        [HttpPost]
        public async Task<ExternalAuthenticateResultModel> ExternalAuthenticate([FromBody] ExternalAuthenticateModel model)
        {
            var externalUser = await GetExternalUserInfo(model);

            var loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, model.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());

            switch (loginResult.Result)
            {
                case AbpLoginResultType.Success:
                    {
                        var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
                        return new ExternalAuthenticateResultModel
                        {
                            AccessToken = accessToken,
                            EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
                            ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds
                        };
                    }
                case AbpLoginResultType.UnknownExternalLogin:
                    {
                        var newUser = await RegisterExternalUserAsync(externalUser);
                        if (!newUser.IsActive)
                        {
                            return new ExternalAuthenticateResultModel
                            {
                                WaitingForActivation = true
                            };
                        }

                        // Try to login again with newly registered user!
                        loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, model.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());
                        if (loginResult.Result != AbpLoginResultType.Success)
                        {
                            throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
                                loginResult.Result,
                                model.ProviderKey,
                                GetTenancyNameOrNull()
                            );
                        }

                        return new ExternalAuthenticateResultModel
                        {
                            AccessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity)),
                            ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds
                        };
                    }
                default:
                    {
                        throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
                            loginResult.Result,
                            model.ProviderKey,
                            GetTenancyNameOrNull()
                        );
                    }
            }
        }

        private async Task<User> RegisterExternalUserAsync(ExternalAuthUserInfo externalUser)
        {
            var user = await _userRegistrationManager.RegisterAsync(
                externalUser.Name,
                externalUser.Surname,
                externalUser.EmailAddress,
                externalUser.EmailAddress,
                Authorization.Users.User.CreateRandomPassword(),
                true
            );

            user.Logins = new List<UserLogin>
            {
                new UserLogin
                {
                    LoginProvider = externalUser.Provider,
                    ProviderKey = externalUser.ProviderKey,
                    TenantId = user.TenantId
                }
            };

            await CurrentUnitOfWork.SaveChangesAsync();

            return user;
        }

        private async Task<ExternalAuthUserInfo> GetExternalUserInfo(ExternalAuthenticateModel model)
        {
            var userInfo = await _externalAuthManager.GetUserInfo(model.AuthProvider, model.ProviderAccessCode);
            if (userInfo.ProviderKey != model.ProviderKey)
            {
                throw new UserFriendlyException(L("CouldNotValidateExternalUser"));
            }

            return userInfo;
        }

        private string GetTenancyNameOrNull()
        {
            if (!AbpSession.TenantId.HasValue)
            {
                return null;
            }

            return _tenantCache.GetOrNull(AbpSession.TenantId.Value)?.TenancyName;
        }


        [HttpPost]
        public AbpLoginResult<Tenant, User> GetLoginResult2Async(string usernameOrEmailAddress, string password, string tenancyName)
        {
            var loginResult = _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName).Result;

            switch (loginResult.Result)
            {
                case AbpLoginResultType.Success:
                    return loginResult;
                default:
                    throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);
            }
        }

        private async Task<AbpLoginResult<Tenant, User>> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName)
        {
            var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName);

            switch (loginResult.Result)
            {
                case AbpLoginResultType.Success:
                    return loginResult;
                default:
                    throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);
            }
        }

        private string CreateAccessToken(IEnumerable<Claim> claims, TimeSpan? expiration = null)
        {
            var now = DateTime.UtcNow;

            var jwtSecurityToken = new JwtSecurityToken(
                issuer: _configuration.Issuer,
                audience: _configuration.Audience,
                claims: claims,
                notBefore: now,
                expires: now.Add(expiration ?? _configuration.Expiration),
                signingCredentials: _configuration.SigningCredentials
            );

            return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
        }

        private static List<Claim> CreateJwtClaims(ClaimsIdentity identity)
        {
            var claims = identity.Claims.ToList();
            var nameIdClaim = claims.First(c => c.Type == ClaimTypes.NameIdentifier);

            // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
            claims.AddRange(new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, nameIdClaim.Value),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)
            });

            return claims;
        }

        private string GetEncrpyedAccessToken(string accessToken)
        {
            return SimpleStringCipher.Instance.Encrypt(accessToken, AppConsts.DefaultPassPhrase);
        }
    }
}

I am getting a reasonable loginResult. And PasswordSignInAsync method returns with success. At that point I conclude the sign in process is OK. But after when I check User.Identity. I see it is null. Same is valid for the SignInAsync method.All I wanna do is sign-in the user only using the remote services. Thank you all..


Solution

  • I solved the problem as given below:

    1. Change the httpost login method in AccountController of Web.Mvc application as below

      [HttpPost]
      [UnitOfWork]
      public virtual async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "")
      {
          var claims = GetClaims(loginModel.UsernameOrEmailAddress, loginModel.Password);
      
      
          if (claims == null)//giriş yapılamadı
          {
              return Json(new AjaxResponse { TargetUrl = "" });
          }
          else
          {
              var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name,
                  ClaimTypes.Role);
              foreach (var claim in claims)
              {
                  identity.AddClaim(new Claim(claim.type, claim.value));
              }
      
              //AbpSession.UserId=18;
              //// Authenticate using the identity
              //var principal = new ClaimsPrincipal(identity);
              //await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });
              //bool chk = User.Identity.IsAuthenticated;
              ////bool bak = User.Identity.IsAuthenticated;
              //bool bak2 = User.IsInRole("Admin");
              //return RedirectToAction("Index", "Home");
              await _signInManager.SignInAsync(identity, loginModel.RememberMe);
              await UnitOfWorkManager.Current.SaveChangesAsync();
              bool bak = User.Identity.IsAuthenticated;
              var bakl = AbpSession.UserId;
          }
      
      
          returnUrl = NormalizeReturnUrl(returnUrl);
          if (!string.IsNullOrWhiteSpace(returnUrlHash))
          {
              returnUrl = returnUrl + returnUrlHash;
          }
      
      
      
          return Json(new AjaxResponse { TargetUrl = returnUrl });
      }
      
    2. Create GetClaims method in AccountController of Web.Mvc application

          protected List<ClaimRootObject> GetClaims(string UserName, string Password)
      {
      
          using (var client = new HttpClient())
          {
              string reqString = "http://localhost:21021/api/" + "TokenAuth/GetClaims/GetClaims?UserName=" + UserName + "&password=" + Password + "&TenantName=Default";
              //string reqString = "http://localhost:81/api/TokenAuth/GetClaims/GetClaims?UserName=admin&password=123qwe&TenantName=TSEDergi";
              HttpResponseMessage response = client.GetAsync(reqString).Result; // Blocking call!  
              if (response.IsSuccessStatusCode)
              {
                  // Get the response
                  var JsonString = response.Content.ReadAsStringAsync();
                  // Deserialise the data (include the Newtonsoft JSON Nuget package if you don't already have it)
                  //List<Claim> deserialized = JsonConvert.DeserializeObject<List<Claim>>(JsonString.Result);
                  List<ClaimRootObject> deserialized = JsonConvert.DeserializeObject<List<ClaimRootObject>>(JsonString.Result);
      
                  if (deserialized != null)
                  {
                      return deserialized;
                  }
              }
              else
              {
      
              }
          }
          return null;
      }
      
    3. Create the required objects

      
      public class ClaimRootObject
      {
          public string issuer { get; set; }
          public string originalIssuer { get; set; }
          public Properties properties { get; set; }
          public Subject subject { get; set; }
          public string type { get; set; }
          public string value { get; set; }
          public string valueType { get; set; }
      }
      public class Properties
      {
      }
      public class Subject
      {
          public string authenticationType { get; set; }
          public bool isAuthenticated { get; set; }
          public object actor { get; set; }
          public object bootstrapContext { get; set; }
          public List claims { get; set; }
          public object label { get; set; }
          public string name { get; set; }
          public string nameClaimType { get; set; }
          public string roleClaimType { get; set; }
      }
      
    4. And last step, modify your startup class of Web.Mvc project to enable cookie authentication.

    
        public IServiceProvider ConfigureServices(IServiceCollection services)
            {
                // MVC
                services.AddMvc(
                    options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())
                );
    
                #region cookieAuthentication
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
                #endregion cookieAuthentication
    
                IdentityRegistrar.Register(services);
                AuthConfigurer.Configure(services, _appConfiguration);
    
                services.AddScoped();
    
                services.AddSignalR();
    
                // Configure Abp and Dependency Injection
                return services.AddAbp(
                    // Configure Log4Net logging
                    options => options.IocManager.IocContainer.AddFacility(
                        f => f.UseAbpLog4Net().WithConfig("log4net.config")
                    )
                );
            }
    

    That's all. Then you can log in a user into the application using only remote services..