I am developing a frontend application in AngularJS, an i'am trying to login in a elgg platform.
As described in the documentation here if you want to use HMAC auth, you have to add some custom headers. And in AngularJS I have used interceptors.
$httpProvider.interceptors.push(function($q, HMAC){
return {
// optional method
'request': function(config) {
// do something on success
console.log("Interceptor Config-> ",config);
console.log("HTTP? --> ",config.url.indexOf('http'));
if(config.url.indexOf('http') == 0){
//do something only on http requests
config.headers = config.headers || {};
config.headers['X-Elgg-apikey'] = "";
config.headers['X-Elgg-time'] = "";
config.headers['X-Elgg-none'] = "";
config.headers['X-Elgg-hmac'] = "";
config.headers['X-Elgg-hmac-algo'] = "";
}
console.log("Return -->", config);
return config || $q.when(config);
}
};
});
But the response the response i got is :
XMLHttpRequest cannot load http://localhost/elgg-1.9.8/elgg-1.9.8/services/api/rest/xml/?method=auth.gettoken.
Request header field X- Elgg-hmac-algo is not allowed by Access-Control-Allow-Headers.
I have apache on my webserver on localhost and i have configured :
<Directory />
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Request-Headers "*"
Header always set Access-Control-Request-Method "*"
AllowOverride none
Require all denied
</Directory>
I have also checked the response header and this what i got :
HTTP/1.1 200 OK
Date: Sat, 30 May 2015 13:37:07 GMT
Server: Apache/2.4.10 (Unix) OpenSSL/1.0.1j PHP/5.6.3 mod_perl/2.0.8-dev Perl/v5.16.3
Access-Control-Allow-Origin: *
Access-Control-Request-Headers: *
Access-Control-Request-Method: *
X-Powered-By: PHP/5.6.3
Cache-Control: no-cache, must-revalidate
Expires: Fri, 05 Feb 1982 00:00:00 -0500
Vary: Accept-Encoding,User-Agent
Content-Encoding: gzip
Content-Length: 81
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
I can't find where is the bug. I setted the headers response on the server but i still got this error on console.
Thanks
Related
My Nextjs app is deployed on AWS x Amplify at https://<example.com> but the js console indicates it fails to fetch data from the internal api.
The endpoint is defined in this function in a Flux Store object.
_getAssets = async () => {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API}/api/v1/assets`, {
method: 'get',
headers: {
'Authorization': `Basic ${process.env.NEXT_PUBLIC_API_TOKEN}`,
}
})
const assetsCall = await response.json()
return assetsCall.data
} catch(ex) {
console.log(ex)
return []
}
}
In my environment:
NEXT_PUBLIC_API=<example.com>
NEXT_PUBLIC_API_TOKEN=12345
The build script is "next build && next export"
The api call produces error 404.
In js console:
**GENERAL**
Request URL: <example.com>/api/v1/assets
Request Method: GET
Status Code: 404
Remote Address: 13.35.13.120:443
Referrer Policy: strict-origin-when-cross-origin
**RESPONSE HEADERS**
age: 5524
cache-control: public, max-age=0, s-maxage=2678400, must-revalidate
content-encoding: gzip
content-type: text/html
date: Tue, 15 Mar 2022 05:31:29 GMT
etag: W/"a396cb2a54d64f73533333bfa2da1e6d"
last-modified: Tue, 15 Mar 2022 05:24:37 GMT
server: AmazonS3
vary: Accept-Encoding
via: 1.1 0b3572829f6f42309f3adfa694398770.cloudfront.net (CloudFront), 1.1 fb176da9df72832dd488674f28c0a880.cloudfront.net (CloudFront)
x-amz-cf-id: g-I0JZ1hTHIfaL0yfGTX5c42i6wkxdRljRgRCD9Ei87lRb4KNsJo-w==
x-amz-cf-pop: SIN5-C1
x-amz-cf-pop: SIN5-C1
x-cache: Error from cloudfront
**REQUEST HEADERS**
:authority: <example.com>
:method: GET
:path: /api/v1/assets
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
authorization: Basic 12345
referer: https://<example.com>/vest
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
sec-gpc: 1
user-agent: Mozilla/5.0 <blabla>
My questions:
Is it correct to define NEXT_PUBLIC_API as <example.com> (the frontend URL) in my environmnent, or should the api call another URL?
Why can't the app fetch data from the api? I haven't defined custom headers in customHttp.yml, do i need to write something in this file?
I'm developing a custom html dashboard using the rally sdk and I want to populate one of the field data from a CORS origin request call.
Can you please provide some examples/links how to make CORS call using rally sdk custom html code?
I tried via ajax call it gives me 403 exception.
var usChangeSets = story.getCollection('Changesets');
console.log('usChangeSets--',usChangeSets);
usChangeSets.load({
fetch : ['Author', 'Message', 'Uri'],
callback: function(records, operation, success){
Ext.Array.each(records, function(changeset){
//Ajax api call to get details from external link
var blink ="https://[sonarqube]/job/Appdev/job/TestProject/api/json";
Ext.Ajax.request({
url: blink,
method :'GET',
crossDomain: true,
withCredentials: true,
headers : {
'Authorization': 'Basic dsasfsfxfhfj',
'Content-Type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin' : '*'
},
success: function(response){
var backToJs=JSON.parse(response.responseText);
console.log('resp data-',backToJs);
//console.log(backToJs['QueryResult'].Results);
},
failure: function(response) {
console.log('ajax call failure');
}
});
}
}
}
You need to configure your external server to allow CORS requests. The browser will automatically add the origin header to your request and then your server should respond with the appropriate cors headers (Access-Control-Allow-Origin).
Here's an example of how the rally server responds to a request from a different origin:
% http https://rally1.rallydev.com/slm/webservice/v2.0/testcaseresult/54277371431 zsessionid:_5507Kn8 origin:localhost -v
GET /slm/webservice/v2.0/testcaseresult/54277371431 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: rally1.rallydev.com
User-Agent: HTTPie/0.9.9
origin: localhost
zsessionid: _55GAn8
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: localhost
Access-Control-Expose-Headers:
CF-RAY: 38cbe03c4dd45005-DEN
Cache-Control: private,max-age=0,must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 623
Content-Type: application/json; charset=utf-8
Date: Fri, 11 Aug 2017 14:27:29 GMT
ETag: "0b0e0cdae135fc6cd32fa496d7660c756"
Expires: Thu, 01 Jan 1970 00:00:00 GMT
P3P: CP="NON DSP COR CURa PSAa PSDa OUR NOR BUS PUR COM NAV STA"
RallyRequestID: qs-app-103xz471u80pea8opfovz9g8gv.qs-app-1014978663
Server: cloudflare-nginx
Set-Cookie: __cfduid=d604a6a0fa131613b997640ead95cc5171502461649; expires=Sat, 11-Aug-18 14:27:29 GMT; path=/; domain=.rallydev.com; HttpOnly
Set-Cookie: JSESSIONID=qs-a0;Path=/;Secure;HttpOnly
Set-Cookie: SUBBUCKETID=209;Path=/;Domain=rally1.rallydev.com;Secure;HttpOnly
Set-Cookie: SUBSCRIPTIONID=209;Path=/;Domain=rally1.rallydev.com;Secure;HttpOnly
Set-Cookie: SERVERID=319fca23748f5704e88bd8741ae60476b188cf5e; path=/
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload;
Vary: Accept-Encoding
X-XSS-Protection: 1; mode=block
The current route of the request originates on localhost:3001, goes through a proxy running on that same localhost at localhost:3001/proxy, where the request is then routed to the Salesforce instance. The proxy is made using ExpressJS and the client side app is made using AngularJS. Note: I did remember to tag my security token at the end of my password (Salesforce API requirement), although when using cURL, this doesn't seem to be necessary. Here are a series of HTTP traces that will hopefully provide some clues:
HTTP Request Log from Angular.JS App:
POST /proxy HTTP/1.1
Host: localhost:3001
Connection: keep-alive
Content-Length: 234
Pragma: no-cache
Cache-Control: no-cache
X-User-Agent: salesforce-toolkit-rest-javascript/v29.0
Origin: http://localhost:3001
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: application/json, text/plain, */*
SalesforceProxy-Endpoint: https://uniquename.salesforce.com/services/oauth2/token
Referer: http://localhost:3001/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: liveagent_oref=; liveagent_ptid=3c69c2f9-139d-4439-ba6c-fd8d9dcae101; liveagent_vc=5
grant_type=password&client_id=3MVGxyzxyzxyzxyz&client_secret=99889988&username=first.last%40email.com&password=pswdwACYodaYfHs
400 Bad Request
Object {error_description: "grant type not supported", error: "unsupported_grant_type"}
Relevant Express.JS code used for proxy routing:
app.all('/proxy', function(req, res) {
var url = req.header('SalesforceProxy-Endpoint');
console.log(req.body); //prints all form data variables in JSON format
console.log(res.body); //undefined
request({url: url}).pipe(res).on('error', function(error){
//I think I may need to pipe more information using request?
console.log(error);
});
});
Request details using cURL:
curl -v https://uniquename.salesforce.com/services/oauth2/token
-d "grant_type=password" -d "client_id=3MVGxyzxyzxyzxyz"
-d "client_secret=99889988" -d "username=jfirst.last#email.com" -d "password=pswd"
> POST /services/oauth2/token HTTP/1.1
> User-Agent: curl/7.41.0
> Host: uniquename.salesforce.com
> Accept: */*
> Content-Length: 207
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 207 out of 207 bytes
< HTTP/1.1 200 OK
< Date: Wed, 29 Jul 2015 06:04:55 GMT
< Set-Cookie: BrowserId=auu1mgvHSMS1EedDEduz8Q;Path=/;Domain=.salesforce.com;Exp
ires=Sun, 27-Sep-2015 06:04:55 GMT
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Pragma: no-cache
< Cache-Control: no-cache, no-store
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
<
{
"id":"https://test.salesforce.com/id/05390530530",
"issued_at":"1438132525896197",
"token_type":"Bearer",
"instance_url":"https://uniquename.salesforce.com",
"signature":"blahblah",
"access_token":"XXXXXXXXXXXXXXXXX"
}
* Connection #0 to
host uniquename.salesforce.com left intact
As you can see, I get back a valid response from the cURL request. I suspect something is wrong with the proxy, as it may not be forwarding all the form data to Salesforce, but I'm not sure how to debug that in Express.JS. The reason I suspect this is because if I try curl https://uniquename.salesforce.com/services/oauth2/token it returns the same unsupported_grant_type error.
I finally got this working by switching to use the cors-anywhere proxy. I deploy my AngularJS app on port 8080, and the proxy on port 3001. My packages are managed using npm and grunt. Here is the code for the proxy:
var host = 'localhost';
var port = 3001;
var cors_proxy = require('cors-anywhere');
cors_proxy.createServer().listen(port, host, function() {
console.log('CORS proxy running on ' + host + ':' + port);
});
And here is how I'm making the HTTP request in AngularJS (you have to fill in your own credentials in the data object):
var login = {
method: 'POST',
url: 'http://localhost:3001/https://login.salesforce.com/services/oauth2/token',
data: 'grant_type=password&client_id='+encodeURIComponent(CLIENT_ID)+'&client_secret='+encodeURIComponent(CLIENT_SECRET)+'&username='+encodeURIComponent(EMAIL)+'&password='+encodeURIComponent(PASSWORD+USER_SECURITY_TOKEN),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*'
}
};
$http(login).success(function(response){
console.log(response);
})
.error( function(response, status){
console.log(response);
console.log("Status: " + status);
});
You can run the proxy with the node server.js command, and run the AngularJS app with grunt. I hope this helps someone out, this was a tough problem to solve.
I don't think you're proxying everything in the HTTP request. I've included some code below that will pass the request method and the request headers to your endpoint.. Also try using the 'request-debug' library so you can compare the proxy HTTP request vs your curl request and look for differences
var express = require('express');
var app = express();
var request = require('request');
require('request-debug')(request);
app.all('/proxy', function(req, res) {
var url = req.header('SalesforceProxy-Endpoint');
var options = {
url: url,
method: req.method,
headers: req.headers
}
request(options).pipe(res).on('error', function (error, response, body) {
if (!error && response.statusCode == 200) {
//TODO: do something useful with the response
} else {
console.log('ERROR: ' + error);
}
});
});
I tried angular.js and started with a web-ui for a restful api (using http basic auth). It is really nice and all except the authorization works.
Up to now I am using $http.defaults.headers.common.Authorization to set the password, but mostly the browser opens his default login-form for http-basic-auth. Another strange behaviour is that the angular-request does not contain a Authorisation-Header (neither OPTIONS nor the GET-request). I also tried to set this header on each request with the header-config, but this also didn't work.
Are there some special headers I have to set? Or do I have to set $http.defaults.headers.common.Authorization in a special context?
tools.factory('someFactory', function ($http, Base64) {
//...
factory.checkAuth = function (username, password) {
storeCredentials(username, password);
factory.initConnection();
return factory.getData();
};
factory.initConnection = function(){
var credentials = loadCredentials();
factory.authHeader = 'Basic ' + Base64.encode(credentials.username + ':' + credentials.password);
$http.defaults.headers.common.Authorization = factory.authHeader;
};
factory.getData = function () {
return $http({
method: 'GET',
url: urlBase + '/happening',
headers: {
// 'Authorization': factory.authHeader
}
});
};
Request header:
OPTIONS /v8/happening HTTP/1.1
Host: api.gospry.com
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://web.gospry.com
User-Agent: [..]
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://web.gospry.com/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Response header:
The backend was slightly modified to support CORS (Access-Control-headers and preflight-request-support).
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://web.gospry.com
Access-Control-Allow-Methods: POST, GET, PUT, PUSH, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Authorization
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=2DDC92A1B0DC57C221CDC3B7A5DC1314; Path=/v8/; Secure; HttpOnly
WWW-Authenticate: Basic realm="Realm"
X-Content-Type-Options: nosniff
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
Content-Length: 0
Date: Wed, 08 Apr 2015 18:04:04 GMT
I use an interceptor. I can't spot the issue in your code but here is what I am using:
authModule.factory('CeradAuthInterceptor',
['CeradAuthManager', function ( authManager)
{
return {
request: function (config)
{
config.headers = config.headers || {};
if (authManager.authToken)
{
config.headers.Authorization = 'Bearer ' + authManager.authToken;
}
return config;
}
};
}]);
appModule.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('CeradAuthInterceptor');
}]);
The issue you describe with the login-form is explained in the AngularJS Tips and Tricks Using ngResource with a workaround. See the Basic Authentication section:
A workaround, if appropriate, is to tell your web server to return something other than 401 on an authentication failure, and go from there. In AngularJS, a 500 (for example) will cause the $http promise to be rejected and you can handle it however you’d like. This is not recommended if you actually ever need the login prompt to occur!
I'm trying to execute a POST throw the save method. Here is my model.
app.Models.Dummy = Backbone.Model.extend({
initialize: function () {
url = 'http://anotherdomain/Hello/';
},
});
When i execute:
dummy.save({text : "greg"}, {
success : function(){
console.log('Ok!');
},
error: function(){
console.log('Error');
}
});
The request is fired with an OPTIONS header (code 200) but the POST request is never fired.
However When i execute:
$.ajax({
type: 'POST',
url: "http://anotherdomain/Hello/",
data: {text:"greg"},
success: function(r) { alert(r.Result) },
dataType: "application/json"
});
it's works!
Does i need to override something in backbone?
EDIT:
The request is:
OPTIONS http://anotherdomain/Hello/ HTTP/1.1
Host: anotherdomain
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/17.0 Firefox/17.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Origin: http://mydomain
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Pragma: no-cache
Cache-Control: no-cache
and the response is:
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 0
Server: Microsoft-IIS/7.5
Set-Cookie: ARRAffinity=611c389e4fd6c5d83202b700ce5627f6e0850faf0604f13c25903b4662919f36;Path=/;Domain=anotherdomain
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
X-Powered-By: ARR/2.5
X-Powered-By: ASP.NET
Date: Wed, 05 Dec 2012 18:44:27 GMT
That's not a valid OPTIONS response for CORS. The response needs to include headers that tell the browser what's allowed. For example (taken from MDN):
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
I know you said that the $.ajax worked but based on this CORS response I doubt that is accurate and suggest you double check. Under the covers, backbone is itself just using $.ajax