I have an existing module project that I have built to be a remote service. I am creating a web application project to be the main site. The setup I am going for is the web project has the web application and Identity Server setup, and the module project acts as a remote API that the web project accesses.
My module is in a separate solution and runs via a host project (the one that is generated by the CLI tool). I have removed Identity Server from this solution and would like the module to rely on the Identity Server host in my web project solution. The module handles its own migrations through the host project and has a separate DB from the web project.
For some reason I am struggling to get the dynamic C# client for the module to work in the web project across an authorized API call. I have no problems using it with unauthorized API endpoints. It is giving me a 401 unauthorized response when I attempt to access authorized endpoints. Below I have detailed the setup from a blank project of my web application in hopes of finding where I am going wrong.
Below is my process to create this issue:
- Using ABP Suite 2.6.2, I generated a new solution using MVC, no mobile, EF Core, and non-tiered.
- I ran the DbMigrator project to populate and setup the database. Runs successfully and I can confirm the database is populated.
- Added a reference for RemoteServices in the web project appsettings.json to point at my module project.
"RemoteServices": {
"Devices": {
"BaseUrl": "https://localhost:44375"
}
},
- Added references in the dependencies of the web project for the module dlls for:
- Application.Contracts
- Domain.Shared
- HttpApi.Client
- Added a test page for accessing the remote API. I’m attempting to pull a single device with the identifier “string” from the remote API and displaying it on the page. The remote endpoint has an [Authorized] tag on it. If I remove the [Authorized] tag the call works, and I see the information on the page. If I have the tag it throws a 401 status response and doesn’t display the page.
Devices.cshtml
@page
@model NowMicro.Dice.Portal.Web.Pages.DevicesModel
<h2>Device Test</h2>
@{ var device = Model.Device; }
<p>Serial Number: </p>
@device.SerialNumber;
DevicesModel.cs
using Microsoft.AspNetCore.Authorization;
using NowMicro.Dice.Devices.Authorization;
using NowMicro.Dice.Devices.DtoModels;
using NowMicro.Dice.Devices.ServiceInterfaces;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace NowMicro.Dice.Portal.Web.Pages
{
public class DevicesModel : AbpPageModel
{
public DeviceDto Device { get; set; }
private readonly IDevicesAppService _devicesAppService;
private readonly IAuthorizationService _authorization;
public DevicesModel(IDevicesAppService devicesAppService, IAuthorizationService authorization)
{
_devicesAppService = devicesAppService;
_authorization = authorization;
}
public async Task OnGetAsync()
{
if (!await _authorization.IsGrantedAsync(DevicesPermissions.Devices.Default))
{
Redirect("/");
}
Device = await _devicesAppService.GetAsync("string");
}
}
}
- In the WebModule.cs file in the web project I added an addition to the DependsOn tag at the top for:
typeof(DevicesHttpApiClientModule)
which is the HttpApiClient module for the remote API module. - Added a nuget reference to Volo.Abp.Http.Client in the web project to fix this error:
[FTL] Host terminated unexpectedly!
System.IO.FileNotFoundException: Could not load file or assembly 'Volo.Abp.Http.Client, Version=2.6.2.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
- I run the project and in Identity Management > Users I give the admin user permissions to the remote API permission set.
- In the Identity Server > Api Resources section I created a new resource for the remote API.
- Name: Devices
- Display Name: Devices API
- Owned Claims: sub, name, email, role
- In the module appsettings.json I make sure the Authority url is that of the web project.
- I attempt to go to the devices page while both module and web projects are running and I get an unauthorized result as found in the web project logs:
2020-05-05 13:17:23.894 -05:00 [INF] Executed endpoint '/Devices'
2020-05-05 13:17:23.903 -05:00 [DBG] Added 0 entity changes to the current audit log
2020-05-05 13:17:23.903 -05:00 [DBG] Added 0 entity changes to the current audit log
2020-05-05 13:17:23.904 -05:00 [INF] Request finished in 4369.7582ms 500 application/json; charset=utf-8
2020-05-05 13:17:52.985 -05:00 [INF] Request starting HTTP/2.0 GET https://localhost:44353/devices
2020-05-05 13:17:23.894 -05:00 [INF] Executed page /Devices in 4347.7692ms
2020-05-05 13:17:52.988 -05:00 [INF] Executing endpoint '/Devices'
2020-05-05 13:17:52.989 -05:00 [INF] Route matched with {page = "/Devices", controller = "", area = "", action = ""}. Executing page /Devices
2020-05-05 13:17:52.991 -05:00 [INF] Executing handler method NowMicro.Dice.Portal.Web.Pages.DevicesModel.OnGetAsync - ModelState is "Valid"
2020-05-05 13:17:52.991 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:U,pk:5a86407f-aa6f-3eab-147a-39f4f7773384,n:Devices.Device
2020-05-05 13:17:52.991 -05:00 [DBG] Found in the cache: pn:U,pk:5a86407f-aa6f-3eab-147a-39f4f7773384,n:Devices.Device
2020-05-05 13:17:52.991 -05:00 [DBG] PermissionStore.GetCacheItemAsync: pn:R,pk:admin,n:Devices.Device
2020-05-05 13:17:52.991 -05:00 [DBG] Found in the cache: pn:R,pk:admin,n:Devices.Device
2020-05-05 13:17:52.992 -05:00 [INF] Authorization was successful.
2020-05-05 13:17:52.992 -05:00 [INF] Start processing HTTP request GET "https://localhost:44375/api/abp/api-definition"
2020-05-05 13:17:52.992 -05:00 [INF] Sending HTTP request GET "https://localhost:44375/api/abp/api-definition"
2020-05-05 13:17:53.244 -05:00 [INF] Received HTTP response after 248.9889ms - "OK"
2020-05-05 13:17:53.244 -05:00 [INF] End processing HTTP request after 252.0985ms - "OK"
2020-05-05 13:17:53.283 -05:00 [INF] Start processing HTTP request GET "https://localhost:44375/api/devices?serialNumber=string&api-version=1.0"
2020-05-05 13:17:53.293 -05:00 [INF] Sending HTTP request GET "https://localhost:44375/api/devices?serialNumber=string&api-version=1.0"
2020-05-05 13:17:53.366 -05:00 [INF] Received HTTP response after 73.2875ms - "Unauthorized"
2020-05-05 13:17:53.370 -05:00 [INF] End processing HTTP request after 86.7997ms - "Unauthorized"
2020-05-05 13:17:53.452 -05:00 [ERR] ---------- RemoteServiceErrorInfo ----------
2020-05-05 13:17:53.452 -05:00 [ERR] {
"code": null,
"message": "An internal error occurred during your request!",
"details": null,
"validationErrors": null
}
2020-05-05 13:17:53.452 -05:00 [ERR] Remote service returns error! HttpStatusCode: Unauthorized, ReasonPhrase: Unauthorized
Volo.Abp.AbpException: Remote service returns error! HttpStatusCode: Unauthorized, ReasonPhrase: Unauthorized
at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.ThrowExceptionForResponseAsync(HttpResponseMessage response)
at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.MakeRequestAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.MakeRequestAndGetResultAsync[T](IAbpMethodInvocation invocation)
at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.GetResultAsync(Task task, Type resultType)
at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at NowMicro.Dice.Portal.Web.Pages.DevicesModel.OnGetAsync() in C:\Users\hilto\Source\repos\DICE\Modules\NowMicro.Dice.Portal\src\NowMicro.Dice.Portal.Web\Pages\Devices.cshtml.cs:line 29
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.NonGenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
2020-05-05 13:17:53.453 -05:00 [INF] Executing ObjectResult, writing value of type 'Volo.Abp.Http.RemoteServiceErrorResponse'.
2020-05-05 13:17:53.454 -05:00 [INF] Executed page /Devices in 464.5444ms
2020-05-05 13:17:53.454 -05:00 [INF] Executed endpoint '/Devices'
2020-05-05 13:17:53.463 -05:00 [DBG] Added 0 entity changes to the current audit log
2020-05-05 13:17:53.463 -05:00 [DBG] Added 0 entity changes to the current audit log
2020-05-05 13:17:53.463 -05:00 [INF] Request finished in 477.7475ms 500 application/json; charset=utf-8
Here are the coinciding logs from the module host:
2020-05-05 13:17:53.301 -05:00 [INF] Request starting HTTP/1.1 GET https://localhost:44375/api/devices?serialNumber=string&api-version=1.0
2020-05-05 13:17:53.302 -05:00 [DBG] AuthenticationScheme: Bearer was not authenticated.
2020-05-05 13:17:53.309 -05:00 [INF] Authorization failed.
2020-05-05 13:17:53.364 -05:00 [INF] AuthenticationScheme: BearerIdentityServerAuthenticationJwt was challenged.
2020-05-05 13:17:53.365 -05:00 [INF] AuthenticationScheme: Bearer was challenged.
2020-05-05 13:17:53.365 -05:00 [INF] Request finished in 63.8403ms 401
I’ve had no luck in figuring out why I continue to get unauthorized results. Any help in deciphering this is much appreciated. Let me know if you need any more information from me.
14 Answer(s)
-
0
Hi @hiltond ,
When you log in and navigate to the devices page, what is the value of the
CurrentUser
property? -
0
Thanks for getting back, here is what I see:
| Name | Value | | --- | --- | | Email | "admin@abp.io" | | EmailVerified | false | | Id | {5a86407f-aa6f-3eab-147a-39f4f7773384} | | IsAuthenticated | true | | PhoneNumber | null | | PhoneNumberVerified | false | | Roles | {string[1]} | | TenantId | null | | UserName | "admin" |
Then when I expand Roles I see:
| Name | Value | | --- | --- | | [0] | "admin" |
-
0
First , you need to pass the token to the remote service. See https://docs.abp.io/en/abp/latest/Samples/Microservice-Demo#passing-the-access-token-1.
But it still returns unauthorized, you need to separate authorization server. like microservce example : https://github.com/abpframework/abp/tree/dev/samples/MicroserviceDemo/applications/AuthServer.Host
When I find other solution, I will reply.
-
0
Hi @ hiltond,
Try using tiered template.
-
0
I've setup a tiered version of the web project, but I'm running into a problem getting the permissions for the remote API to show up. When I go to add the permissions to the admin user I don't see any for the remote API permission set I created. I have references for
typeof(DevicesHttpApiClientModule), typeof(AbpHttpClientIdentityModelModule)
in the Web module. Am I missing something in the tiered version that wasn't required in the non-tiered one? -
0
Can you explain this problem in detail?
-
0
As suggested earlier, I created a tiered version of the web project: MVC, no mobile, EF Core, and tiered. Steps I took after that:
- I ran the db migrator against a fresh database.
- Commented out redis config stuff in the host modules that use it because I don't have a local redis cache.
//context.Services.AddStackExchangeRedisCache(options => //{ // options.Configuration = configuration["Redis:Configuration"]; //});
- I added the Devices page in the web project to reference the remote API in the same way as my initial post. This included referencing the same assemblies and adding
typeof(DevicesHttpApiClientModule), typeof(AbpHttpClientIdentityModelModule)
as dependency references in the WebModule.cs file. - I ran the sites and went into the User Management to give permissions for the remote API permission set like I did in the non-tiered project, but I don't see the permission set in there, it seems to be missing and I'm not sure why. Is there some other place I need to add a reference or something in the tiered web project that I didn't have to in the non-tiered one to show these permissions?
-
0
In which project do you define permissions?
-
0
In the Remote API solution (which is a module template solution) in Application.Contracts I have two files in an Authorization folder: DevicesPermissionDefinitionProvider
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Localization; namespace NowMicro.Dice.Devices.Authorization { public class DevicesPermissionDefinitionProvider : PermissionDefinitionProvider { public override void Define(IPermissionDefinitionContext context) { var devicesGroup = context.AddGroup(DevicesPermissions.GroupName); var products = devicesGroup.AddPermission(DevicesPermissions.Devices.Default); products.AddChild(DevicesPermissions.Devices.Update); products.AddChild(DevicesPermissions.Devices.Delete); products.AddChild(DevicesPermissions.Devices.Create); } } }
DevicesPermissions
using Volo.Abp.Reflection; namespace NowMicro.Dice.Devices.Authorization { public class DevicesPermissions { public const string GroupName = "Devices"; public static class Devices { public const string Default = GroupName + ".Device"; public const string Delete = Default + ".Delete"; public const string Update = Default + ".Update"; public const string Create = Default + ".Create"; } public static string[] GetAll() { return ReflectionHelper.GetPublicConstantsRecursively(typeof(DevicesPermissions)); } } }
-
0
Hi,
You should add
DevicesApplicationContractsModule
as dependency references in the<YourProject>ApplicationContractsModule.cs
. Because WebProject just an UI Intreface, it calls HttpApi.Host project. -
0
That fixed the permission issue. Now when I try the request from the Web project to the Remote API through the "Devices" page that I made before I get these errors in the logs for the Remote API service:
2020-05-18 09:58:34.520 -05:00 [INF] Request finished in 125.7825ms 200 application/json;charset=utf-8 2020-05-18 09:58:35.684 -05:00 [INF] Request starting HTTP/1.1 GET https://localhost:44321/api/devices?serialNumber=string&api-version=1.0 2020-05-18 09:58:39.874 -05:00 [ERR] Exception occurred while processing message. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://localhost:44349/.well-known/openid-configuration'. ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44349/.well-known/openid-configuration'. ---> System.Net.Http.HttpRequestException: No connection could be made because the target machine actively refused it. ---> System.Net.Sockets.SocketException (10061): No connection could be made because the target machine actively refused it. at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() 2020-05-18 09:58:39.898 -05:00 [ERR] IDX20803: Unable to obtain configuration from: 'https://localhost:44349/.well-known/openid-configuration'. System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://localhost:44349/.well-known/openid-configuration'. ---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44349/.well-known/openid-configuration'. ---> System.Net.Http.HttpRequestException: No connection could be made because the target machine actively refused it. ---> System.Net.Sockets.SocketException (10061): No connection could be made because the target machine actively refused it. at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(String address, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever.GetAsync(String address, IDocumentRetriever retriever, CancellationToken cancel) at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel) --- End of inner exception stack trace --- at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() at Microsoft.AspNetCore.Authentication.AuthenticationHandler`1.AuthenticateAsync() at Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, String scheme) at IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler.HandleAuthenticateAsync() 2020-05-18 09:58:39.900 -05:00 [INF] Bearer was not authenticated. Failure message: IDX20803: Unable to obtain configuration from: 'https://localhost:44349/.well-known/openid-configuration'. 2020-05-18 09:58:39.907 -05:00 [INF] Authorization failed. 2020-05-18 09:58:39.914 -05:00 [INF] AuthenticationScheme: BearerIdentityServerAuthenticationJwt was challenged. 2020-05-18 09:58:39.914 -05:00 [INF] AuthenticationScheme: Bearer was challenged. 2020-05-18 09:58:39.916 -05:00 [INF] Request finished in 4231.9708ms 401
-
0
Unable to obtain configuration from: 'https://localhost:44349/.well-known/openid-configuration' No connection could be made because the target machine actively refused it
Please Check if you start the service.
-
0
The Authority endpoint in the Module project was wrong, I fixed that and now I get this error on request:
2020-05-19 08:50:39.673 -05:00 [INF] Request starting HTTP/1.1 GET https://localhost:44321/api/devices?serialNumber=string&api-version=1.0 2020-05-19 08:50:40.147 -05:00 [INF] Failed to validate the token. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'Portal'. Did not match: validationParameters.ValidAudience: 'Devices' or validationParameters.ValidAudiences: 'null'. at Microsoft.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable`1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateAudience(IEnumerable`1 audiences, JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateTokenPayload(JwtSecurityToken jwtToken, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken) at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() 2020-05-19 08:50:40.169 -05:00 [INF] BearerIdentityServerAuthenticationJwt was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'Portal'. Did not match: validationParameters.ValidAudience: 'Devices' or validationParameters.ValidAudiences: 'null'. 2020-05-19 08:50:40.170 -05:00 [INF] Bearer was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'Portal'. Did not match: validationParameters.ValidAudience: 'Devices' or validationParameters.ValidAudiences: 'null'. 2020-05-19 08:50:40.176 -05:00 [INF] Authorization failed. 2020-05-19 08:50:40.181 -05:00 [INF] AuthenticationScheme: BearerIdentityServerAuthenticationJwt was challenged. 2020-05-19 08:50:40.181 -05:00 [INF] AuthenticationScheme: Bearer was challenged. 2020-05-19 08:50:40.182 -05:00 [INF] Request finished in 508.9196ms 401
I'm guessing I am missing some part of the configuration in the Identity Server but I'm not sure what.
-
0
Hi,
I think this can help you.