I have asp.net hosted blazor wasm app that uses hangfire. I have the dashboard configured on the URL "/hangfire" with custom filter that analyzes an access token passed via a query parameter. Here is the related configure part from the startup:
endpoints.MapHangfireDashboard(
pattern: "/hangfire",
options: new() { DashboardTitle = "Job Dashboard", Authorization = new[] { new HangFireAuthorizationFilter() } });
This works as long as I'm testing locally. When I deploy and I try to navigate to the dashboard via the navigation manager I geta 404:
var accessToken = await LocalStorageService.GetItemAsync<string>(StorageConstants.AUTH_TOKEN);
NavigationManager.NavigateTo($"/hangfire?token={accessToken}", forceLoad: true);
When I copy the URL and paste it in a new InPrivate tab it works as expected. For me it sounds like the Router catches the request and redirects to NotFound despite the forceLoad parameter being set to true. Is there any way how I can prevent that?
Related
I have API protected by Azure AD where Authentication is required to get access data, where I expose the API with only one simple scope for now.
The API and client app both are registered in Azure AD.
Roles are also configured for the API, only a a user with Admin role can call my API.
Do I need to assign this Admin role as well to the client App? or AccessApi scope is enough?
Scopes Who can consent Admin consent display name User consent display name State
api://xx User AccessApi AccessApi Enable
And a client application build using webassembly blazor also registered in Azure AD, and its configured with Api permission to use delegated access to AccessApi.
API / Permissions name Type Description Admin consent required Status
myApi (1)
AccessApi Delegated AccessApi No
I configured webassembly blazor client application to authenticate against azure and get token and use that token to call myApi, however I keep getting loading but no data is being displayed without any error.
Im not sure what went wrong here ?
program class of client application:
private static string scope = #"api://xxx/AccessApi";
...
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri("https://localhost:44314"))
.AddHttpMessageHandler<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
});
At fetch data razor page I imported all necessary libraries
#using Microsoft.AspNetCore.Authorization
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject IAccessTokenProvider TokenProvider
#attribute [Authorize]
#inject NavigationManager UriHelper
#inject HttpClient Http
...
data = await Http.GetFromJsonAsync<data[]>(#"API-url--runs-locally-on-docker");
The authorization message handler class
private static string scope = #"api://xxx/AccessApi";
public GraphAPIAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:44314" },
scopes: new[] { scope });
}
After authenticated myself with Azure AD account, the client app shows loading.. but no data is being displayed.
at the console level shows this error.
Failed to load resource: the server responded with a status of 401 (Unauthorized)
I followed Microsoft documentation and I'm not sure what I'm missing here.
what could be wrong here ?
Update
The Api expose tab:
Api permission for the client app:
Obviously, your scope is set incorrectly. You should set the scope to the client id of the api application instead of the client id of the client application. In your question, I think your scope should be: api://7b4d6df9-63cd-4ed7-881bxxx/AccessApi.
Parse the token and you should see scp claim and roles claim.
I am developing a Single Page Application (SPA) with ReactJS and ASP.NET. I I am trying to execute an HTTP Request to an endpoint from client side code. I used the default ASP.NET SPA template and changed my configuration as shown below.
fetch(`/items/${id}`).then((response: any) => {
return response.data;
})
I modified the default web api template and use a Proxy Server in development instead. Like so:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Client";
if (env.IsDevelopment())
{
spa.UseProxyToSpaDevelopmentServer("http://127.0.0.1:3000/");
//spa.UseReactDevelopmentServer(npmScript: "start");
}
});
When I execute the fetch in React client code, I get this error: Proxy error: Could not proxy request /items/1 from localhost:3000 to http://localhost:44381/ (ECONNREFUSED).
How do I configure my server-side code to proxy the request?
According to the linked issue on GitHub, the spa.UseReactDevelopmentServer should already setup the proxy server itself as of .Net Core 3.1. So you might not need to be calling the spa.UseProxyToSpaDevelopmentServer yourself manually.
https://github.com/dotnet/AspNetCore.Docs/issues/12008#issuecomment-573453266
I have created a small .NET Core 3.1 console application using the MSAL library which requests scope api://55a047a1-a0d1-4b6b-9896-751a848e1e06/testscope2
Custom API exposes two scopes
api://55a047a1-a0d1-4b6b-9896-751a848e1e06/testscope1
api://55a047a1-a0d1-4b6b-9896-751a848e1e06/testscope2
I have also configured another application named test-app in Azure Active Directory which represents my console application.
I have configured only one API permission (api://55a047a1-a0d1-4b6b-9896-751a848e1e06/testscope1) for this application. My understanding is with this configuration in place client app will only be able to request for scope test1 and it won't allow test-app to request for scope2
Below is screenshot
This is my code:
//<PackageReference Include="Microsoft.Identity.Client" Version="4.13.0" />
namespace console_client
{
class Program
{
static void Main(string[] args)
{
#region Azure AD parameters
var clientId = "dddeefa5-d95c-4931-a53d-2382deee27c3";
var tenant = "-- MY TENANT ID--";
var instance = "https://login.microsoftonline.com/";
#endregion
var client = PublicClientApplicationBuilder
.Create(clientId)
.WithDefaultRedirectUri()
.WithAuthority($"{instance}{tenant}")
.Build();
List<string> scopes = new List<string>();
try
{
// I was under impression that this call will throw as exception as
// this app is requesting 'testscope2' which is not included in API Permissions
// while configuring test-app in Azure Active Directory (dddeefa5-d95c-4931-a53d-2382deee27c3 )
// But I was able to retrieve token back with testscope2 in it.
scopes.Add("api://55a047a1-a0d1-4b6b-9896-751a848e1e06/testscope2");
var authenticationResult = client.AcquireTokenInteractive(scopes).ExecuteAsync().Result;
Console.WriteLine($"Interactive Access token is : {authenticationResult.AccessToken}");
}
catch (Exception ex)
{
System.Console.WriteLine($"******* {ex.Message}");
}
}
}
}
Question
Am I missing anything? Why am I getting the access token back even if the app doesn't have permission configured?
Thanks
TL;DR it is a feature.
With the v2 endpoint / MSAL, you can request for scopes that are not defined in your app manifest.
The ones in your app registration are the static permissions required by your application.
But your application can also request dynamic permissions at login time.
The user/admin would still need to consent to that of course, an app won't get a permission without consent.
Your app seems to be a single-tenant app so this doesn't really make a difference for you.
It is mainly for multi-tenant SaaS applications that can require the minimum needed permissions in the app registration/manifest, and then request more permissions for opt-in features as they are needed.
By the way, if you want to use the permissions defined in your app registration, you can request a special scope: api://55a047a1-a0d1-4b6b-9896-751a848e1e06/.default (your app ID URI or client id + "/.default").
This will make AAD look at your app registration to decide which permissions to check consent for.
I have promoted a test .NET Web Api to an Azure application service and included an app registration under Azure Active Directory. I then went to do some testing locally and noticed that Azure wanted to use the reply URL in the app registration after login. The reply URL in the app registration is the URL for the application service. My local instance will be something like https://localhost:44377/. How are you supposed to test changes locally after doing an initial deploy to Azure? All I can think to do is create another app registration for testing, use my local host reply URL, then update my web.config to point to that development app registration. Then prior to publishing again, update the web.config back to the other app registration.
Below is the code I used for authentication which was based on the standard template from a simple MVC project. The values app registration are being used for the redirect URL but maybe I am supposed to override those values below while testing?
public class AccountController : Controller
{
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
If you want to test locally, just add localhost as a reply URL and ensure that the web.config also lists localhost.
Please refer to this repository if you have not done so already: https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect
You can have multiple reply urls by specifying which you want to use in the authentication request. You do that when configuring your authentication in Startup.cs. You need to add a RedirectUri to your OpenIdConnectAuthenticationOptions.Notifications.RedirectToIdentityProvider
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
//...
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.RedirectUri = "<current reply uri>";
return Task.FromResult(0);
}
}
// ...
};
That reply uri can be pulled from your web.config or generated dynamically using context.Request.
If you want to use a different AD App after going to production, you can have two apps and put the client id and secret in the web.config.
This might be a silly question but here is my simple webapi 2 method
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
Now if I run it , on my pc it runs on http://localhost:3145/Products, and I can see the products as XML
It also works using soapui
But now if I try to access this with a html file and this javascript
<script type="text/javascript">
function GetProducts() {
$.ajax({
url: "http://localhost:3145/Products",
dataType: "json",
success: function (data) {
for (var i = 0; i < data.length; i++)
$('#myDiv').append(data[i].Category).append("<br/>");
},
error: function (xhr, status) {
alert(xhr);
}
});
}
</script>
</head>
<body onload="GetProducts()">
<h1>My App</h1>
<div id="myDiv"></div>
I get the CORS error
How is SOAP UI not getting an error here when it is using http as well?
Thanks
Put that index file into some server location, and then browse the page with server url , like, http://localhost/virtual_dir/index.html , else it will say it file:/// domain does not match with http://localhost:port . And you might face CORS issue if you deploy this page to some other domain and start using.
I have seen that you are using webapi , and you might face CORS issue if you place you JS in domain ( "example1.com"), i mean files served from example1.com will have ajax calls to webapi and that webapi may be hosted in example2.com. This will raise CORS issue. Browser restricts ajax call to other domains, unless that domain allow you to invoke. To achieve this, you can follow this link - Angular.js $resource with ASP.Net webapi? (don't go by the title)
I have answered there the same scenario.
I just experienced the same situation with a web page I was developing that needed to send a SOAP request to a web service that was served by a remote server that was not CORS-enabled. This was in a test environment; in production, the web page and web service are served from the same domain and port.
How is SOAP UI not getting an error here when it is using http as well?
Yes, SoapUI and a web browser both use HTTP. But SoapUI is not a web browser, and does not share the same restrictions.
A colleague solved this issue for me by pointing me to CORS Anywhere.
Install Node.js, if you don't already have it.
Install CORS Anywhere (npm install cors-anywhere).
Run CORS Anywhere (node cors-anywhere.js).
Instead of using your original non-CORS-enabled web service URL, use the domain and port in the resulting message from CORS Anywhere ("Running CORS Anywhere on ..."), with your original web service URL as the path component.
You can specify the CORS Anywhere port in an environment variable (or edit the default value in your local copy of cors-anywhere.js).
In my case, the production web service URL is hardcoded in the web page as a relative path. But I've added code that reads a URL from a parameter in the fragment identifier ("hash string"), overriding the default web service URL.
For example:
http://localhost:8081/mywebpage.html#url=http://localhost:8089/remote.domain.com:8085/servicename
where:
http://localhost:8081/mywebpage.html is my web page (I'm using http://, not file://, for this).
http://localhost:8089 is the CORS Anywhere proxy.
remote.domain.com:8085/servicename (you can drop the leading http://) is the remote, non-CORS-enabled web service.
I could point you 2 options to solve this:
Disable your browser CORS: Due the CORS enforcement is done by the browser you can just disable this during development in yourself browser. If you use Chrome, you must just set an parameter otherwise I may guess looking to the underneath image you are using Firefox, for this you have an extension to do this: https://addons.mozilla.org/pt-PT/firefox/addon/cors-everywhere/
Allow CORS in SOAP UI: It can take a little bit more effort than above solution but it fits very good when you need to share with mutiple teammates or just to make the solution attached to the mock service. To do this,you must add a response for the root of resource path that responds to OPTIONS and with headers you need for CORS with status 204 (or 200).
After, in the MockService you just need to add a script to grab these same headers in all calls that passes through.
Here is the article to solve this step-by-step:
https://medium.com/#andrelimamail/how-to-deal-with-cors-in-soap-ui-mock-services-or-anyother-f4cc55b3dccd