Activities of "s4lv0"

I'm sorry. I changed the default AuthorizationEndpoint on *AppHttpApiHostModule,cs to "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" for a multitenant app

Hi, the unit test confirm my error: EntityId is null-

Please add the following check here on the update operation && !x.EntityChanges[1].EntityId.IsNullOrEmpty()

You will have the following test result:

Volo.Abp.Auditing.Auditing_Tests.Should_Write_AuditLog_For_ValueObject_Entity Source: Auditing_Tests.cs line 366 Duration: 9,8 sec Message:  NSubstitute.Exceptions.ReceivedCallsException : Expected to receive a call matching: SaveAsync(x => ((((((((((x.EntityChanges.Count == 2) AndAlso (Convert(x.EntityChanges.get_Item(0).ChangeType, Int32) == 1)) AndAlso (x.EntityChanges.get_Item(0).EntityTypeFullName == Volo.Abp.Auditing.App.Entities.AppEntityWithValueObject.FullName)) AndAlso (Convert(x.EntityChanges.get_Item(1).ChangeType, Int32) == 1)) AndAlso (x.EntityChanges.get_Item(1).EntityTypeFullName == Volo.Abp.Auditing.App.Entities.AppEntityWithValueObjectAddress.FullName)) AndAlso (x.EntityChanges.get_Item(1).PropertyChanges.Count == 1)) AndAlso (x.EntityChanges.get_Item(1).PropertyChanges.get_Item(0).PropertyName == "Country")) AndAlso (x.EntityChanges.get_Item(1).PropertyChanges.get_Item(0).OriginalValue == ""England"")) AndAlso (x.EntityChanges.get_Item(1).PropertyChanges.get_Item(0).NewValue == ""Germany"")) AndAlso Not(x.EntityChanges.get_Item(1).EntityId.IsNullOrEmpty()))) Actually received no matching calls. Received 2 non-matching calls (non-matching arguments indicated with '*' characters): SaveAsync(*AUDIT LOG: [---: -------] - UserName - UserId : - - ClientIpAddress : - ExecutionDuration : 332 - Entity Changes: - [Created] Volo.Abp.Auditing.App.Entities.AppEntityWithValueObjectAddress, Id = AppEntityWithValueObjectId: -> "08d140ca-5b9f-4f28-a797-b7d3b77b1522" Country: -> "England" - [Updated] Volo.Abp.Auditing.App.Entities.AppEntityWithValueObject, Id = 08d140ca-5b9f-4f28-a797-b7d3b77b1522 Name: "test name" -> "test name 2" - [Deleted] Volo.Abp.Auditing.App.Entities.AppEntityWithValueObjectAddress, Id = AppEntityWithValueObjectId: "08d140ca-5b9f-4f28-a797-b7d3b77b1522" -> Country: "USA" -> *) SaveAsync(*AUDIT LOG: [---: -------] - UserName - UserId : - - ClientIpAddress : - ExecutionDuration : 59 - Entity Changes: - [Updated] Volo.Abp.Auditing.App.Entities.AppEntityWithValueObject, Id = 08d140ca-5b9f-4f28-a797-b7d3b77b1522 - [Updated] Volo.Abp.Auditing.App.Entities.AppEntityWithValueObjectAddress, Id = Country: "England" -> "Germany" *) Stack Trace:  ReceivedCallsExceptionThrower.Throw(ICallSpecification callSpecification, IEnumerable`1 matchingCalls, IEnumerable`1 nonMatchingCalls, Quantity requiredQuantity) CheckReceivedCallsHandler.Handle(ICall call) Route.Handle(ICall call) CallRouter.Route(ICall call) CastleForwardingInterceptor.Intercept(IInvocation invocation) AbstractInvocation.Proceed() ProxyIdInterceptor.Intercept(IInvocation invocation) AbstractInvocation.Proceed() ObjectProxy.SaveAsync(AuditLogInfo auditInfo) Auditing_Tests.Should_Write_AuditLog_For_ValueObject_Entity() line 412 --- End of stack trace from previous location ---

Hi,

I'm writing to bring your attention to an issue with the auditing process in the Volo.Abp.Auditing module. I've observed that the auditing process does not properly support value objects, and I would like to discuss a specific problem I've encountered.

Through a test, I've identified that the error in inserting data into the EntityChanges table is due to the fact that the EntityId is null, as evidenced by the test results.

In summary, I'm raising a concern with the Volo.Abp.Auditing module, as it currently fails to track changes to the properties of value objects. This issue stems from an error in inserting data into the EntityChanges table, which does not allow null values in the EntityId column.

I would appreciate your guidance on a potential solution to this problem. Your assistance in resolving this issue would be greatly appreciated.

Hi, the problem is only on account module. The app now is deployed on nginx using the dynamic-env.json All modules are using the right endpoint apis->default->url instead the account module use the localhost.

{
  "production": true,
  "application": {
    "baseUrl":"https://etsapp-ui-dev.mydomain.it",
    "name": "App",
    "logoUrl": ""
  },
  "oAuthConfig": {
    "issuer": "https://etsapp-api-dev.mydomain.it/",
    "redirectUri": "https://etsapp-ui-dev.mydomain.it",
    "clientId": "App_App",
    "responseType": "code",
    "scope": "offline_access openid profile email phone App"
   },
  "apis": {
    "default": {
      "url": "https://etsapp-api-dev.mydomain.it",
      "rootNamespace": "App"
      }
  }
}

The missing section in dynamic-env.json was AbpAccountPublic. We solve adding the following section inside the file.

{
  "production": true,
  "application": {
    "baseUrl":"http://localhost:4200",
    "name": "App",
    "logoUrl": ""
  },
  "oAuthConfig": {
    "issuer": "https://localhost:44391/",
    "redirectUri": "http://localhost:4200",
    "clientId": "App_App",
    "responseType": "code",
    "scope": "offline_access openid profile email phone App"
  },
  "apis": {
    "default": {
      "url": "https://localhost:44391",
      "rootNamespace": "App"
    },
    "AbpAccountPublic": {
      "url": "https://localhost:44391/",
      "rootNamespace": "AbpAccountPublic"
    }
  }
}

Hi liangshiwei, I had already tried, I added the following reference to the .EntityFrameworkCore.Tests.csproj <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.8" />

It is not clear hot to "Add a custom claim to the access token (recommended)".

Can I use the extra property on [OpenIddictApplications] /openiddict/Applications ? I add the following code ObjectExtensionManager.Instance.Modules() .ConfigureOpenIddict(auth => { auth.ConfigureApplication(application => { //voglio impostare la maxlenght della property application.AddOrUpdateProperty<string>( ExtraPropertyConsts.TourOperatorCodePropertyName, property => { property.DefaultValue = string.Empty; property.Attributes.Add(new MaxLengthAttribute(ExtraPropertyConsts.MaxTourOperatorCodeLength)); // Imposta la lunghezza massima property.Attributes.Add(new DataTypeAttribute(DataType.Text)); }); }); });

In which project should I define TourOperatorClaimDestinationsHandler?

The extra property on OpenIddictApplication does not appear in the Angular UI.

[AI-Bot] said: Yes — using an extra property on OpenIddictApplication is a good place to store your Tour Operator code, and your ObjectExtensionManager.Instance.Modules().ConfigureOpenIddict(... ConfigureApplication ...) approach is valid for persisting that metadata.

But that alone does not automatically put it into the access token. You must do both:

  1. Store the value on the OpenIddict application (as you’re doing)
  2. Copy it into a claim when tokens are issued, and mark that claim’s destination as AccessToken

Below is a complete, ABP-supported way to do step (2).


1) Keep your OpenIddict application extra property (what you already did)

Your extension config is fine for adding an extra property to the OpenIddict application entity.

Then you set the value for each client (Tour Operator) in /openiddict/Applications UI or via code.


2) Add the extra property value into the access token as a custom claim

2.1 Create a claim destinations handler (ABP documented hook)

Create a handler that ensures your custom claim is actually included in the access token (otherwise OpenIddict may drop it depending on destinations).

using System.Threading.Tasks; 
using OpenIddict.Abstractions; 
using Volo.Abp.DependencyInjection; 
using Volo.Abp.OpenIddict; 
 
public class TourOperatorClaimDestinationsHandler 
    : IAbpOpenIddictClaimsPrincipalHandler, ITransientDependency 
{ 
    public Task HandleAsync(AbpOpenIddictClaimsPrincipalHandlerContext context) 
    { 
        foreach (var claim in context.Principal.Claims) 
        { 
            if (claim.Type == ExtraPropertyConsts.TourOperatorCodePropertyName) 
            { 
                claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken); 
            } 
        } 
 
        return Task.CompletedTask; 
    } 
} 

Register it:

Configure<AbpOpenIddictClaimsPrincipalOptions>(options => 
{ 
    options.ClaimsPrincipalHandlers.Add<TourOperatorClaimDestinationsHandler>(); 
}); 

2.2 Add the claim to the principal during token issuance

You now need to add the claim value itself to the principal when the token is being created.

ABP’s OpenIddict docs recommend using claims principal handlers to control token claim destinations, but adding the claim value is typically done by contributing to the principal generation pipeline (implementation detail depends on whether you issue tokens for users or clients).

For client_credentials (machine-to-machine), you can add the claim during OpenIddict’s sign-in processing using an OpenIddict server event handler (this is also a known ABP approach in support answers for token claim customization).

Conceptually:

  • Determine the client application (client_id)
  • Load its extra property TourOperatorCode
  • Add it as a claim onto the principal that becomes the access token principal

(Your storage choice—extra property on OpenIddictApplication—is fine; you’ll just need to load it via OpenIddict’s application manager or your own repository/table.)


3) Enforce filtering in your API using the claim

Once the access token contains TourOperatorCode, you can filter:

var tourOperatorCode = CurrentUser.FindClaimValue(ExtraPropertyConsts.TourOperatorCodePropertyName); 
// apply query restriction based on tourOperatorCode 

This makes the filter non-bypassable (because it is derived from the authenticated client identity, not request parameters).


Sources (validated)

  • https://abp.io/docs/latest/modules/openiddict#updating-claims-in-access_token-and-id_token (ABP: IAbpOpenIddictClaimsPrincipalHandler, destinations, registration)
  • https://documentation.openiddict.com/configuration/claim-destinations.html (OpenIddict claim destinations)

This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

can you suggest the implementation for 2.2

Hi @maliming, thank you for your feedback.

The final solution is the following:

1️⃣ OpenIddict – Custom Claims Handler

Implemented TourOperatorClientCredentialsClaimsHandler and updated InventoryHttpApiHostModule to inject the claim into JWT tokens:

public class TourOperatorClientCredentialsClaimsHandler :
    IOpenIddictServerHandler<OpenIddictServerEvents.ProcessSignInContext>,
    ITransientDependency
{
    private readonly IOpenIddictApplicationManager _applicationManager;
    private readonly ILogger<TourOperatorClientCredentialsClaimsHandler> _logger;
    public static OpenIddictServerHandlerDescriptor Descriptor { get; }
        = OpenIddictServerHandlerDescriptor.CreateBuilder<OpenIddictServerEvents.ProcessSignInContext>()
            .UseScopedHandler<TourOperatorClientCredentialsClaimsHandler>()
            .SetOrder(int.MinValue + 100_000)
            .SetType(OpenIddictServerHandlerType.Custom)
            .Build();

    public TourOperatorClientCredentialsClaimsHandler(
        IOpenIddictApplicationManager applicationManager,
        ILogger<TourOperatorClientCredentialsClaimsHandler> logger)
    {
        _applicationManager = applicationManager;
        _logger = logger;
    }

    public async ValueTask HandleAsync(OpenIddictServerEvents.ProcessSignInContext context)
    {
        // Check if this is a client_credentials grant
        if (context.Request?.GrantType != OpenIddictConstants.GrantTypes.ClientCredentials)
        {
            return;
        }

        var clientId = context.Request.ClientId;
        if (string.IsNullOrEmpty(clientId))
        {
            _logger.LogWarning("No client_id found in request");
            return;
        }

        _logger.LogInformation($"Processing client_credentials sign-in for client: {clientId}");

        // Load application by client_id
        var application = await _applicationManager.FindByClientIdAsync(clientId, context.CancellationToken);
        if (application == null)
        {
            _logger.LogWarning($"Application not found for client_id: {clientId}");
            return;
        }

        // Read your extra property from the OpenIddict application "Properties" JSON
        var properties = await _applicationManager.GetPropertiesAsync(application, context.CancellationToken);
        
        _logger.LogInformation($"Application properties count: {properties.Count}");
        
        if (!properties.TryGetValue(ExtraPropertyConsts.TourOperatorCodeClaimName, out var tourOperatorCodeElement))
        {
            _logger.LogWarning($"No {ExtraPropertyConsts.TourOperatorCodeClaimName} property found for client: {clientId}");
            return;
        }

        string? tourOperatorCode = null;
        if (tourOperatorCodeElement.ValueKind == JsonValueKind.String)
        {
            tourOperatorCode = tourOperatorCodeElement.GetString();
        }

        if (string.IsNullOrWhiteSpace(tourOperatorCode))
        {
            _logger.LogWarning($"Empty {ExtraPropertyConsts.TourOperatorCodeClaimName} property for client: {clientId}");
            return;
        }

        _logger.LogInformation($"Found tour_operator_code: '{tourOperatorCode}' for client: {clientId}");

        // Add the claim to the principal
        var identity = context.Principal?.Identity as ClaimsIdentity;
        if (identity == null)
        {
            _logger.LogError($"No identity found in principal for client: {clientId}");
            return;
        }

        // Create the claim with explicit destinations set to AccessToken
        var claim = new Claim(ExtraPropertyConsts.TourOperatorCodeClaimName, tourOperatorCode);
        claim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
        
        identity.AddClaim(claim);
        
        _logger.LogInformation($"Successfully added {ExtraPropertyConsts.TourOperatorCodeClaimName} claim with value '{tourOperatorCode}' and AccessToken destination for client: {clientId}");
    }
}
PreConfigure<OpenIddictServerBuilder>(builder =>
{
    builder.AddEventHandler(TourOperatorClientCredentialsClaimsHandler.Descriptor);
});

2️⃣ Claim-based Filtering Infrastructure

Added ITourOperatorCodeFilter interface

Added TourOperatorCodeFilterProvider

Added ExtraPropertyConsts for claim-based filtering logic

public class TourOperatorCodeFilterProvider : ITourOperatorCodeFilter, ITransientDependency
{
    private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;

    public TourOperatorCodeFilterProvider(ICurrentPrincipalAccessor currentPrincipalAccessor)
    {
        _currentPrincipalAccessor = currentPrincipalAccessor;
    }

    public string? TourOperatorCode
    {
        get
        {
            var principal = _currentPrincipalAccessor.Principal;
            if (principal == null || principal.Identity?.IsAuthenticated != true)
            {
                return null;
            }

            var claim = principal.Claims?.FirstOrDefault(c => c.Type == ExtraPropertyConsts.TourOperatorCodeClaimName);
            return claim?.Value;
        }
    }

    public bool IsEnabled => !string.IsNullOrWhiteSpace(TourOperatorCode);
}

3️⃣ Automatic Query Filtering

Introduced BlockspaceQueryFilterExtensions

Updated the repository to automatically apply the filter to all Blockspace queries

public override async Task<IQueryable<Blockspace>> WithDetailsAsync()
{
    var query = await base.WithDetailsAsync();
    return query.ApplyTourOperatorCodeFilter(_dataFilter, _tourOperatorCodeFilter);
}

public override async Task<IQueryable<Blockspace>> GetQueryableAsync()
{
    var query = await base.GetQueryableAsync();
    return query.ApplyTourOperatorCodeFilter(_dataFilter, _tourOperatorCodeFilter);
}

🔜 Remaining Task

The only thing missing is the UI in Angular for managing OpenIddict Application Properties.

Showing 1 to 10 of 11 entries
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.3.0-preview. Updated on March 06, 2026, 09:11
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.