AngularJS with Microsoft Graph Group API - angularjs

When I try to retrieve the Office365 Groups I'm member of using AngularJS HTTP Get. I always get a 400 - Bad Request error in Chrome. In IE I do get a result.
var endpoint = "https://graph.microsoft.com/v1.0/me/memberOf/$/microsoft.graph.group?$filter=groupTypes/any(a:a eq 'unified')";
this.$http.get(endpoint, {
headers: {
"Content-Type": "application/json;odata=verbose"
}
}).then(function (result) {
console.log(result);
}, function (error) {
console.log("error:" + error);
});
Looks like the quotes in the URL are replaced with %27 and the Microsoft Graph/OData doesn't support this. I'll get the following error back:
Collection open properties are not supported in this release.
The JoinedGroups resource isn't available in the Microsoft Graph anymore. Is there another way to retrieve to Groups I am member of? Or is there another way to do this in Angular?

You can use ListMemberOf to get members of a group. here is a reference how to call the endpoint:
https://graph.microsoft.io/docs/api-reference/v1.0/api/group_list_members
You can use any falvor of JS client library to request o365 api. there is no limitation of which library you use as you have a correct request to the endpoint.
You can also use the graph explorer to make sure that you have a right request before coding it in AngularJS.
Graph explorer url: https://graphexplorer2.azurewebsites.net/
Hope this helps.

Office 365 unified groups I’m member of: https://graph.microsoft.com/v1.0/me/memberOf/$/microsoft.graph.group?$filter=groupTypes/any(a:a%20eq%20'unified')

Related

REACT application to call secure Azure WEBAPI Service - NO USERS

I have created a simple REACT application that is ONLY run on a local PC attached to a large screen on our network. Internal use only! It is like a billboard or dashboard. There is ZERO user interaction. The screen is NOT a touch screen and there is no keyboard and mouse attached. Therefore NO users to login.
The REACT application is build and then deployed to a folder on the PC. All automated. The initial deployment includes all current data. Then at windows startup a command something like this is executed:
"python -m http.server 3000" (just example...)
The application has initial data that was deployed with the application, however, I would like it to also be able to call a secure Azure WebAPI service to get updated statistics every few minutes. Very small data. Mostly integer values. I just want to provide some real time updates.
I have the REACT app fully working (if the WEBAPI is not secure) or the individual calls allow anonymous. However, we have business rules that require all endpoints to be secure.
This app runs locally, but the API is an Azure App Service.
I have setup the REACT application in Azure AD as a registered application and configured it to have permissions to call the WEBAPI service.
I have many console applications that are setup and work basically the same way as this REACT application. With the C# daemon applications, there is a MSAL package that makes it easy.
I am trying to learn REACT, and instead of building this as another WPF or UWP application, I wanted to try using REACT.
So, I know I need an access token somehow. I was thinking with a client ID and Secret just like I do in my C# daemon clients that are written in C#.
I cannot find any REACT nor Angular examples that do this without a user login first. Remember, the PC does not have input devices. Display ONLY. Again, my app does not have users. It calls a secure API to get data. That's it.
Thanks for your help.
Using Joy Wang's comments and this page from documentation:
Service-to-Service Access Token Request
This is my new code:
const adalConfig = {
tenant: '...',
clientId: '...',
clientSecret: '...',
authority: 'https://login.microsoftonline.com/{tenant}/oauth2/token',
endpoints: {
apiResourceId: 'api://bbbbbb-...',
},
};
function getAccessToken() {
var requestParams = {
grant_type: 'client_credentials',
client_id: adalConfig.clientId,
client_secret: adalConfig.clientSecret,
resource: adalConfig.endpoints.apiResourceId
};
// Make a request to the token issuing endpoint.
fetch(adalConfig.authority,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify( requestParams )
}).then(response => {
if (response.status >= 200 && response.status < 300) {
console.log(response);
console.log(response.json());
} else {
console.log('Somthing happened wrong');
console.log(response);
}
}).catch(err => err);
}
When I call the function above, I get the following response:
Response {type: "cors", url: "https://login.microsoftonline.com/.../oauth2/token", redirected: false, status: 400, ok: false, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: false
redirected: false
status: 400
statusText: "Bad Request"
type: "cors"
url: "https://login.microsoftonline.com/.../oauth2/token"
proto: Response
Maybe there is another way to start the REACT application so that CORS is not checked? Any ideas?
Thanks again.
So, currently there is not a secure way to do what I want. The basic issue is that you cannot use the client credential grant type from JavaScript in a browser.
However, I think I have a good work around that may help others. I am sure it is NOT for most application. And I believe OAUTH is working on a solution so this may not be needed in the near future. If a better solution is add, I will gladly mark it as the correct answer. Thanks for your help.
My app is basically an automated dashboard/billboard with ZERO user input. It pulls secure data and displays it. The REACT application is ONLY on a LOCAL PC on a wall with NO inputs. A script runs when the PC is turned on.
The script starts the built REACT application using an http server like python.
Ex: "python -m http.server 8000"
The script then opens the browser in kiosk mode so the only thing you see on the screen is the application.
So far, this is exactly as I had it before.
WORK AROUND:
I created a command line utility called GetToken. Before the REACT application is started by the script, it calls this utility like so: "gettoken --client Dashboard --file token.json"
This utility makes the Client Credential Grant Type call to get a token.
It then saved that token to a local json file with the other built REACT files. Ex: \public\data\token.json
In my REACT application, it just loads the token and uses it.
const t = await fetch('./data/token.json').then(r => r.json());
this.setState({ token: t.token });
Then I just add this to my api calls like so:
const fetchOptions = {
method: 'GET',
headers: {
"Authorization": `Bearer ${this.state.token}`,
"Content-Type": "application/json"
}
};
const newSlides = await fetch(this.state.baseUrl + '/api/Dashboard/GetSlides', fetchOptions).then(response => response.json());
IMPORTANT: This only works if you also have the ability to update the API. If you cannot, then you will still get CORS errors. You will have to allow calls from the localhost and port you use to start you application. You should pick something other than 3000, 4200, or 8000.
I added the following to my API startup.cs:
public void ConfigureServices(IServiceCollection services) {
...
var origins = Configuration.GetSection("AppSettings:AllowedOrigins").Value.Split(",");
services.AddCors(o => o.AddPolicy(specificOriginsPolicy, builder => {
builder.WithOrigins(origins)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.SetIsOriginAllowed((host) => true);
}));
...
}
public void Configure(IApplicationBuilder app) {
...
app.UseCors(specificOriginsPolicy);
...
}
I am still refining this solution, but it works well so far. I may turn the utility into a background service that is updating the token on an interval. Or I may turn the utility into a Shell, and then use it instead of the script. Either way, you get the idea.
LESSON:
I know I could have done this as a UWP or WPF application and avoided all these issues, but the main goal was to learn REACT. I learned a lot. I would do it again. It is shocking just how little code there is to my REACT application now that it is done. I believe REACT could be used for many similar scenarios.
You could refer to this sample, it uses client credential flow(i.e. client id and secret you want) to get the access token, just change the resource to the one you want to get token for, the sample gets the token for Microsoft Graph.
auth.getAccessToken = function () {
var deferred = Q.defer();
// These are the parameters necessary for the OAuth 2.0 Client Credentials Grant Flow.
// For more information, see Service to Service Calls Using Client Credentials (https://msdn.microsoft.com/library/azure/dn645543.aspx).
var requestParams = {
grant_type: 'client_credentials',
client_id: config.clientId,
client_secret: config.clientSecret,
resource: 'https://graph.microsoft.com'
};
// Make a request to the token issuing endpoint.
request.post({ url: config.tokenEndpoint, form: requestParams }, function (err, response, body) {
var parsedBody = JSON.parse(body);
console.log(parsedBody);
if (err) {
deferred.reject(err);
} else if (parsedBody.error) {
deferred.reject(parsedBody.error_description);
} else {
// If successful, return the access token.
deferred.resolve(parsedBody.access_token);
}
});
return deferred.promise;
};

Trying to access SFB through APIs

I am trying to send and receive messages to/from SFB (Skype for business) through UCWA web apis but facing access denied issue.
1) I created Azure AD application.
2) I am able to login successfully and fetching access token and refresh token.
3) But when i try to auto-discover with the above access token, it is giving 403 access denied error. Please see the request in the code below.
Is it because of SFB deprecation or something else? Please help.
let options = {
method: 'GET',
url: 'https://webdirin1.online.lync.com/Autodiscover/Autodiscoverservice.svc/root/oauth/user',
headers:
{
accept: 'application/json',
'x-requested-with': 'XMLHttpRequest',
'access-control-allow-origin':'*',
cors:true,
'Access-Control-Allow-Origin':'*',
'x-ms-diagnostic': `PNQIN100EDG08.infra.lync.com`,
'x-ms-origin': `MAAIN100EDG03.infra.lync.com`,
authorization: `Bearer ${users[0].access_token}`
}
};
In general, 403 means you have provided the wrong access token.
Please refer to Requesting an access token using implicit grant flow.
Note: It seems that the request in the official example is incomplete, you can refer to mine:
https://login.microsoftonline.com/{Tenant ID}/oauth2/authorize?response_type=id_token+token&client_id={Client ID}&redirect_uri={Reply URL}&state=8f0f4eff-360f-4c50-acf0-99cf8174a58b&resource=https://webdirXX.online.lync.com&nonce=12434152345
Modify the above string based on your need and put it directly into the browser for access and log in with your admin account. The access token will be included in the string returned in the address bar.
At last, you could use the access token to call
Get https://webdirXX.online.lync.com/Autodiscover/AutodiscoverService.svc/root/oauth/user.
Are there any CORS issues?
check in the response header if you see something like : "Service does not allow a cross domain request from this origin."
Refer: https://ucwa.skype.com/documentation/itadmin-configuration

Bing News API Error 403 - InsufficientAuthorization

I've been trying to work on a project with angularJS and the bing search api's v7. However, whenever I make a http request to the provided URL ("https://api.cognitive.microsoft.com/bing/v7.0/news?") I get an error 403 response with error message: "Insufficient Authorization". I looked at the Microsoft docs and they said the error could be caused "if the subscription key has been disabled or has expired."
However, I just got this key today. It seems odd that it would have expired or already been deactivated. Not sure what could be going on here.
Relevant code is
var params = {
// Bing news search request parameters
"q": "query",
"count": "3",
"offset": "0",
"mkt": "en-us",
"safesearch": "Moderate"
};
$http({
method: 'GET',
url: "https://api.cognitive.microsoft.com/bing/v7.0/news?"+ $httpParamSerializer(params),
headers:{"Ocp-Apim-Subscription-Key":"my_subscription key"}
}).then(
function successCallback(response) {
console.log('success');
console.log(response);
}, function errorCallback(response) {
console.log('error');
console.log(response);
});
What could be causing this issue? If it's a problem with the API, are there any other good news-gathering API's available?
Why is "https://api.cognitive.microsoft.com/bing/v7.0/news" giving 403
- InsufficientAuthorization?
The v5.0 url works. The v7.0 simply won't. This seems to be Microsoft.
After all it is still in preview.
On either v5.0 or 7.0 /search works.
And for /trendingtopics you need to pass ?mkt= with en-US or zh-CN.
Do I need an azure account to access the API?
No, you do not need an Azure account. The API keys generated can be used in the API test from Microsoft(if available), in Postman or any other mechanism that makes the request for you.
https://api.cognitive.microsoft.com/bing/v7.0/news is a different end-point. It is meant for category news search: https://dev.cognitive.microsoft.com/docs/services/e5e22123c5d24f1081f63af1548defa1/operations/56f02400dbe2d91900c68553.
The https://api.cognitive.microsoft.com/bing/v7.0/news/search defined here: https://dev.cognitive.microsoft.com/docs/services/e5e22123c5d24f1081f63af1548defa1/operations/56b449fbcf5ff81038d15cdf is the correct end-point for getting news for a given query.
Both the endpoints work for their respective usecases - the one without "/search" for category news (optional "category" parameter) and one with "/search" for the intended usecase here.

No 'Access-Control-Allow-Origin' header in Angular

I am using a REST API call to query all open issues in the JIRA server. Below is the query:
https://jiraserver/rest/api/2/search?jql=project IN ("Project Name") AND status IN (open)
When I pass the query in Google Chrome advanced REST client, I am able to get a JSON response but when I give this in a HTTP call in Angular.js it returns:
No 'Access-Control-Allow-Origin' header error.
I am new to Angular.js, and I am not able to recognize what is going wrong. Can someone help me out?
Access-Control-Allow-Origin
This comes when the server is configured for not to share data for cross domain requests.
When I pass the query in google chrome advanced rest client , am able to get a json response
Yes, All the rest clients can access data because they are allowed to do so, but for ajax You should enable the CORS to share data cross domain.
Another way is to create proxy
What one can do is, create a proxy at server which can in turn calls the other url and get the data and send it. You can do something like this:
<?php
// suppose this page as "proxy.php"
header('Content-Type: application/json');
$jsonData = json_decode(file_get_contents('https://jiraserver/rest/api/2/search?jql=project'));
echo $jsonData;
?>
Then in the ajax you can use ajax to send a request to this file:
$http({
method: 'POST',
url: '/proxy.php'
}).then(function successCallback(response) {
console.log(response);
}, function errorCallback(response) {
console.log(response);
});
Since JIRA 6.0 you could configure a whitelist for CORS, see JIRA-30371:
CORS has been supported in the JIRA REST API since JIRA 6.0 for JIRA Server. If you are a JIRA Server customer, simply go to the "Whitelist" section of JIRA Administration and add the domains you wish to request resources from. Note: You must have the System Administrator global permission to access this section of JIRA administration.
Unfortunately, this domain whitelist is not available in JIRA Cloud for security reasons. We are currently exploring alternative ways to allow certain requests that would not expose JIRA to this attack vector. I will update this issue as soon as we have more information.
See also: Configuring the Whitelist

AngularJS: $http.post throws error

I am using Request Bin to post some data. In my controller, I have the following code:
$http.post('http://requestb.in/redacted', fooBar).
success(function(data) {
$scope.fooBarPostedSuccess = true;
}).
error(function(err) {
console.log("Error while posting to Request Bin");
console.log("Error Info : " + err);
});
This is triggered by means on a button on the UI. Now when this gets triggered, the data is not posted to Request Bin and I get this error:
XMLHttpRequest cannot load http://requestb.in/redacted.
Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin.
How do I post data to request bin through an AngularJS controller? Also, what does the above error mean?
EDIT : I wish to add here that I am using Node.js with AngularJS. Is this something to do with Node perhaps?
Ah yes... you are dealing with cross-domain scripting issues. This is not an AngularJS problem, but a browser security limitation and a VERY common friction point.
You cannot POST/PUT/DELETE to another domain (different from the one which is hosting -- localhost in your case) without doing some Cross-Origin Resource Sharing (CORS). You are limited to GET for a cross-domain HTTP request.
You have two options:
See if your API supports any cross-domain capabilities. This might be via CORS or it might be via an overloaded GET API or JSONP.
Proxy requests through your server. Since you are using Node.js, proxying REST through your server is extremely simple... you just need to set up a route handler (like /api/redacted) in your Node.js server and then make a new request to your actual API server with something like Restler (NPM package) and return the result back to your client.
Hope this helps!
EDIT:
Your API supports JSONP (Your API Docs). You should be able to use Angular's JSONP function to access your API's JSONP capabilities. (Angular.js JSONP docs).
Since you want to be able to POST to the service, you will need to use the second approach.
CORS allows both GET and POST
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
http://www.w3.org/TR/cors/
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Now, that that's out of the way...
I too have found that angular's $http won't let me POST cross domain. I was suspicious about that though because I have jquery ajax calls in other projects that can post cross domain just fine. So, I swapped my $http POST with $.ajax POST and that worked.
// $http({
// url: url,
// method: "POST",
// data: data
// })
// .success(successCallback)
// .error(errorCallback)
// ole reliable
$.ajax({
type : "POST",
url : url,
data : data,
success : successCallback,
error : errorCallback,
cache : false,
dataType : 'json',
})
You can use PutsReq instead of RequestBin. PutsReq supports CORS.

Resources