I am working on an ABP-based tiered application (ABP v8.3.4) and facing an issue while testing client proxies through Swagger UI. I generated the client proxy using a local IIS app with the URL https://10.50.1.39:444/ (a non-tiered, single-layered ABP application).
Whenever I try to call an API via the client proxies, I receive a 401 Unauthorized error. However, API methods decorated with the [AllowAnonymous] attribute are working fine and returning the expected values.
I referred to the following documentation for client proxy generation:
https://abp.io/docs/8.0/API/Static-CSharp-API-Clients
and generated the client proxies in the Application project of the tiered application.
Here I am attaching the request and response.
10 Answer(s)
-
0
Can you check your application logs? There should be log that indicates which action fails for authentication. Then we can take action accordingly
-
0
Can you check your application logs? There should be log that indicates which action fails for authentication. Then we can take action accordingly
Application log :
2025-05-07 15:34:51.830 +05:30 [INF] Request starting HTTP/2 GET https://localhost:44393/api/app/branches-client-proxy/10 - null null 2025-05-07 15:34:51.832 +05:30 [INF] Executing endpoint 'QueuingSystem.Branches.BranchesClientProxy.GetAsync (********App.Application)' 2025-05-07 15:34:51.834 +05:30 [INF] Route matched with {action = "Get", controller = "BranchesClientProxy", area = "", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[QueuingSystem.Branches.BranchDto] GetAsync(Int32) on controller QueuingSystem.Branches.BranchesClientProxy (********App.Application). 2025-05-07 15:34:52.100 +05:30 [INF] Start processing HTTP request GET https://10.50.1.39:444/api/app/branches/10?api-version=1.0 2025-05-07 15:34:52.100 +05:30 [INF] Sending HTTP request GET https://10.50.1.39:444/api/app/branches/10?api-version=1.0 2025-05-07 15:34:52.197 +05:30 [INF] Received HTTP response headers after 97.5304ms - 401 2025-05-07 15:34:52.197 +05:30 [INF] End processing HTTP request after 97.708ms - 401 2025-05-07 15:34:52.209 +05:30 [ERR] ---------- RemoteServiceErrorInfo ---------- { "code": "Unauthorized", "message": "Unauthorized", "details": null, "data": null, "validationErrors": null }
2025-05-07 15:34:52.209 +05:30 [ERR] Unauthorized Volo.Abp.Http.Client.AbpRemoteCallException: Unauthorized at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.ThrowExceptionForResponseAsync(HttpResponseMessage response) at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.RequestAsync(ClientProxyRequestContext requestContext) at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase1.RequestAsync[T](ClientProxyRequestContext requestContext) at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase
1.RequestAsync[T](String methodName, ClientProxyRequestTypeValue arguments) at QueuingSystem.Branches.BranchesClientProxy.GetAsync(Int32 id) in F:\CodeBox\QueuingSystemNew**********App\src**********App.Application\ClientProxies\QueuingSystem\Branches\BranchesClientProxy.Generated.cs:line 40 at lambda_method3807(Closure, Object) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker. -
0
Can you send a sample project that reproduces problem to my E-mail address with this issue number?
enis.necipoglu@volosoft.com
-
0
Can you send a sample project that reproduces problem to my E-mail address with this issue number?
enis.necipoglu@volosoft.com
https://we.tl/t-Q0QHOMB27Y
-
0
Hi,
Here is my first findings:
Client Proxies is used for sending request by using AppService interfaces from Application.Contracts project they tehy should be generated in HttpApi.Client project. In case of need, you'll need to reference HttpApi.Client project of the related project.
When you generate them in the application layer, it replaces the original implementation of the interfaces.
-
0
Hi,
Here is my first findings:
Client Proxies is used for sending request by using AppService interfaces from Application.Contracts project they tehy should be generated in HttpApi.Client project. In case of need, you'll need to reference HttpApi.Client project of the related project.
When you generate them in the application layer, it replaces the original implementation of the interfaces.
Can you explain this in detail? Also, what changes can I make to this project?
-
0
Dear,
I haven’t received a response yet, and I wanted to kindly check if there is any update or if additional information is required from my side. Please let me know the status at your earliest convenience, as this issue is affecting our development timeline.
-
0
Hi,
You have to create client-proxies into
HttpApi.Client
project:Otherwise, you replace the original ApplicationService implementation and it cannot work. Whenever you need to consume by using client-proxies, you'll need to use
HttpApi.Client
andApplication.Contracts
project reference and use interfaces of your AppServices. Whenever you inject that interface, it'll use ClientProxy if the project hasHttpApi.Client
reference. -
0
Thank you for your reply.
As mentioned earlier, I am working on an ABP (v8.3.4) Tiered application, and I need to connect it to another ABP (v8.3.4) Single-layer application hosted on my local IIS at the address: https://10.50.1.39:444/.
Currently, the connection is established using an HttpClient request, where I generate an access token using the following parameters: ClientId, ClientSecret, Scope, GrantType, UserName, and Password.
The main goal is to access the APIs of the client application (https://10.50.1.39:444/) from my server (Tiered) application.
As you mentioned, I have created client proxies in the HttpApi.Client project. And the structure looks like below:
Additionally, I have implemented the same AppServices in both applications, allowing them to operate in a server-client architecture and enabling seamless communication and functionality sharing between the two. However, I am not getting the expected output. Is there anything I might have missed in the configuration or setup? Also, is it possible to achieve the same result using Client Proxies alone, or is additional configuration required?
-
0
Hello @k-s-c,
Thanks for reaching out and providing such a clear description of your setup and the issue you're facing. This "Unauthorized" error in a tiered application with an API Gateway is a classic scenario, and we can definitely help you debug it.
Based on your description, the problem lies in the authentication flow between your Public Gateway and your Host API, specifically concerning JWT token validation when requests are proxied from HTTPS to HTTP.
Let's break down the potential causes and how to diagnose them:
1.
RequireHttpsMetadata = false
(Revisit this carefully!)You've correctly identified
RequireHttpsMetadata = false
on theJwtBearerOptions
as a potential fix for the Host API. This setting is crucial when your IdentityServer (which is your Host API in this case, acting as the token issuer) issues tokens with aniss
(issuer) claim that is an HTTPS URL, but the token is then validated by an API (your Host API, when accessed via the gateway) that receives the request over HTTP.Action:
Verify the
iss
claim: Inspect a JWT token issued by your Host API (you can get one from the working Swagger UI on the Host API, or by logging in from your MVC app). What is the value of theiss
claim? If it'shttps://your-gateway-domain/
, and your Host API is receiving requests overhttp://localhost:port/
, thenRequireHttpsMetadata = false
is indeed necessary on the Host API'sAddJwtBearer
configuration.Double-check placement: Ensure
RequireHttpsMetadata = false
is applied correctly within theConfigureServices
method of your Host API:C#
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://your-gateway-domain/"; // Or your Host API's public URL if it's directly accessible for discovery options.RequireHttpsMetadata = false; // <-- This is the key options.Audience = "YourProjectName_Host"; // Or the audience of your Host API });
Note:
options.Authority
should point to the public URL where the IdentityServer discovery document can be found, which would typically be your Public Gateway's HTTPS URL.
2. Audience Mismatch
Both the Public Gateway (if it validates) and the Host API must be configured to accept the correct audience (
aud
claim) in the JWT token. The audience for your Host API is typically its own unique identifier (e.g.,"YourProjectName_Host"
).Action:
Check
OpenIddict
configuration: In your Host API'sPreConfigureServices
, where you configure OpenIddict (ABP's default IdentityServer), ensure the client configuration for your Web MVC app (and any other clients) requests the correct scope/audience:C#
PreConfigure<OpenIddictBuilder>(builder => { builder.AddValidation(options => { options.AddAudiences("YourApplicationName"); options.UseLocalServer(); options.UseAspNetCore(); }); });
Check client applications: Verify that your Web MVC application (or any client obtaining a token) is requesting the
"YourProjectName_Host"
scope.Check
appsettings.json
in the DbMigrator: ABP layered template has data seeding logic with DbMigrator. Make sure your credentials is correct in theappsettings.json
insideYourProjectName.DbMigrator
folder and then execute DbMigrator once when your credentials ready to publish. ⚠️ The production database should be properly migratedCheck
OpenIddictDataSeedContributor
if any change requiremens: In theDomain
project you'll seeOpenIddict\OpenIddictDataSeedContributor.cs
file that seeds all the openiddict configuration to the database. Check there and make sure all of your clients are added there.
3. Swagger UI and Token Injection
When using Swagger UI on the Public Gateway, the "Authorize" button injects the bearer token. The issue here might be how Swagger on the Gateway handles this, or if the token it's using is somehow different or malformed when forwarded.
Action:
- Inspect Network Calls: Using your browser's developer tools (F12), go to the Network tab on the Swagger UI page of your Public Gateway.
- Check the request headers: When you try to execute an endpoint, look at the
Authorization
header. Is the bearer token present? Is it the correct token? - Check the response: What is the exact response from the gateway? Is it a 401 directly from the gateway, or is it a 401 relayed from the Host API? This distinction is critical for debugging. If the gateway sends 401, the token validation failed at the gateway. If the Host API sends 401, the token validation failed at the Host API after the gateway forwarded it.
- Check the request headers: When you try to execute an endpoint, look at the
4. Logging (Your Best Friend!)
Enable detailed logging on both your Public Gateway and your Host API. Look for logs related to authentication, JWT validation, and authorization.
Action:
Configure Serilog (or default logging): Ensure
appsettings.json
has sufficient logging levels:JSON"Serilog": { "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning", "System": "Warning", "OpenIddict": "Debug", // Very useful for token validation issues "Microsoft.AspNetCore.Authentication": "Debug", // Also very useful "Volo.Abp": "Information" } } }
Examine logs: Look for messages like "token validation failed," "issuer not found," "invalid audience," or "signature validation failed."
Troubleshooting Steps (Order of operations I'd recommend):
- Isolate the Host API: Can you directly call an authenticated endpoint on your Host API (bypassing the Public Gateway) with a valid token obtained from the Host API's own Swagger or by logging in? If this works, the Host API's authentication is correct in isolation.
- Inspect Token from Gateway Swagger: Get a token via the Public Gateway's Swagger "Authorize" button. Paste this token into
jwt.io
.- What are the
iss
(issuer),aud
(audience), andexp
(expiration) claims? - Are the scopes correct?
- What are the
- Network Trace: Use tools like Fiddler, Wireshark, or the browser's developer tools to trace the exact HTTP requests and responses between the Gateway and the Host API. This will show you exactly what headers are being sent (including
Authorization
).
Example Scenario and Solution:
Often, the problem is that the Host API is configured to expect
Authority = "https://public-gateway-domain/"
(because that's where the discovery document is publicly available), but it's receiving requests over HTTP internally (from the gateway). In this case,RequireHttpsMetadata = false
is critical on the Host API'sJwtBearerOptions
.Let me know the results of these checks, especially the network tab analysis and any relevant log messages. With more details, we can narrow down the exact cause.
Best regards,