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
);
};
})();
Related
I have an ASP.NET Core 6 MVC / Web API app I am deploying to Azure which uses the ASP.NET Authentication Framework. It uses cookie authentication with VueJs on the front end.
When I deploy to Azure Web Services, the [Authorize] attribute returns a 401 for all Patch, Put or Post calls. However, for all Get calls, the [Authorize] attribute works fine, and the API call is authenticated.
I've tried removing the [Authorize] attribute and inspected the claims and identity. Again, for all non Get calls, the identity is empty and User.Identity.IsAuthenticated is false.
But if I just change it back to a Get call, then all identity information comes through.
I can't think of any reason why the .NET authentication stack would great a Get call differently than a Post call when deployed on Azure.
Locally on my dev machine, and on a regular IIS deployment everything works fine. I've inspected the http packets, and the identity cookie is being sent by the client.
Any pointers on where to look is appreciated.
I have come across the scenario when using postman to create the users Which uses POST request.
GET https://graph.microsoft.com/v1.0/users
Even for GET , i was getting unauthorized as previously read permissions are not given and invalid bearer tpoken was given
You must be missing receiving auth access token , please add the authorization header in the code which is done In the Configure method in Startup.cs class
app.UseSession();
app.Use(async (context, next) =>
{
var JWToken = context.Session.GetString("Token");
if (!string.IsNullOrEmpty(Token))
{
context.Request.Headers.Add("Authorization", "Bearer " + Token);
}
await next();
});
app.UseAuthentication();
app.UseAuthorization();
check the scope is provided in controller part where [Authorize] attribute is given.
Check Protect a web API with the Microsoft identity platform - Microsoft Entra | Microsoft Learn
I could get the GET request response successfully after giving below permissions
I have these Directory.Read.All and User.Read delegated and application permissions only.
POST request needs write permissions , in my case to create users , it requires User.ReadWrite.All and Directory.ReadWrite.All Application permissions
Create User - Microsoft Graph v1.0 | Microsoft Learn
After giving required permissions and getting admin grant consent .
I could execute the post request successfully and create the users
Please check POST request requirements and permissions needed and make sure they are granted admin consent.It can be permissions with scopes that are granted to particular web api.
Make sure the access token or user interaction is done and permissions required are granted.
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
I have a .net core 2.2 api setup and deployed to Azure. I have used OpenId Connect to handle the authentication using azure active directory single tenenat using the code below. And the Authorize decorator on my controller. Everything works and when I browse to my azure deployed api (myappname.azurewebsites.net) I get microsoft login prompt. I'm able to then login and view my route data.
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = ctx =>
{
return Task.CompletedTask;
}
};
opts.Scope.Add("openid");
opts.Scope.Add("profile");
opts.Scope.Add("access_as_user");
});
The problem is that when I have the Authorization turned on for my controller, I am not able to call it form my angular SPA client application. I have successfully configured MSAL and my api calls are passing a token. However, I get the following error:
https://login.microsoftonline.com/bit4f574-5968-4a40-049d-1c0dc2ca0513/oauth2/authorize?client_id=caor847f-dd19-4489-bef7-684803728c665&redirect_uri=https%3A%2F%2Fmyapi.azurewebsites.net%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile%20user_access&response_mode=form_post&nonce=637373859487758409.MzhhYTAoeiudtMTdlNS00NzgxLWJjMTQtNzM1YWE3NsdlkelasdNGYxMmQtMjZmYS00YmI2LTgwY2UtNDEwMTNhMWNkN2Zi&state=CfDJ8KCu3Hr4UOhLjOspjLNEh0VtJd4GNXqwdibjSiZf7FpUJOL0EDlFso0g0s_iOZHDNbP2aiHVfdzqJSmHkesd-bMjP6ThYva6AfZBa8UZcnGcwgo2ldlg4Fx9vmNVDuSlvHyTlHkd8yNndslkgoyHtfM4RMXamq1wny1J39BZRRATn1RdAsgaLgKP_QkxLaDCwgvdzjp3dKls5UVQE1j7MD6bcKR__1-VmfVKhROn1coQh7OJrea6Jni4jdV7e0wv70TVprGtseJFg8fyHg3KKW14xeX2orlkgls5aLe1uG0c5ehlapFXBirBSgFU3uqOWw0_iLeJUbTL8-HPooixynQRWe1WoiLnQuFYUu7Lx-usdlglvM4WvLfAyTZ5uQY_KsOtr08MxWRlQ5HHVk8Moe1k_N_3BCz8sdkgowwZEKsGiKd_iwcXgzxmgg&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0
How can I fix this? It seems like my api is redirecting the client request to the microsoft login, but I'm thinking the way this should work is that my api should validate the token in the request or the scopes and grant access without redirecting the request to a login.
The key point here is to separate SPA OAuth entirely from API OAuth, with the following behaviours:
SPA does the redirect handling and gets a token from the Authorization Server - this code is Javascript based and does not need a C# 'web back end'
API validates tokens via the token signing public key of the Authorization Server - this code is C# based, which is a good language for API development
Authorization Server is an out of the box component such as Azure AD, and you never code issuing of JWTs yourself (good to see you doing this already)
Many online articles I've seen mix up these concerns and can be very confusing to people who are new to OAuth technologies. So it's important to be clear about what you want from the end solution.
API OAUTH CODE
Your C# code should only need a few lines by default, starting by adding a package such as this one:
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.8" />
The code to configure the API's security would then look something like this. Note that there is nothing about OpenIdConnect / web behaviour here.
private void ConfigureOAuthTokenValidation(IServiceCollection services)
{
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Authority = "https://login.microsoftonline.com/mytenantid";
options.Audience = "api://default";
});
REAL WORLD SAMPLE
My .Net Core API solution has some stuff that may be of interest. These samples are quite advanced but enable you to run a local API and SPA that work together.
.Net Core Blog Post
API Code
API Security Configuration Code
SPA Code
The goal is to get the best end result for both API and SPA, so perhaps the above links will help you think about what that is in your case.
I am using Azure mobile app services with Xamarin Forms.
In my app, I use web social media authentication (Facebook, Twitter, Google) configured in the azure portal.
I am taking the sid gotten from CurrentClient.Id to match it with users in my Easy Tables. However, for some users, after logging in with the same account and same provider, no match is found in my database because the sid is different! I am 100% sure that it is the same account used to login before, yet I get a different sid. How is that possible? Shouldn't it remain the same with every login or what's the whole point of it then?
You are using Azure App Service Authentication for this. There is a stable ID that is available within the JWT that you pass to the service. You can easily get it from the /.auth/me endpoint (see https://learn.microsoft.com/en-us/azure/app-service/app-service-authentication-how-to#validate-tokens-from-providers )
When you GET /.auth/me with the X-ZUMO-AUTH header set to the authenticationToken returned from the login, the user.userId field will be populated with a stable ID. So, the next question is "how do I add this / compare this within the Node.js backend?" Fortunately, the HOW-TO FAQ for Node.js explicitly answers this. Short version is, use context.user.getIdentity() (an async method) to get the identity, then do something with it:
function queryContextFromUserId(context) {
return context.user.getIdentity().then((data) => {
context.query.where({ id: data.userId });
return context.execute();
});
}
function addUserIdToContext(context) {
return context.user.getIdentity().then((data) => {
context.itme.id = data.userId;
return context.execute();
});
}
table.read(queryContextFromUserId);
table.insert(addYserIdToContext);
table.update(queryContextFromUserId);
table.delete(queryContextFromUserId);
The real question here is "what is in the data block?" It's an object that contains "whatever the /.auth/me endpoint with the X-ZUMO-AUTH header produces", and that is provider dependent.
The mechanism to figure this out.
Debug your client application - when the login completes, inspect the client object for the CurrentUser and get the current token
Use Fiddler, Insomnia, or Postman to GET .../.auth/me with an X-ZUMO-AUTH header set to the current token
Repeat for each auth method you have to ensure you have the formats of each one.
You can now use these in your backend.
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
});