Open Closed

Tokens created via custom grant #8141


User avatar
0
naeem76 created
  • ABP Framework version: v8.3.1
  • UI Type: MVC
  • Database System: EF Core ( PostgreSQL)
  • Tiered (for MVC) or Auth Server Separated (for Angular): no
  • Exception message and full stack trace:
  • Steps to reproduce the issue:

I am currently using ABP 8.3.1 Pro version. There is a bug when trying to use tokens created via a custom grant. The main issue from the logs says:

[16:45:33 DBG] AuthenticationScheme: OpenIddict.Validation.AspNetCore was successfully authenticated. [16:45:33 DBG] Get dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 DBG] Filling dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 WRN] SessionId() claim not found for user: a9a8d44f-c8e3-482f-a870-66e93186d540, log out. [16:45:33 DBG] Remove dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 WRN] The token is no longer valid because the user's session expired. [16:45:33 INF] Authorization failed. These requirements were not met: DenyAnonymousAuthorizationRequirement: Requires an authenticated user.

I am removing the dynamic claim right now with this as a temporary solution.

        context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = false;
        });

But I would prefer to keep them for other flows of my application.

I would prefer that my custom grant is not part of dynamic claims (or it could if you provide some code to create sessions appropriately?) This page mentions that there is a Principal Contributor: https://abp.io/docs/latest/modules/identity/session-management#how-it-works So I tried to get rid of it with this command: options.RemoveEventHandler(OpenIddictValidateIdentitySessionValidationHandler.Descriptor); But I still get the error. Maybe there is a flag I can set on the context to avoid getting my principal attached with a sessionId, but I can’t really debug further because all these packages are in the Pro and not compiled with ILDASM.

Can you please provide the following:

  1. Have a way to turn off session in this token grant
  2. Have a way to create the session appropriately when creating the token

Supporting code

public class MagicTokenHandler
{
    public const string ExtensionGrantName = "magic_token";
    public const string MagicTokenKey = "magic_token";
}

// https://github.com/abpframework/abp/blob/b2878b4d3dca82811a5fc1739dee29cc88669eaa/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs#L49
// https://github.com/abpframework/abp/blob/b2878b4d3dca82811a5fc1739dee29cc88669eaa/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md
public class MagicTokenExtensionGrant(
    IdentityUserManager identityUserManager,
    SignInManager<Volo.Abp.Identity.IdentityUser> signInManager,
    IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser> userClaimsPrincipalFactory,
    AbpOpenIddictClaimsPrincipalManager abpOpenIddictClaimsPrincipalManager,
    IOpenIddictScopeManager openIddictScopeManager
) : ITokenExtensionGrant, IScopedDependency
{
    public readonly string TOKEN_HANDLER = LinkUserTokenProviderConsts.LinkUserTokenProviderName;
    public readonly string TOKEN_PURPOSE = LinkUserTokenProviderConsts.LinkUserLoginTokenPurpose;

    public string Name => MagicTokenHandler.ExtensionGrantName;

    public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
    {
        var magicToken = context.Request.GetParameter(MagicTokenHandler.MagicTokenKey).ToString();

        if (string.IsNullOrEmpty(magicToken) || string.IsNullOrEmpty(context.Request.Username))
        {
            return GenericError;
        }

        var user = await identityUserManager.FindByNameAsync(context.Request.Username);
        if (user == null)
        {
            return GenericError;
        }

        var result = await identityUserManager.VerifyUserTokenAsync(user, TOKEN_HANDLER, TOKEN_PURPOSE, magicToken);
        if (!result)
        {
            return GenericError;
        }

        var principal = await signInManager.CreateUserPrincipalAsync(user);
        var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
        claimsPrincipal.SetScopes(principal.GetScopes());
        claimsPrincipal.SetAudiences("VarScanner");
        claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));

        //For abp version >= 7.3
        await abpOpenIddictClaimsPrincipalManager.HandleAsync(context.Request, principal);

        return new Microsoft.AspNetCore.Mvc.SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
    }

    private async Task<IEnumerable<string>> GetResourcesAsync(ExtensionGrantContext context, ImmutableArray<string> scopes)
    {
        var resources = new List<string>();
        if (!scopes.Any())
        {
            return resources;
        }

        await foreach (var resource in openIddictScopeManager.ListResourcesAsync(scopes))
        {
            resources.Add(resource);
        }

        return resources;
    }

    private ForbidResult GenericError => new ForbidResult(
        new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
        properties: new AuthenticationProperties(new Dictionary<string, string>
        {
            [OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
        }!));
}
Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

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

    hi

    Can you try to get an access token and share the Logs.txt? also share an access token value.

    Thanks

    liming.ma@volosoft.com

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    jean@groovejones.com created

    Hello,

    Access Token:

    eyJhbGciOiJSUzI1NiIsImtpZCI6IkUzMEMxMUU1MDU2MUM5NDNFNzg5NkE2OTM2RTkyQTg2NTc3QTBEMTAiLCJ4NXQiOiI0d3dSNVFWaHlVUG5pV3BwTnVrcWhsZDZEUkEiLCJ0eXAiOiJhdCtqd3QifQ.eyJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDM0My8iLCJleHAiOjE3Mjk2MDk4MzYsImlhdCI6MTcyOTYwNjIzNiwiYXVkIjoiVmFyU2Nhbm5lciIsInNjb3BlIjoib3BlbmlkIiwianRpIjoiZGU1NDIzNjgtN2Y0ZS00MWZjLThiZTEtZmY5YjYyZWE1N2JjIiwic3ViIjoiYTlhOGQ0NGYtYzhlMy00ODJmLWE4NzAtNjZlOTMxODZkNTQwIiwib2lfcHJzdCI6IlZhclNjYW5uZXJfQXBwIiwiY2xpZW50X2lkIjoiVmFyU2Nhbm5lcl9BcHAiLCJvaV90a25faWQiOiIzYTE1YzZiZC05MzU3LWVkNmMtMzQ5Ni1jY2IwOTBjNjA5NmIifQ.4eYHNo8OW1IYIAOMFe8PF3_ITM7bcuRmk6TrXx2Wb8M78_y0MLk5J6j0LE97yUhEtqkePD6MdLgL4cDBJMDONsqpkzQVXmG4-AZVrhp0oWUp4MK6ub3TvCDmFa1U0wN2FdpkUZ7JXRL1K_ix3N3xfOdbJmhgPOueWdFpCGEnHA26oxapd485x5iOPZMdebQ_-iLKAbYngOka53khB_SRH_48Hz47TQ3f44aLikSEAdZqZMpUAELMbc6vlzS56PPSWGsNmmY7tCoOCLXYKHpEVEiOELceRig6F475YWJDBcBOD9YOI_zUTcoADUkmHddeW57n6N39NF19Hr4neaZ4lA
    

    I'm not sure what are the "Logs.txt", but I will send you the output window by email.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    The claims in the access token are:

    {
      "iss": "https://localhost:44343/",
      "exp": 1729609836,
      "iat": 1729606236,
      "aud": "VarScanner",
      "scope": "openid",
      "jti": "de542368-7f4e-41fc-8be1-ff9b62ea57bc",
      "sub": "a9a8d44f-c8e3-482f-a870-66e93186d540",
      "oi_prst": "VarScanner_App",
      "client_id": "VarScanner_App",
      "oi_tkn_id": "3a15c6bd-9357-ed6c-3496-ccb090c6096b"
    }
    

    Can you try to call await abpOpenIddictClaimsPrincipalManager.HandleAsync(context.Request, claimsPrincipal);?

    Make sure the claimsPrincipal is handled by abpOpenIddictClaimsPrincipalManager.

    principal vs claimsPrincipal

    
    var principal = await signInManager.CreateUserPrincipalAsync(user);
    var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
    claimsPrincipal.SetScopes(principal.GetScopes());
    claimsPrincipal.SetAudiences("VarScanner");
    claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));
    
    //For abp version >= 7.3
    await abpOpenIddictClaimsPrincipalManager.HandleAsync(context.Request, claimsPrincipal);
    
    return new Microsoft.AspNetCore.Mvc.SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);```
    
    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    jean@groovejones.com created

    It does indeed solve the issue. I was able to remove the RemoveEvent and re-activate the dynamic claims flag. I copied this code from your documentation here:

    https://github.com/abpframework/abp/blob/b2878b4d3dca82811a5fc1739dee29cc88669eaa/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md

    Maybe it could be updated.

    Thank you for your support.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Thanks, I will update it.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    https://github.com/abpframework/abp/pull/21167

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
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.8.0-preview. Updated on October 05, 2026, 11:32
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.