Search code examples
asp.net-coreauthenticationasp.net-core-webapiidentityserver4access-token

how can I get token from token endpoint without username and password in identityserver4?


I'm using IdentityServer4 for user authentication and authorization in my asp.net core web api.I use this api in android application.My users Signup and Login with username and password with no problem .And here is my access token that I got from connect/token endpoint

{
  "alg": "RS512",
  "typ": "at+jwt"
}
{
  "nbf": 1600324303,
  "exp": 1631860303,
  "iss": "https://myIdentityServerApi.com",
  "aud": [
    "IdentityServerApi",
    "MyAPI1"
  ],
  "client_id": "MyApp1",
  "sub": "521d198c-3657-488e-997e-3e50d756b353",
  "auth_time": 1600324302,
  "idp": "local",
  "role": "Admin",
  "name": "myusername",
  "scope": [
    "openid",
    "IdentityServerApi",
    "MyAPI1"
  ],
  "amr": [
    "pwd"
  ]
}

Now in my new android application I want users signup and login with phone number and sms activation. When User send the ActivationCode I should send him access token.But how can I get token from token endpoint without username and password?

In below I wanted to generate token manually.but generated token don't work.

        [HttpPost("Activate")]
        [AllowAnonymous]
        public async Task<IActionResult> Activate([FromBody] SignUpPhoneModel model)
        {
            if (string.IsNullOrWhiteSpace(model.PhoneNumber))
            {
                return BadRequest("Phone Number is Empty");
            }

            PhoneValidation pv = new PhoneValidation();


            IdentityUser CurrentUser = await db.Users.Where(e => e.PhoneNumber == model.PhoneNumber).FirstAsync();
            if (!await UserManager.VerifyChangePhoneNumberTokenAsync(CurrentUser, model.ActivationCode, model.PhoneNumber))
            {
                return BadRequest("Activation Code is not correct");
            }
            else
            {
                //Here user is activated and should get token But How?
                CurrentUser.PhoneNumberConfirmed = true;
                List<string> UserRoles = (await UserManager.GetRolesAsync(CurrentUser)).ToList();
                var tokenHandler = new JwtSecurityTokenHandler();

                RSACryptoServiceProvider rsap = new RSACryptoServiceProvider(KeyContainerNameForSigning);
                SecurityKey sk = new RsaSecurityKey(rsap.Engine);
                List<Claim> UserClaims = new List<Claim>() {
                    new Claim(JwtRegisteredClaimNames.Sub, CurrentUser.Id),

                };
                foreach (var r in UserRoles)
                {
                    UserClaims.Add(new Claim(JwtClaimTypes.Role, r));
                }
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                     Issuer= "https://myidentityserverapi.com",
                    Audience = "IdentityServerApi,MyAPI",
                     
                    Subject = new ClaimsIdentity(UserClaims),
                    Expires = DateTime.UtcNow.AddDays(365),
                    SigningCredentials = new SigningCredentials(sk, SecurityAlgorithms.RsaSha512),
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                TokenModel tm = new TokenModel()
                {
                    access_token = tokenHandler.WriteToken(token)
                };
                return Ok(tm);
            }
        }

When I recieve token from above(actionvation method) in my application is like below,But It don't work for example User.Identity.IsAuthenticated is false.Does any one know How can I generate token like connect/token endpoint without username and password?

  "alg": "RS256",
  "typ": "JWT"
}

{
  "unique_name": "13f2e130-e2e6-48c7-b3ac-40f8dde8087b",
  "role": "Member",
  "nbf": 1600323833,
  "exp": 1718259833,
  "iat": 1600323833
}

Did I choose the right method? Or I should use another way for example different flow or grant types?


Solution

  • I finally create access token like this:

    [HttpPost("Activate")]
            [AllowAnonymous]
            public async Task<IActionResult> Activate([FromBody] SignUpPhoneModel model, [FromServices] ITokenService TS, [FromServices] IUserClaimsPrincipalFactory<JooyaIdentityUser> principalFactory, [FromServices] IdentityServerOptions options)
            {           
                JooyaIdentityUser CurrentUser = await db.Users.Where(e => e.PhoneNumber == model.PhoneNumber).FirstOrDefaultAsync();
                
                if (!await UserManager.VerifyChangePhoneNumberTokenAsync(CurrentUser, model.ActivationCode, model.PhoneNumber))
                {
                    return BadRequest("Activation Code in not correct");
                }
                CurrentUser.PhoneNumberConfirmed = true;
                await db.SaveChangesAsync();
                await UserManager.UpdateSecurityStampAsync(CurrentUser);
                var Request = new TokenCreationRequest();
                var IdentityPricipal = await principalFactory.CreateAsync(CurrentUser);
                var IdentityUser = new IdentityServerUser(CurrentUser.Id.ToString());
                IdentityUser.AdditionalClaims = IdentityPricipal.Claims.ToArray();
                IdentityUser.DisplayName = CurrentUser.UserName;
                IdentityUser.AuthenticationTime = System.DateTime.UtcNow;
                IdentityUser.IdentityProvider = IdentityServerConstants.LocalIdentityProvider;
                Request.Subject = IdentityUser.CreatePrincipal();
                Request.IncludeAllIdentityClaims = true;
                Request.ValidatedRequest = new ValidatedRequest();
                Request.ValidatedRequest.Subject = Request.Subject;
    
                Request.ValidatedRequest.SetClient(SeedConfig.GetClients().Where(e => e.ClientId == model.ClientId).First());
                List<ApiResource> Apis = new List<ApiResource>();
                Apis.Add(SeedConfig.GetApis().Where(e => e.Name == "IdentityServerApi").First());
                Apis.Add(SeedConfig.GetApis().Where(e => e.Name == model.ApiName).First());
                Request.Resources = new Resources(SeedConfig.GetIdentityResources(), Apis);
                Request.ValidatedRequest.Options = options;
                Request.ValidatedRequest.ClientClaims = IdentityUser.AdditionalClaims;
                var Token = await TS.CreateAccessTokenAsync(Request);
                Token.Issuer = HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value;
                Token.Lifetime = 32000000;
                var TokenValue = await TS.CreateSecurityTokenAsync(Token);
                TokenModel tm = new TokenModel()
                {
                    access_token = TokenValue
                };
                return Ok(tm);
            }