No 'Access-Control-Allow-Origin' header with Microsoft Online Auth - javascript

I am trying to make a simple request to get an access token using the Microsoft graph OAuth endpoint. When I send the simple request below I get
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:8080/myapprunninglocally' is therefore not allowed access.**"
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "https://login.microsoftonline.com/common/oauth2/authorize?client_id=<client_id>&scope=wl.signin%20wl.calendars_update&response_type=token&redirect_uri=localhost:8080/myapprunninglocally", true);
xhttp.send();
I have also registered this app using Microsoft Azure Directory, requested ALL permissions, and used the delegated client_id.
I have read up on CORS and I am aware Cross-Origin Policies however, I'm aware there are APIs which expose endpoints that include the 'Access-Control-Allow-Origin' in their response headers. Is anyone able to help?

Your not going to be able to run that from the client. Part of the CORS setup requires that microsoftonline.com adds your domain to their CORS supported whitelist.
I would suggest that you make a call a service on your server, which then makes the request server to server.

To integrate AAD in javascript, we suggest you to use azure-activedirectory-library-for-js which is a library in javascript for frontend to integrate AAD with a ease.
There are 2 options we need to pay attention on before we use ADAL for JS:
According the node at https://github.com/OfficeDev/O365-jQuery-CORS#step-6--run-the-sample:
Note This sample will not work in Internet Explorer. Please use a different browser, such as Google Chrome. ADAL.js uses an iframe to get CORS API tokens for resources other than the SPA's own backend. These iframe requests require access to the browser's cookies to authenticate with Azure Active Directory. Unfortunately, cookies are not accessible to Internet Explorer when the app is running in localhost.
Enable the oauth2AllowImplicitFlow of your Azure AD application. Refer to https://crmdynamicsblog.wordpress.com/2016/03/17/response-type-token-is-not-enabled-for-the-application-2/ for the detailed steps.
Here is the code sample to acquire access token from Microsoft Graph:
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.14/js/adal.min.js"></script>
<body>
login
access token
</body>
<script type="text/javascript">
var configOptions = {
tenant: "<tenant_id>", // Optional by default, it sends common
clientId: "<client_id>",
postLogoutRedirectUri: window.location.origin,
}
window.authContext = new AuthenticationContext(configOptions);
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
function getToken(){
authContext.acquireToken("https://graph.microsoft.com",function(error, token){
console.log(error);
console.log(token);
})
}
function login(){
authContext.login();
}
</script>

Related

Making Get request to Yammer API works using Postman tool but not with Vue-Resource

I am trying to integrate Yammer API in my Vue.JS project, for Http calls I am using Vue-Resource plugin. While making GET Http call to get posts from Yammer it gives me following error -
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I tried postman tool and that gives successful response, but when I try to run the same thing in my Vue.JS project using Vue-Resource plugin it wont work.
The Vue.JS code snippet -
function(){
this.$http.get("https://www.yammer.com/api/v1/messages/my_feed.json").then((data)=>{
console.log(data);
});
In main.vue file i have -
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer my_yammer_token')
request.headers.set('Accept', '*/*')
next()
})
Then I tried the code snippets provided by Postman tool for jquery, that too not working.
jQuery code -
var settings = {
"url": "https://www.yammer.com/api/v1/messages/my_feed.json",
"method": "GET",
"timeout": 0,
"headers": {
"Authorization": "Bearer my_yammer_token",
"Cookie": "yamtrak_id=some_token; _session=some_token"
},
};
$.ajax(settings).done(function (response) {
console.log(response);
});
Though, I found similar questions but nothing worked for me.
I am working this to resolve from last 2 days but getting failed again and again. Please guide/help me.
A browser has higher security requirements than a request in PostMan. In a browser, you are only allowed to make XHR requests to your own current host (combination of domain + port) but not to other remote hosts. To nevertheless make a request to a remote host, you can use the browser built-in CORS. By using this, your browser makes a pre-flight request to the remote host to ask if the current page is allowed to request from that host. This is done via the Access-Control response headers. In your case, this header is probably missing or not allowing your page to access, which is why the request does not go through. Please read further into that topic.
However, in your case, using CORS probably won't be a solution for two reasons: To use CORS, the remote host must present a header which allows every requesting host (*) or your specific one. If you cannot set that setting anywhere on the remote host, it won't work. Second, it is not safe to place your authorization token into client-side JavaScript code. Everybody can just read your JS code and extract the authorization token. For that reason, you usually make the actual API call from the server-side and then pass the data to the client. You can use your own authentication/authorization against your server and then use the static authorization key on the server to request the data from the remote host. In that case, you'll never expose the authorization key to your user. Also, on the server-side, you do not have to deal with CORS as it works just like PostMan or curl as opposed to a browser.

Cloud Run + Cloud Endpoints + Service Account Authentication – works in curl but doesn't when using fetch API in JS

I've configured a cloud endpoint that executes a GCF. Everything works fine when the cloud run service is allowing allUsers to call the API.
Once I remove the allUsers and authenticate using the service account, I get 403 errors showing up in the Cloud run console:
The request was not authenticated. Either allow unauthenticated invocations or set the proper Authorization header. Read more at https://cloud.google.com/run/docs/securing/authenticating
Chrome JS console shows the following error message:
Access to fetch at 'https://.run.app/do-this&key=' from origin
'http://0.0.0.0:8080' has been blocked by CORS policy: Response to preflight request doesn't
pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch
the resource with CORS disabled.
This is my JS code running in the browser:
let options: RequestInit = {
headers: {
'Authorization': `Bearer ${token}`,
},
}
const result = await fetch(fetchURL, options);
When running curl with the same token, I get the expected response
curl -H "Authorization: Bearer ${token}" 'https://<my-api>.run.app/do-this&key=<key>'
For completeness here is also the endpoints yaml
swagger: '2.0'
info:
title: My first widget
description: This is a great widget
version: 1.0.0
host: <my-api>.run.app
schemes:
- https
produces:
- application/json
paths:
/do-this:
get:
summary: Do-this
operationId: doit
x-google-backend:
address: https://<project-id>.cloudfunctions.net/do-that
responses:
'200':
description: A successful response.
schema:
type: string
'403':
description: An error occurred
schema:
type: string
security:
- api_key: []
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
Command to update esp:
gcloud run services update <my-api> --set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--rollout_strategy=managed,--cors_preset=basic" --project=<project-id> --platform=managed --region=europe-west1
Update
Enabling cors browser side did not help.
The Google docs mention that it should be possible to call from outside GCP
If you're invoking a service from a compute instance that doesn't have access to compute metadata (e.g. your own server), you'll have to manually generate the proper token:
Self-sign a service account JWT with the target_audience claim set to the URL of the receiving service.
Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the above URL.
Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the service.
Although Identity-Aware Proxy is not yet supported for Cloud Run (fully managed), you can examine the Identity-Aware Proxy sample code for code examples of the steps above.
The end-users section: mentions CORS though
When you build a web app, you have to account for Cross-Origin Resource Sharing (CORS) issues. For example, CORS preflight requests are sent without an Authorization header, so they are rejected on a non-public service. Because the preflight requests fail, the main request will also fail.
To work around this, you can host your web app and service(s) on the same domain to avoid CORS preflight requests. You can achieve that by using Firebase Hosting.
I tried hosting the JS script and HTML on Firebase hosting, yet the issue persists.
Another question that comes to mind is: do I need to set OAuth alongside API key authentication in the open api specification?
Update 2
This discussion suggests it is not possible to use Cloud Run with Authentication supporting CORS. I'm yet wondering why it's possible in curl. I'm using a service account token for auth, not end user.
The cors aren't activated for Cloud Endpoint. update your openAPI spec like this
swagger: '2.0'
info:
title: My first widget
description: This is a great widget
version: 1.0.0
host: <my-api>.run.app
x-google-endpoints:
- name: <my-api>.run.app
allowCors: True
...
...
...
Or set no-cors check in your call as described by the error message.
I've made it work the following way:
Add oauth as a security definition to the OpenAPI spec and use it alongisde the api key for each API path
Deploy endpoint with --set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--cors_preset=basic,--rollout_strategy=managed"
In the Cloud Function, set the Access-Control-Allow-Origin to empty string '': res.setHeader("Access-Control-Allow-Origin", '')
Allow the Cloud Run container to be accessible by allUsers
While the Cloud Run container is accessible by everyone, the endpoint is taking care of the authentication.
One thing that surprised me is that the CF is automatically adding the calling host (e.g. mydomain.com) and * in the Access-Control-Allow-Origin header. Multiple items are not permitted in this header and I'm therefore getting rid of the mydomain.com and keep *.
I'm going to play around with the different options and once done will provide a how-to with all the steps involved. Any comments/suggestions are very much apprecated!
UPDATE
After digging deeper, I understand now that Access-Control-Allow-Origin is automatically added by const cors = require('cors')({origin: true});
For my use case I don't require cors inside the CFs because they're only accessible from Cloud Run ESP.
Therefore the important steps are:
add oauth to OpenAPI spec
enable cors through ESP_ARGS
allow cloud run container to be accessible by allUsers

LinkedIn OAuth redirect login returning "No 'Access-Control-Allow-Origin' header is present on the requested resource" error

I'm currently implementing OAuth login with LinkedIn in my React and Play app and am running into a CORS error when trying to redirect to the authorization page in my dev environment:
XMLHttpRequest cannot load https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin. Redirect from 'https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin' to 'https://www.linkedin.com/uas/login?session_redirect=%2Foauth%2Fv2%2Flogin-s…' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have the following setup:
Play server running at localhost:9000
React app (created via create-react-app) running at localhost:3000
My JS code calls the /auth/linkedin endpoint which is implemented as follows:
Action { implicit req: RequestHeader =>
val csrfToken = CSRF.getToken.get.value
Redirect(linkedinUrl(oauthConfig.linkedinClientId, csrfToken)).withSession("state" -> csrfToken)
}
I have my Play application set to handle CORS appropriately.
My react app just makes a request to the above endpoint via Axios:
axios.get('/auth/linkedin')
This responds with a 303 with a redirect to the LinkedIn auth page which then gives me the error.
How do I get the CORS policy working correctly in this dev setup? I've tried adding the following to my package.json as the create-react-app documentation recommends:
"proxy": "http://localhost:9000",
And I've also tried setting a request header to "Access-Control-Allow-Origin" : "*" on the redirect in the Play server with no success.
Note that going to localhost:9000/auth/linkedin redirects properly.
https://www.linkedin.com/oauth/v2/authorization responses apparently don’t include the Access-Control-Allow-Origin response header, and because they do not, your browser blocks your frontend JavaScript code from accessing the responses.
There are no changes you can make to your own frontend JavaScript code nor backend config settings that’ll allow your frontend JavaScript code to make requests the way you’re trying directly to https://www.linkedin.com/oauth/v2/authorization and get responses back.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains in more detail but the gist of it is: for CORS, the server the request is being sent to must be configured to send the Access-Control-Allow-Origin response header, nor your own backend server.
2019-05-30 update
The current state of things seems to be that when needing to do LinkedIn authorization, you’ll have to initiate the request from your backend code. There’s no way you can do it from your frontend code, because LinkedIn no longer provides any support for it at all.
LinkedIn did previously provide some support for handling it from frontend code. But the page that documented it, https://developer.linkedin.com/docs/getting-started-js-sdk, now has this:
The JavaScript SDK is not currently supported
And https://engineering.linkedin.com/blog/2018/12/developer-program-updates has this:
Our JavaScript and Mobile Software Development Kits (SDKs) will stop working. Developers will need to migrate to using OAuth 2.0 directly from their apps.
So the remainder of this answer (from 2017-06-13) has now become obsolete. But it’s preserved below for the sake of keeping the history complete.
2017-06-13 details, now obsoleted
Anyway https://developer.linkedin.com/docs/getting-started-js-sdk has official docs that explain how to request authorization for a user cross-origin, which appears to be just this:
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: [API_KEY]
onLoad: [ONLOAD]
authorize: [AUTHORIZE]
lang: [LANG_LOCALE]
IN.User.authorize(callbackFunction, callbackScope);
</script>
And https://developer.linkedin.com/docs/signin-with-linkedin has docs for another auth flow:
<script type="in/Login"></script> <!-- Create the "Sign In with LinkedIn" button-->
<!-- Handle async authentication & retrieve basic member data -->
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", getProfileData);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
IN.API.Raw("/people/~").result(onSuccess).error(onError);
}
</script>
I ran into a similar problem, so let's divide this problem into detailed steps
Hit request to get the code(from frontend)
now send this code to the backend
In the backend, make another call to LinkedIn OAuth API and get the access token
With this access token make 3 separate calls to get the name, profile picture
and email of the user(yes you heard that right you need to make 3 separate calls and also the response JSON format is not very appealing)
Visit this for the detailed step-by-step process, it involves a lot of things. I can just share the process here but for the actual implementation visit this.
https://www.wellhow.online/2021/04/setting-up-linkedin-oauth-and-fixing.html
What could be done is:
window.location.href='http://localhost:9000/auth/linkedin'
The urlEndPoint could be directly to linkedIn's API or a back-end service which makes the call to linkedIn's API.

Issue with reading Response headers

I am trying to build a chrome extension for which i need to ping to different machines.The code which i tried with is able to read the response headers for a https site but not for http.I am new to Javascripting. Any help would be great.I understand it is a CORS issue and tried setting the headers in the client code.many forums mention it setting al the server side but where can I do in this case? Please find the code below and the plugin UI and response returned from https site in the snapshot.
Code--
url="https://www.icicibank.com/";
//url = "www.rediff.com/";
ping = new XMLHttpRequest();
ping.open("get", url,true);
//ping.setRequestHeader("Access-Control-Allow-Origin","*");
// ping.setRequestHeader("Access-Control-Allow-Credentials", "true");
ping.send(null)
ping.onreadystatechange=function() {
if (ping.readyState==4) {
alert(ping.getAllResponseHeaders());
//alertify.alert(ping.getAllResponseHeaders());
}
}
Thanks
CORS is indeed part of the server response, not the request. So you cannot "set" it on your side.
However, extensions are allowed to bypass CORS restrictions and make cross-origin requests. But for that you need to list domains you're going to connect to in manifest permissions. The user will be warned, at install time, that you'll interact with those domains.
For example, to allow requests to http://example.com and https://example.com domains regardless of CORS, you need to include in the manifest:
"permissions" : [
"*://example.com/"
],
If you can't say which sites you'll need to connect to in advance, you'll either need permissions for all urls (special permission, literally, "<all_urls>") or use Optional Permissions to request that at runtime.

Api Gateway cannot allow Access-Control-Allow-Origin

This url is in AWS API Gateway with method get and stage is well deployed.
And I enabled CORS following the aws document.
Here are my steps to enable CORS.
-Resource->action->enable CORS->
default setting ->enable CORS and replacing the CORS headers.
There is no error log in CORS result.
I am not a profesional web developer and my browser is safari.
Here is my code to query "http://my.com"
function request(idex) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", "http://my.com", true);
xmlHttp.send(null);}
The console print the error :
XMLHttpRequest cannot load "http://my.com" Origin http://example.com is not allowed by Access-Control-Allow-Origin.
If there are some mistakes in javascript request or in API Gateway deploy?
After consulting and trying each method, I found the error as following.
According to AWS document, we can not deploy our api before enabling CORS. All the settings about the header and CORS must be set before being deployed.
But the API Gateway does not block this setting nor does it show any error dialog. API Gateway will not change the header even if your setting process shows success.
The cross origin problem is from server side not javascript side. When the server does not allow request from other domains it throws cross origin error. But you said you already added CORS in aws instance
As the javascript is only accessing the service from my.com, You need to added proper domain mapping in your my.com site to tell that request will come from another domain called example.com. might be the server is not properly configured. or try if server is expecting any header.
try to see the result in any rest client like soapui, rect client plugin in chrome, etc. once you confirm that there is no problem in server, try it from javascript
To test there is a chrome plugin you can try
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en

Categories