Open Closed

How to implement OTP Login using balzor web app and auth server(openiddict) #9900


User avatar
0
mc86 created

HI: I had my customgrant named EmployeeNumberGrant `public class EmployeeNumberGrant : ITokenExtensionGrant { public const string ExtensionGrantName = "employee_number";

public string Name => ExtensionGrantName;

private  IdentityUserManager _userManager;
private  IUserClaimsPrincipalFactory<IdentityUser> _claimsFactory;
private  AbpOpenIddictClaimsPrincipalManager _claimsPrincipalManager;
private AbpSignInManager _signInManager;
public EmployeeNumberGrant(
    IdentityUserManager userManager,
    IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
    AbpOpenIddictClaimsPrincipalManager claimsPrincipalManager)
{
    _userManager = userManager;
    _claimsFactory = claimsFactory;
    _claimsPrincipalManager = claimsPrincipalManager;
    
}

public EmployeeNumberGrant()
{
    

}

public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
{
    _userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>();
    _claimsFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<IdentityUser>>();
    _claimsPrincipalManager = context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>();
    _signInManager= context.HttpContext.RequestServices.GetRequiredService<AbpSignInManager>();
    var empNo = context.Request.GetParameter("employee_number")?.ToString();
    var ts = context.Request.GetParameter("timestamp")?.ToString();
    var signature = context.Request.GetParameter("signature")?.ToString();

    if (string.IsNullOrEmpty(empNo) || string.IsNullOrEmpty(ts) || string.IsNullOrEmpty(signature))
    {
        return new ForbidResult(new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
            new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Missing parameters."
            }));
    }

    // 1. 校验时间戳
    if (!long.TryParse(ts, out var ticks) ||
        DateTime.UtcNow - new DateTime(ticks, DateTimeKind.Utc) > TimeSpan.FromMinutes(5))
    {
        return new ForbidResult(new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
            new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Expired timestamp."
            }));
    }

    // 2. 校验签名
    var raw = $"{empNo}:{ts}";
    if (!VerifySignature(raw, signature, "SuperSecretSharedKey123!"))
    {
        return new ForbidResult(new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
            new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Invalid signature."
            }));
    }

    // 3. 查找用户
    var user = await _userManager.FindByNameAsync(empNo);
    if (user == null)
    {
        return new ForbidResult(new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
            new AuthenticationProperties(new Dictionary<string, string>
            {
                [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
                [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "User not found."
            }));
    }

    // 4. 创建 ClaimsPrincipal
    var principal = await _claimsFactory.CreateAsync(user);

    // 附加自定义 Claim
    ((ClaimsIdentity)principal.Identity!).AddClaim("employee_number", empNo);

    // 设置 scopes
    var scopes = new[] {"profile", "roles", "email", "phone", "offline_access", "master9" }.ToImmutableArray();
    principal.SetScopes(scopes);

    // 关联资源
    var resources = new List<string>();
    await foreach (var resource in context.HttpContext.RequestServices
                       .GetRequiredService<IOpenIddictScopeManager>()
                       .ListResourcesAsync(scopes))
    {
        resources.Add(resource);
    }
    principal.SetResources(resources);
    principal.SetAudiences("Master9");
    // 交给 ABP 内置 ClaimsPrincipalManager 处理(角色、权限等)
    await _claimsPrincipalManager.HandleAsync(context.Request, principal);
    //principal.SetScopes(context.Request.GetScopes());

 
   // await _signInManager.SignInAsync(user, isPersistent: false);
    return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal);
}

private static bool VerifySignature(string raw, string signature, string secretKey)
{
    using var hmac = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(secretKey));
    var hash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(raw));
    var expected = Convert.ToBase64String(hash);
    return expected == signature;
}

}`

now I don't know how to make blazor app login with this grant.


29 Answer(s)
  • User Avatar
    0
    mc86 created

    已共享

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    你i可以使用模版项目和你的自定义代码复现问题吗?

    你的项目太复杂, 使用模版项目会更清晰

    谢谢

  • User Avatar
    0
    mc86 created

    好的.稍等建个测试项目

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    好的

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on October 07, 2025, 05:59