Calling an Azure AD secured Web API from Angular Frontend - azure

I am currently developing an Angular Frontend, which uses MSAL to authenticate users. This Frontend should call a Web-API (also hosted in Azure), which is secured by Azure Active Directory.
While I easily managed to work with Angular and MSAL, getting a Token and successfully calling Graph/me as test, I cannot get the API call to work, I'm always receiving 401's.
I'm using the following setup:
Angular frontend with MSAL
I created an Application in AAD, gave it User.Read permissions for MS Graph, copied the ID into the MSAL code and using the MSAL Interceptor calling Graph API was pretty easy following the documentation.
Web-API
I created a web-api with in .NET core, simply returning some demo data in the GET. I published the API to an azure Web Application (https://myappurl.azurewebsites.net/api/test, calling it from Angular or Postman was no Problem
Auth
Using the Azure Portal, in the App Web settings, I activated web service authentication with Azure Active Directory. As the required application, I put the same one I used in step 1 for the Frontend.
At this point I was not able to call my API any more, always receiving 401's.
I parsed the JWT Token out of the Angular code and tried postman calling with Authorization: Bearer eyJ0xxxxx headers, still 401.
I thought, that by "logging into" my frontend I should be able to use the token to identify myself for the API call aswell, since it uses the same app-id, but somehow I think I got it mixed up. I looked at a lot of documentation, but it's mostly outdated since the App registration changes in Azure Portal.
export const protectedResourceMap:[string, string[]][]=[['https://graph.microsoft.com/v1.0/me', ['user.read']] ];
MsalModule.forRoot({
clientID: "my-client-id",
redirectUri: "http://localhost:4200/profile",
postLogoutRedirectUri: "http://localhost:4200/bye",
authority: "https://login.microsoftonline.com/my-tenant-id",
validateAuthority: true,
cacheLocation : "localStorage",
navigateToLoginRequestUrl: true,
popUp: false,
consentScopes: [ "user.read" ],
unprotectedResources: ["https://www.microsoft.com/en-us/"],
protectedResourceMap: protectedResourceMap,
correlationId: '1234',
piiLoggingEnabled: true
}),
Do I need to add the webAPI to the protected ressources in Angular? Do I need an extra Application to secure the API and then allow my Frontend App to access the backend app? Reading through all the available articles confused me even more.

In your azure registration app go to "expose an api", copy the scope url and set this value as a scope in your loginRequest
var loginRequest = {
scopes: ["api://aa059cdd-1f53-4707-82a8-fdf7bd6c2859/Test"]
};
msalInstance.loginPopup(loginRequest)
.then(response => {
// handle response
})
.catch(err => {
// handle error
});

Related

Getting Error AADB2C99067 when trying to request access token from Azure B2C

I have an Azure AD B2C tenant setup with an Angular app on the front-end using Authorization Code Flow with PKCE and a back-end api. Everything is working fine. I now have a need to allow the user to access certain pages on the front-end anonymously. I would prefer to still protect the apis these pages will call using the same access token.
I have followed the article here to set up Client Credentials flow. I am able to get an access token successfully using Postman and use it to call my back-end apis fine. However, when I try to do the same from the Angular app, I get the following error:
{"error":"invalid_request","error_description":"AADB2C99067: Public Client XXXXX-XXXXXX is not supported for Client Credentials Grant Flow\r\nCorrelation ID: 2b3346ef-1828-4900-b890-06cdb8e0bb52\r\nTimestamp: 2022-07-28 04:12:21Z\r\n"}
Below is the code snippet I am using in Angular to retrieve the access token.
const urlencoded = new URLSearchParams();
urlencoded.set('grant_type', 'client_credentials');
urlencoded.set('client_id', '<clientid>');
urlencoded.set('client_secret', '<clientsecret>');
urlencoded.set('scope', '<scope>');
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }),
};
const url = 'https://<b2ctenant>.b2clogin.com/<b2ctenant>.onmicrosoft.com/<customPolicy>/oauth2/v2.0/token';
return this.httpClient.post(url, urlencoded, httpOptions);
Any ideas what could be missing?
Thanks!
Though azureadb2c supports client_credential flow.One may not use them with SPA apps.
This scenario is not supported by MSAL.js. Client credential flow/ grant type will not work in SPAs(Angular) because browsers cannot securely keep client secrets.
As they may end up in the browser, visible to everyone and to attackers that load them.
Note:As the application's own credentials itself are being used, they must be kept safe - never publish that credential in your source
code
If you are using it for web app , please make sure to select the platform as web or change the reply url type to be web.
"replyUrlsWithType": [
{
"url": "https......",
"type": "Web"
},
]
Please refer :
Configure authentication in a sample Angular SPA by using Azure
Active Directory B2C | Microsoft Docs
OAuth 2.0 client credentials flow on the Microsoft identity platform- Microsoft Entra | Microsoft Docs

Can't log on to Azure App Service using Facebook access token

I've a simple HTML/Javascript page, served locally on localhost:5000.
On Azure I've a App Service running a Aspnet core API.
I've set up CORS so I can access the API from my page. Now I try to enable Facebook authentication.
From my page I use facebook SDK. I works fine and I can login to my facebook-application. I get back a access token.
My problem is how to authenticate in my Rest Api. On the Azure portal I've turned on "App Service Authentication" and "Log in with Facebook".
If I've understood the docs correctly I'm supposed to provide the access token from Facebook to my Azure RestApi. I use the following code to do that.
const resp = await fetch('https://<mysite>.azurewebsites.net/.auth/login/facebook',
{
method: 'POST',
body: {
"access_token": fbToken
}
});
Unfortunaletly I got a 400 response, saying "Invalid client credentials".
What's wrong? How can it be fixed?
It was a really silly mistake. I missed to serialize the body to JSON.
body: JSON.stringify({
"access_token": fbToken
})

Blazor standalone client calling a function via Azure B2C

Is there a good example or a walkthrough of a Blazor standalone app calling a function app via an Azure Active Directory B2C passing in a claim with an identity to the function?
I have been working my way through the documentation,
Secure an ASP.NET Core Blazor WebAssembly standalone app with Azure Active Directory B2C and
ASP.NET Core Blazor WebAssembly additional security scenarios but cannot get past 404 result.
So what am I trying to achieve? I want to put together a couple of POCs. The first is a Blazor standalone client app, authenticating via B2C, then passing an authorisation token claims token to an azure function, where I can unpack the user id and email address. The second is same as above, but with Api Management between the Blazor client and the functions api. Because of the nature of the project I am using the consumption plan for both the functions and api management.
So just concentrating on the first case (Blazor - B2C - Function), on the assumption if I get that to work, the api will follow…
I have a B2C client tenant with 2 applications: a front end app authorised for the scopes of a 2nd B2C application for the api. My Function app has authentication/authorisation set to 'Login with Active Directory' with the client ID set to the Front end app's client id, the issuer uri set to the B2C's pocsignupsignin Userflow and the 'Allowed Token Audiences' set to the client id of the Api in B2C.
I can get a JWT token via a browser and using postman successfully call a function in my function app passing a bearer token.
My Blazor app can log in to B2C. If I have no authorisation configured for the web app, then my function call is successful.
But once I turn authorisation I run into CORS 404 - origin 'https://localhost:44389' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. But I have CORS configured with 'Enable Access-Control-Allow-Credentials' and my client url configured.
What am I missing?
I also suspect that the token with the id will not be passed correctly.
From Program.cs
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri(functionURI))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(readScopeURI);
options.ProviderOptions.DefaultAccessTokenScopes.Add(writeScopeURI);
Console.WriteLine($"options.ProviderOptions.DefaultAccessTokenScopes {options.ProviderOptions.DefaultAccessTokenScopes}");
});
From CustomAuthorizationMessageHandler
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { B2CClientUrl },
scopes: new[] { "read", "write" });
}
}
From appsettings
"AzureAdB2C":
{
"Authority": B2C signupsignin uri,
"ClientId": B2C frontend client,
"ValidateAuthority": false
}
Function call
async Task GetFromDedicatedFunctionClient(string subUrl)
{
try
{
var client = ClientFactory.CreateClient("ServerAPI");
Console.WriteLine($"client.BaseAddress {client.BaseAddress}");
result =
await client.GetStringAsync(subUrl);
}
catch …
}

401 Authorization Required integrating Hyperledger Composer REST API from Webapp

Introduction
I have a hyperledger env running in secure mode by following this link https://hyperledger.github.io/composer/integrating/enabling-rest-authentication.html
and it works fine if I authenticate as specified in the document (hitting http://mydomain:3000/auth/github directly from the browser) and then access the Rest API from the http://mydomain:3000/explorer and could authorize as various participants (i.e, issuing identity and adding them to the wallet and setting one as default at a time) and could see the assets as per the .acl file.
Issue
But I started facing problems when I started integrating the Rest API's from my web application rather directly from the browser. As a first step from my web app, I called the http://mydomain:3000/auth/github to authenticate and then started calling the other APIs (transaction/list, etc.) but I do always get
Error 401: 'Authorization Required'
What i have tried
Gave my web application URL as the 'Redirect URL' in the env variable for the hyperledger. And upon successful authentication (calling http://mydomain:3000/auth/github) it successfully redirected to my webapp home page but afterwards accessing the Rest API's (from web app) again throws 'Authorization Required' error.
Environment variaable as below:
export COMPOSER_PROVIDERS='{
"github": {
"provider": "github",
"module": "passport-github",
"clientID": "CLIENT_ID",
"clientSecret": "CLIENT_SECRET",
"authPath": "/auth/github",
"callbackURL": "/auth/github/callback",
"successRedirect": "http://localhost:8080/home.html",
"failureRedirect": "/"
}
}'
Incorporated passport-github2 mechanism in my web application (i.e, registered my app with the oauth of github) and upon successful login to my web application; called the http://mydomain:3000/auth/github to authenticate to the blockchain and it did not work out as well.
I have a few questions:
Is it feasible to call the secured hyperledger Rest API's from another web application?
If Yes, how to do it? I don't find that information in the hyperledger composer documentation.
Have been trying this for a week now and have no answers. Any help would be greatly appreciated. Please let me know if anything is unclear. Thanks.
I commented about this problem on one of the existing hyperledger github issues(below link) & I want to share the solution that solved this problem for me.
https://github.com/hyperledger/composer/issues/142
Solution: as mentioned by user sstone1
Since the REST server is on a different port number to your web application, you need to specify an additional option to your HTTP client to pass the cookies to the REST server. Using the Angular HTTP client, you add the withCredentials flag, for example:
via Angular:
this.http.get('http://mydomain:3000/api/MyAsset', { withCredentials: true })
via JQuery AJAX:
$.ajax({
url: 'http://mydomain:3000/api/MyAsset',
xhrFields: {
withCredentials: true
},
headers: {
...
}
})

CAML-query to Sharepoint REST API results in "invalid audience URI"?

We have been successfully using the Sharepoint REST API to retrieve items from lists for a while now, but just recently decided to integrate the ADAL.JS in order to be able to access other Microsoft APIs such as Graph, Azure AD etc.
After successfully authenticating Adal.js automatically adds an
Authorization: Bearer eyJ..
header to the REST calls which works fine after fiddling with permissions a bit. The app is an Angular SPA hosted in Sharepoint so this header isn't necessary but doesn't really matter.
HOWEVER, a few of our REST calls require us to also query the taxonomy and as that isn't supported in the normal Sharepoint REST API, we have to hit the (/_api/web/lists/GetByTitle('ListName')/GetItems endpoint) with a CAML-query as request payload i.e. https://mydomain.sharepoint.com/sites/dev/_api/web/lists/GetByTitle('News')/GetItems
Unfortunatelly, this does not work as the API simply returns
Invalid audience Uri '5exx5cef-x7xx-4xxx-axxx-4xxxx2e40'.
So far my only solution is to modify the actual Adal.JS library to remove this header for this specific endpoint.
So, my questions is - has anyone done CAML-queries against Sharepoint REST APIs using Adal.JS, or ran into a similar problem and can provide any insight?
I suspect it is a configuration issue but am somewhat at loss on what to do.
In this case, you need to force setting the endpoint 'https://mydomain.sharepoint.com' to null. Else, each request to "mydomain.sharepoint.com" will add a graph authorization header which be validated by the SharePoint server. Since the app is registered on the Azure AD rather than SharePoint, it will be considered as a invalid audience.
Here is the workaround for your reference, please let me know if it works on your side.
(function () {
angular.module('app', [
'ngRoute',
'AdalAngular'
]).config(config);
// Configure the routes.
function config($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainController',
controllerAs: 'main'
})
.otherwise({
redirectTo: '/'
});
// Initialize the ADAL provider with your clientID (found in the Azure Management Portal) and the API URL (to enable CORS requests).
adalAuthenticationServiceProvider.init(
{
clientId: clientId,
// The endpoints here are resources for ADAL to get tokens for.
endpoints: {
'https://graph.microsoft.com': 'https://graph.microsoft.com',
'https://mydomain.sharepoint.com': null
}
},
$httpProvider
);
};
})();

Resources