Angular Web Component http calls are not routing through clients http interceptor - web-component

Set-up:
I have a web component (Angular 10) being used in an Angular 10 Application. The web component makes an Httpclient call to a web API to get some data to populate a menu dropdown. The web component was made using the standard methods to make a web component using Angular 10.
The web component is loaded through a script in the main client application. This is from the angular.json file for the parent application.
1."scripts":
[
"projects/web-component-test/src/assets/plugin.bundle.js"
]
and all works fine except we get a 401 error (unauthorized) since the end point requires the user to be logged in. By working fine, there are other controls that display as required just the the dropdown list, which gets it data from the API call does not get populated.
The flow:
User goes to website and then is prompted to log in (using keycloak Auth).
Application loads fine, except for the 401 error when the web component tries to load the menu items.
Http calls from the parent app work fine; the jwt token is added to the header for the call to the protected API. Calls from the child web component do not have the jwt token in the header, and thus fail with a 401 error.
httpinterceptor: we have an httpinterecptor on the main client application (the parent of the web control). Http calls that are made from the main app are routed through the interceptor where the token is attached to the header if needed.
Calls made from the child Web Component DO NOT hit the interceptor in the parent app?
Question:
How do I make call from the child web component route through the http interceptor in the parent so the token can be added.
Things I have tried:
I can get the web component to work fine if I do this:
When the parent loads I store the token in local storage
using an http interceptor on the web component, retrieve the token from local storage and use it.
** works, but I DO NOT want to store a secure token in local storage.
Pass the token in an attribute on the child component when the parent loads the child component
** again, I can get it to work, but not very secure.

A web component is an independent piece of code from your main codebase, so when you hit a request from the web component, your main app won't be able to catch those requests in the interceptor.
For me, one thing it worked was creating different Custom Events per every request:
doGetRequest
doPostRequest
doPutRequest
doDeleteRequest
Let me quickly guide you through one example of these events.
From the main app, I'm listening if any of these events are triggered:
this.popupEl.addEventListener('doPostRequest', (info: HTMLElementEventMap | any) => {
this.performHttpRequest(info.detail);
});
Notice that all the info sent from your web component, can be found in the property 'detail' your main app receives.
From the web component, I execute the following:
this.doPostRequest.emit({
url: 'the URL to hit',
endpointParams: {
user: 'my user',
password: '******',
}
});
Note: You can change the structure of the params to sent. It's totally up to you.
This will trigger the execution of the API in the main app, and as the request was executed from the main app, your interceptor will do its work and add whatever JWT you have there. Once the main app receives the response, you will need to set a new property in your web component to pass the response to it. Something like this:
this.popupEl.apiResponse = {
webComponentInfo: { ...infoReceivedFromYourWebComponent },
apiDetails: { ...ResponseFromYourBackend}
};
Finally, in your web component, add a new input that listen for the apiResponse attribute:
#Input()
set apiResponse(apiResponse: RequestParams) {
if (apiResponse.webComponentInfo.url === 'the URL to hit') {
// Do what you want in your web component, as you know exactly which URL just got executed.
}
}
This way, you let your main app continue doing its work with your interceptor and the web component won't need to handle the JWT or actually perform the requests.
One thing you may want to consider is not allowing the Delete request from your web component unless you and only you have full control over the web component. You wouldn't want anything that can perform a successful DELETE request to a very important API.
Hope this helps.

Related

Resource based (Imperative) authorization won't enter the handler

Scenario:
I have an API with .net core 2.2
On top my controller I authorize access using IdentityServer4 with an Attribute
Inside one of my endpoints I want to authorize access to a method only in some cases
I implemented resource based authorization inside my endpoint just like it's shown in microsoft documentation.
It didn't work.
I put a breakpoint inside my authorization handler and tried debugging, but when this handler should be called, it is not.
I mean that when the following line runs
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
the Handler should be called, but that never happens.
Did anyone have the same problem?
So in the end the problem was due to the registration of the service in the startup.cs.
I was using TryAddScope, by changing to AddScope it worked fine.

How to get expiration time for silent token in Oidc-Client

I have SPA developed application on which I used to implement Oidc-Client for OAUTH authentication and below are the clarifications.
How to configure silent-refresh page with web pack config file in angular structure based project since silent-refresh.html is not invoked on token expiration.
Even if silent token generated then how to get/set expiration time of silently generated token?
Kindly help and suggest.
SILENT REFRESH
Rather than a separate HTML page, my personal preference is to handle this by a silent token renewal response to the index.html page. Then write code like this:
if (window.top === window.self) {
// Run the main app
const app = new App();
app.execute();
} else {
// If our SPA is running on an iframe, handle token renewal responses
const app = new IFrameApp();
app.execute();
}
I find that this approach avoids adding complexity to the WebPack / build system. The code for the iframe app does very little other than receiving the silent token renewal response.
EXPIRY
Interesting why you want to use access token expiry times directly. You can get the value like this:
const user = await this._userManager.getUser();
if (user) {
console.log(user.expires_at);
}
The real requirement here is to ensure that you avoid errors for end users when an API call fails due to an expired access token. This is best handled via the following actions:
If an API call fails with a 401 status code
Then try to get a new access token, generally via userManager.signInSilent()
Then retry the API call with the new access token
Therefore the way you call APIs should have a helper class with some retry logic, as in my example here.
To get notified after silent refresh, add an event handler for userLoaded: UserManager.events.addUserLoaded. This will pass the new User with a new expire time

Blazor WebAssembly Standalone access multiple AAD protected APIs

I have managed to make default template work (my blazor standalone SPA should acquire tokens for several scopes from different ADApps - webAPIs; I've managed to get token only for one scope at the time even if I defined additionalScopes or defaultaccesstokenscopes).
builder.Services.AddMsalAuthentication(options =>
{
var config = options.ProviderOptions;
config.Authentication.Authority = "https://login.microsoftonline.com/tenantID";
config.Authentication.ClientId = "clientID";
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/user.read");
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://tenant.crm.dynamics.com/user_impersonation");
options.ProviderOptions.DefaultAccessTokenScopes.Add("clientID/scope1");
// tried this too
// config.AdditionalScopesToConsent.Add("https://tenant.crm.dynamics.com/user_impersonation");
});
Now there is a question on how to get the other tokens because it gets the token only for 'clientID' scope if multiple scopes are mentioned...? and use those tokens from wasm page in HttpClient request?
In angular (with MSAL) this is all done automatically, you define scopes you want and it gets all the tokens. Then it intercepts all requests and adds authorization header and corresponding token by domain of the request.
Is there similar mechanism here or should this be done manually by adding corresponding token for every request and using HttpRequestMessage with HttpClient.SendAsync()?
Obviously for business application there is not much of a use without contacting some kind of protected API, which is usually an app in the same AAD. For example let's say it can be a simple query to the Dynamics CRM's webapi.

Can we completely replace the functionality of MVC with api

I have recently started learning both .Core MVC and WEB api.I found that functionality of Web Api is similar to MVC,then why can't we use API instead of MVC for all cases of MVC
For example for returning a list of Pies from DbContext _dbcontext
Code:
Public IAction Index()
{
var PiesCollection=_dbcontext.Pies.ToList();
return View(PiesCollection);
}
Instead of returning the PiesCollection to a View,why can't we use AJAX from a view to call GetPies api and replace it with
public IAction GetPies()
{
return JsonResult(_dbcontext.Pies.ToList());
}
There's nothing stopping you from doing that, however, it comes with the downside of putting more work on the front end, and can cause some irritating side effects for users if you are not careful.
Rather than doing all the work building the HTML before sending it to the browser, you are sending them incomplete HTML with spaces left open for 'future' content. Then the page then has to request this missing content. The service has to do all the same work as before gathering the data, but now has to serialize it to send to the browser. Then the browser has to parse that content to build a the UI.
This can mean that the front-end is no longer coupled to your data so you can cache it or host it on other servers since it doesn't need all the logic in it. This is how some mobile apps work even. The front end isn't a website anymore, but an iOS or Android app, that gets all the data from the asp.net services.
But the downside is that if the UI is heavily driven by the data returned by that service, then your user has to wait for the browser/app to get the response, parse it, and render it onto the screen. This can be extra irritating for users when the contents of the page move or change as data loads in.

How do OWIN Authentication providers work? How would I write a custom one?

I'm looking at the source code for Microsoft.OWIN.Security.Google and am a bit confused and overwhelmed at how many classes there are to do such a simple thing (redirect, get a cookie, check it).
Can anyone explain how the various components fit together
Middleware
Extensions
etc
... so that I can write a custom provider
After some google-ing and trying different ideas in debugger I ended up with "copy-paste-edit" :)
here is a brief resume of classes
Extensions - nothing special, a helper:
// instead of using
app.Use(typeof(CustomAuthenticationMiddleware), app, options);
// you can use
app.UseCustomAuthentication(options);
Middlware - methods are used to attach authentication to owin pipeline
AuthenticationProvider - As I understand, this could be overriden outside, to be able to change some logic without rewriting whole thing. Has 2 methods:
Authenticated - is called when handler finishes all authentication in AuthenticationHandler.AuthenticateCoreAsync()
ReturnEndpoint which is called in AuthenticationHandler.InvokeAsync, just before external authetication.
But it appeared absolutely useless, when I tried to customize existing providers (google, facebook,...)
Handler - here is all the OAUTH2 functionality.
ApplyResponseChallengeAsync() - generates AuthorizationEndpoint URL and redirects useragent to authorization server
InvokeAsync() - handles the get to RedirectEndpoint (/signin-google or whatever was set up on authorization server) and returns the user to the starting controller(or callback). It is doing a redirect with all needed cookies set up
AuthenticateCoreAsync() - does all server side calls to authorization server. Creates all Identity.Claims necessary to create appropriate cookies before

Resources