Open Closed

How-to-configure-separate-authentication-based-on-user-type. #8041


User avatar
0
smansuri created
  • ABP Framework version: v8.2.1
  • UI Type: Angular / MVC / Blazor Server
  • Database System: EF Core (MySQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): Auth server separated angular
  • Exception message and full stack trace: NA
  • Steps to reproduce the issue: NA

We have use case where we want to configure authentication scheme based on the user type. if a user is not a backend user/company user but a consumer than we want to configure mobile OTP based authentication and for company/tenant backend user , we want to use user name password authentication. Please suggest how to achieve this.


39 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can debug the code to see why return new ForbidResult

  • User Avatar
    0
    pvala created
        if (!await userManager.VerifyUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth", userToken))
    

    This if condition, it's going inside this if block, which it should not.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    You can check why the code is invalid.

    VerifyUserTokenAsync this is nothing to do with your grant extension

    var token = await UserManager.GenerateUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth");
    await userManager.VerifyUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth", token)
    
  • User Avatar
    0
    pvala created

    Yes, I got the point. But problem what I am dealing with is, earlier I was verifying the code here in this endpoint

    public async Task<VerifyOTPAndSignInResponseDto> VerifyOTPAndSignIn(VerifyOTPAndSignInInput input)
    {
        using (CurrentTenant.Change(input.TenantId))
        {
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                try
                {
                    VerifyOTPResponseDto otpVerification = await VerifyOTP(
                        new VerifyOTPInput()
                        {
                            CountryCode = input.CountryCode,
                            MobileNumber = input.MobileNumber,
                            OTP = input.OTP,
                        });
    
                    if (otpVerification.type == "success")
                    {
                        var existingUser = await UserManager.FindByNameAsync(input.CountryCode + input.MobileNumber);
                        var user = new Volo.Abp.Identity.IdentityUser(GuidGenerator.Create(), input.CountryCode + input.MobileNumber, input.CountryCode + input.MobileNumber + "@example.com", null);
                        user.SetPhoneNumber(input.CountryCode + "-" + input.MobileNumber, true);
    
                        if (existingUser == null)
                        {
                            (await UserManager.CreateAsync(user)).CheckErrors();
                            using (CurrentTenant.Change(null))
                            {
                                (await UserManager.AddToRoleAsync(user, "Patient")).CheckErrors();
                            }
                            await UsersAppService.PasswordlessUserRegistration(new PasswordlessUserRegistration(input.CountryCode, input.MobileNumber, user.Id));
                        }
                        else
                        {
                            user = existingUser;
                        }
    
                        var token = await UserManager.GenerateUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth");
    
    // Here I was validating the code
                        //var isValid = await UserManager.VerifyUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth", token);
                        //if (!isValid)
                        //{
                        //    throw new UnauthorizedAccessException("The token " + token + " is not valid for the user " + user.Id);
                        //}
    
                        await UserManager.UpdateSecurityStampAsync(user);
                        await unitOfWork.CompleteAsync();
                        await SignInManager.SignInAsync(user, isPersistent: true);
                        return
                            new VerifyOTPAndSignInResponseDto()
                            {
                                message = otpVerification.message,
                                type = otpVerification.type,
                                token = token
                            };
                    }
                    else
                    {
                        return new VerifyOTPAndSignInResponseDto()
                        {
                            message = otpVerification.message,
                            type = otpVerification.type
                        };
                    }
                }
                catch (Exception e)
                {
                    await unitOfWork.RollbackAsync();
                    unitOfWork.Dispose();
                    throw;
                }
            }
        }
    }
    

    There it was successfully validating it, but when I moved that to the grant extension class, there it does not validate it.

  • User Avatar
    0
    pvala created

    In short, this line of code

    await UserManager.VerifyUserTokenAsync(user, "PasswordlessLoginProvider", "passwordless-auth", token)

    returns true in the controller endpoint, but returns false in the grant extension class, that's the issue.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you prepare a minimal project to reproduce the problem and share it?

    liming.ma@volosoft.com

    Thanks.

  • User Avatar
    0
    pvala created

    Hi, I have given you the access to the git repository : https://github.com/perquantum/passwordless-authentication

    Here I have put the sample project. Here's what you need to do.

    Run the AuthServer project (or the whole solution) In postman, hit this api endpoint,

    In response, you will get { "token": "6 digit token", "userId": "User Id of the user" }

    You need to take these, token and userid values and hit one more API as follows :

    In response, the user should be validated. This is the thing we want to achieve.

  • User Avatar
    0
    pvala created

    Also, we are using the MySQL as database, so I have updated the changes in the project accordingly. You can pull the latest changes and try.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    ok, I will check it today.

    Thanks.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you try to remove the await UserManager.UpdateSecurityStampAsync(user); and try again?


    Also, we are using the MySQL as database

    The connection strings are still SQL server.

  • User Avatar
    0
    pvala created

    Hi, that worked. I was able to the access_token. But using that access_token, if I try to get the details of the application configuration, I should get the details of the currentUser right? But I am not getting the details there.

    I also parsed the token on https://jwt.io/ the details are coming in as correctly there. But when tried to call the application-configuration api, it doesn't fetch the currentUser details.

    And this is the error we are getting from logs :

    Failed to validate the token. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10206: Unable to validate audience. The 'audiences' parameter is empty. at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateTokenPayloadAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) at Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) [08:06:39 INF] Bearer was not authenticated. Failure message: IDX10206: Unable to validate audience. The 'audiences' parameter is empty. [08:06:39 INF] Executing endpoint 'Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController.GetAsync (Volo.Abp.AspNetCore.Mvc)'

    Can you help.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Try to add scope to connect/token request

    Global search the _scopeManager.CreateAsync in your solution.

    await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor {
        Name = "MyProjectName", DisplayName = "MyProjectName API", Resources = { "MyProjectName" }
    });
    
  • User Avatar
    0
    pvala created

    Hi, we have added all the required scope for the client, and after adding the scope, when we hit the connect/token API, we got this result :

    Response :

    { "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQwMzkyQUM4QzU2MzZGQTRDQTEzN0Y3NUE0MTI0OUU1MjZBNzU4RjgiLCJ4NXQiOiJRRGtxeU1WamI2VEtFMzkxcEJKSjVTYW5XUGciLCJ0eXAiOiJhdCtqd3QifQ.eyJpc3MiOiJodHRwczovL2Rldi5kb2N0cnouaW46NDQzMjIvIiwiZXhwIjoxNzMxNDA4OTc5LCJpYXQiOjE3MzE0MDUzNzksInNjb3BlIjoib3BlbmlkIiwianRpIjoiODQ2NTgzZjMtYjY5ZC00MmMwLTkzNzQtYjU3ZjgyMjk3YWE1Iiwic3ViIjoiM2ExNjFkZTYtMzk2NC1hNzg3LTRkNmEtOTg3MGRlZjcxNjE5IiwicHJlZmVycmVkX3VzZXJuYW1lIjoiKzkxOTYzODgyODUzMCIsImVtYWlsIjoiKzkxOTYzODgyODUzMEBleGFtcGxlLmNvbSIsInJvbGUiOiJQYXRpZW50IiwicGhvbmVfbnVtYmVyIjoiKzkxLTk2Mzg4Mjg1MzAiLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOiJUcnVlIiwiZW1haWxfdmVyaWZpZWQiOiJGYWxzZSIsInNlc3Npb25faWQiOiI3MWY1NWM3Yy1mZTc5LTQ5NmYtODUxMS05MTNlYTg1NTc4MzIiLCJ1bmlxdWVfbmFtZSI6Iis5MTk2Mzg4Mjg1MzAiLCJvaV9wcnN0IjoicmVhY3RuYXR2ZUFwcF9Nb2JpbGUiLCJjbGllbnRfaWQiOiJyZWFjdG5hdHZlQXBwX01vYmlsZSIsIm9pX3Rrbl9pZCI6IjNhMTYzMWZhLTUxMWUtYmNiOS1lYTk0LTI1YWMwMTY0YjhiOSJ9.RCuBvzgy4XksHbFepPAPra-6RUgvHVEvNyn3NiOo47HwJJC4cRks7yfPH6Rvbj3BAf7DZYb9dk63UZLtJPVSCEJpG3xlOt6mx8zK4A6jxrh2nTCJI3MiOgIrsV99K02HqBfBz0kleXja4S8D7MPYAWyXrMOoeNQv6I4bNg9zRDHMh3viyLzkYHz_v6a8C059QplYEnJ9uUNQyiXx-aQGhN-pPbR_bo5PQOSwUdjsOYdvvWp6Tt_rT7V-O98otTigwfaTHSbmSb6C7QJ9VmcyF5D7F0Ll4C6FuraX74kG69wpjMevwP8_Dp_KtxsRTmD9RE4kJfv9kTbiQExEfJmrIQ", "token_type": "Bearer", "expires_in": 3599, "scope": "openid", "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQwMzkyQUM4QzU2MzZGQTRDQTEzN0Y3NUE0MTI0OUU1MjZBNzU4RjgiLCJ4NXQiOiJRRGtxeU1WamI2VEtFMzkxcEJKSjVTYW5XUGciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2Rldi5kb2N0cnouaW46NDQzMjIvIiwiZXhwIjoxNzMxNDA2NTc5LCJpYXQiOjE3MzE0MDUzNzksImF1ZCI6InJlYWN0bmF0dmVBcHBfTW9iaWxlIiwic3ViIjoiM2ExNjFkZTYtMzk2NC1hNzg3LTRkNmEtOTg3MGRlZjcxNjE5Iiwic2Vzc2lvbl9pZCI6IjcxZjU1YzdjLWZlNzktNDk2Zi04NTExLTkxM2VhODU1NzgzMiIsImF6cCI6InJlYWN0bmF0dmVBcHBfTW9iaWxlIiwiYXRfaGFzaCI6Ijd4U1V0VkxCRkw2bEM1SkxFNDYtSXciLCJvaV90a25faWQiOiIzYTE2MzFmYS01MTJlLWNlZjUtYjFlYi05MzcxODZkNWFlNWUifQ.Vbl0-9tk9oiJpEye-mlWMBiNDhtYgZ_OOu07Rcx3eMT62xTsJoye5XscH15lIpUG0ixs5pAukcnAXKQ5u48YSWL8xgd0oRFAEIrr0d3NjdCvtW95VwVyRbZW7WHKQC08pLJL9gk-LE0dkCzWcohQjHBXi_QgI627i-xIzfX4I0BBLLBWiDxTEguV1UJ474uwiJrBjDbJWmRZWV2myRGi7NA-ZaWdi7KDWlLhW31DUOJJOnFgYphQWIA_Wm3cO49GGMdBqt7aAoWxy3N3xjExEr-pOKyGvcee6sBQ7sDubaOjljNV_Gqp463OLpu1KcRd57Hmd80bIt9qkHvo4waU4A" }

    And after that when we passed this token in the application-configuration API endpoint, it still had the same response as earlier.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    The scope in your access_token only have openid

    Please update your code as below:

    var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser>>();
    
    var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
    
    //Remove
    -claimsPrincipal.SetScopes(claimsPrincipal.GetScopes());
    -claimsPrincipal.SetResources(await GetResourcesAsync(context, claimsPrincipal.GetScopes()));
    
    /Add
    +claimsPrincipal.SetScopes(request.GetScopes());
    +claimsPrincipal.SetResources(await GetResourcesAsync(context, request.GetScopes()));
    
    
    await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>().HandleAsync(context.Request, claimsPrincipal);
    
    return new Microsoft.AspNetCore.Mvc.SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
    

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11