connect to API with X-Auth-Token when running local - angularjs

I'm in Angular 2 trying to connect to an API where eI do not have access server side.
I know this API and token are ok works As I converting an existing PHP app to Angular.
Here is myapp.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class DxdService {
private apiUrl = 'https://path/to/my/api/';
constructor(private _http: Http){
}
createAuthorizationHeader(headers: Headers) {
headers.append('X-Auth-Token','myvalidkey');
}
getValueFromApi(){
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this._http.get(this.apiUrl, headers)
.map(res => res.json());
}
}
This return invariably
GET https://path/to/my/api/ 401 (Unauthorized)
XMLHttpRequest cannot load https://path/to/my/api/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:3000' is therefore not allowed access. The response had HTTP status code 401.
What I can't figure out here is, did the way I set my headers x-auth-token is wrong, or did I fight agains the Access-Control-Allow-Origin CORS issue.
Considering I can't access server side to change Access-Control-Allow-Origin header response, if the way I set my headers is right, how can I keep developing locally?

You won't be able to fix this issue from a Frontend perspective, you need to access the server to make the proper adjustments and making it accesible from your Angular application
You can try implementing JSONP as the method in your request. Just keep in mind that JSONP only works with GET
$http({
method: 'JSONP',
url: url
}).
success(function(status) {
//your code when success
}).
error(function(status) {
//your code when fails
});
You can find more answers related to this issue here

Related

React, Fetch-API, no-cors, opaque response, but still in browser memory

I've been trying to make an React site, which would fetch a GET-response from API and print it out to my .html-file. I've managed to fetch the file just right, but i can't access the JSON-data server sends me.
If i use no-cors in my Fetch-request, i get an opaque response containing pretty much nothing, but if i go to Developer tools i can find my data there and read it. If i do use cors, almost same thing. I get an 403-error, but my data is in the browser memory, but my code doesn't print it out. I can find the response from Network in developer tools.
Why does the server give me an error, but still i get my data? And how can i access it, if it's in the browser?
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {data2: []}
this.apihaku = this.apihaku.bind(this)
}
componentDidMount() {
this.apihaku(),
console.log("Hei")
}
apihaku () {
fetch('https://#######/mapi/profile/',
{method: 'GET', mode:'no-cors', credentials: 'include',
headers: {Accept: 'application/json'}}
).then((response) => {
console.log(response);
response.json().then((data) =>{
console.log(data);
});
});
}
render() {
return <div>
<button>Button</button>
</div>
}}
ReactDOM.render(
<Clock />,
document.getElementById('content')
)
EDIT: Error images after trying out suggestions
https://i.stack.imgur.com/wp693.png
https://i.stack.imgur.com/07rSG.png
https://i.stack.imgur.com/XwZsR.png
You're getting an opaque response, because you're using fetch with mode: 'no-cors'. You need to use mode: 'cors' and the server needs to send the required CORS headers in order to access the response.
Fetch is doing exactly what the documentation says it's supposed to do, from Mozilla:
The fetch specification differs from jQuery.ajax() in two main ways:
The Promise returned from fetch() won’t reject on HTTP error status
even if the response is an HTTP 404 or 500. Instead, it will resolve
normally (with ok status set to false), and it will only reject on
network failure or if anything prevented the request from completing.
By default, fetch won't send or receive any cookies from the server,
resulting in unauthenticated requests if the site relies on
maintaining a user session (to send cookies, the credentials init
option must be set). Since Aug 25, 2017. The spec changed the default
credentials policy to same-origin. Firefox changed since 61.0b13.
So you need to use CORS, otherwise you get an opaque response (no JSON), and then 403 to me suggests that you haven't authenticated properly. Test your API with Postman, if I had to take a guess I'd say the API isn't sending the cookie because it's a GET request, so no matter how well you set your headers on the client it won't work. Try it as a POST instead. GET requests should really only be used to drop the initial HTML in the browser. I think for your headers use these, include the creds that the API sends and allow the domain to be different.
mode: "cors", // no-cors, cors, *same-origin *=default
credentials: "include", // *same-origin
Try this and see where is the error happening i believe in the parsing but lets check and see
fetch(https://#######/mapi/profile/, {
method: "GET",
headers: {
"Content-Type": "application/json"
},
credentials: "include"
})
.then((response) => {
console.log(response);
try {
JSON.parse(response)
}
catch(err){
console.log("parsing err ",err)
}
})
.catch((err)=>{
console.log("err ",err)
});
I had a similar issue, this kind of problem happend when a HTTP port try to send request to a HTTPS endpoint, adding a "mode:'no-cors'" doesn't do what is SOUND doing but rathere when the documentation says.
I fixed the issue by allowing in my API Application for calls from my HTTP port
(i'm using a .net 6 as an API in debugging mode, my code look like this https://stackoverflow.com/a/31942128/9570006)

How to send an HTTP GET request using Firebase and Angular?

My app uses IBM Watson Speech-to-Text, which requires an access token. From the command line I can get the access token with curl:
curl -X GET --user my-user-account:password \
--output token \
"https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api"
When I make an HTTP request using Angular's $http service I get a CORS error:
var data = {
user: 'my-user-account:password',
output: 'token'
};
$http({
method: 'GET',
url: 'https://stream.watsonplatform.net/authorization/api/v1/token?url=https://stream.watsonplatform.net/speech-to-text/api',
data: data,
}).then(function successCallback(response) {
console.log("HTTP GET successful");
}, function errorCallback(response) {
console.log("HTTP GET failed");
});
The error message says:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:8080' is therefore not allowed
access. The response had HTTP status code 401.
As I understand, it's not possible to do CORS from Angular; CORS has to be done from the server. I know how to do CORS with Node but I'm using Firebase as the server.
Firebase has documentation about making HTTP requests with CORS. The documentation says to write this:
$scope.getIBMToken = functions.https.onRequest((req, res) => {
cors(req, res, () => {
});
});
First, that doesn't work. The error message is functions is not defined. Apparently functions isn't in the Firebase library? I call Firebase from index.html:
<script src="https://www.gstatic.com/firebasejs/4.3.0/firebase.js"></script>
My controller injects dependencies for $firebaseArray, $firebaseAuth, and $firebaseStorage. Do I need to inject a dependency for $firebaseHttp or something like that?
Second, how do I specify the method ('GET'), the URL, and the data (my account and password)?
if you want to send credentials with angular, just set withCredentials=true. I am also using CORS with Angular v4, for your HTTP header error, you are right. Header Access-Control-Allow-Origin must be added on server side, check if you have settings in your api to allow certain domains, urls, pages, because google api's has this function, so check where you get token there should be some settings.
Here is example, how I am calling API with CORS, using typescript:
broadcastPresense(clientId: string) {
const headers = new Headers({'Content-Type':'application/json','withCredentials':'true'});
return this.http.post('http://localhost/api.php',
{
'jsonrpc': '2.0',
'method': 'somemethod',
'params': {'client_id': clientId},
'id': CommonClass.generateRandomString(16)
},{headers: headers, withCredentials:true}).map(
(res: Response) => {
console.log(res);
const data = res.json();
console.log(data);
if (data.error == null) {
return data.result;
} else if (data.error != null) {
throw data.error;
}
throw data.error;
}
).catch(
(error) => {
this.router.navigate(['/error',3],{queryParams: {desc:'Server error'}});
return Observable.throw(error);
}
);
}
Hope it helps :)
The answer is to use Cloud Functions for Firebase, which enable running Node functions from the server. Then you use the Node module request to send the HTTP request from Node.

Axios always fails for some reason

handleLoginClick(event) {
var apiBaseUrl = "http://localhost:8000/api-token-auth/";
var self = this;
var payload={
"email": "myusername",//this.state.username,
"password": "mypassword"//this.state.password
};
axios.post(apiBaseUrl, payload)
.then(function (response) {
alert('success')
})
.catch(function (error) {
alert('NO') . // <----- always reaches here.
console.log(error);
});
}
For some reason, this code always fails and alert 'NO'. the endpoint I'm trying is valid, and accessible with those parameters from curl. Any ideas what's wrong?
I do have:
import axios from 'axios';
Console output:
XMLHttpRequest cannot load http://localhost:8000/api-token-auth/. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
Your requests are not readable by the JavaScript due to Same-origin policy. In order to allow cross-domain requests, you must enable Cross-Origin Resource Sharing (CORS) headers server-side.
For development purposes, you may use a proxy (own, or a simple service like crossorigin.me) or the Allow-Control-Allow-Origin: * Chrome Extension.
To allow calls from any domain you must serve the following header (* - means any, however, you can list domains here):
Access-Control-Allow-Origin: *
To enable CORS in Django app please see django-cors-headers or django-cors-middleware.

Angular 2 http header seems to be generated or send incorrectly

I am trying to send a request to my backend which uses HTTP Basic auth for authentication.
For testing purposes
username: user
password: password
so the correct header is:
Authorization: Basic dXNlcjpwYXNzd29yZA==
I have tested the request with this header in Chrome Advanced Rest Extension and it works:
I generated the request in Angular2 like this:
public getCurrentCounter() {
console.log("Method getCurrentCounter() in CounterService called");
var request = this.backendURL + "counter";
var header = this.generateHeader(this.username, this.password);
console.log(header);
return this._http.get(request, {
headers: header
})
.map(res => res.json());
}
/**
* Generate HTTP header using HTTP basic Auth
*/
private generateHeader(username, password) {
var base64Creds = btoa(username + ":" + password);
var auth = 'Basic ' + base64Creds;
console.log(auth);
var authHeader = new Headers();
authHeader.append("Authorization", auth);
return authHeader;
}
I logged the generated Header Object and it looks like this:
Still I get this response:
XMLHttpRequest cannot load http://localhost:8080/counter. Response for preflight has invalid HTTP status code 401
Anybody an idea what could be wrong?
The problem is related to CORS which is not enabled on the server side.
Your service must answer an OPTIONS request with headers like these:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]
Here is a good doc: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server
also look at this: Chrome v37/38 CORS failing (again) with 401 for OPTIONS pre-flight requests
for the basic authentication in angularjs 1.x could you please try:
service.SetCredentials = function (username, password) {
var authdata = Base64.encode(username + ':' + password);
$http.defaults.headers.common = {"Access-Control-Request-Headers": "accept, origin, authorization"}; //you probably don't need this line. This lets me connect to my server on a different domain
$http.defaults.headers.common['Authorization'] = 'Basic ' + authdata; // jshint ignore:line
};
for the Angularjs 2.x version please have a look at:
Angular2 - set headers for every request
I found extending the BaseRequestOptions very interesting:
class MyRequestOptions extends BaseRequestOptions {
constructor () {
super();
this.headers.append('Authorization', 'Basic ' + authdata);
}
}
I hope it helps
So turns out the problem was on the backend.
The backend expected the OPTIONS request to be base authenticated as well, but since the OPTIONS request sent from angular2 doesn't have the Authentication Headers, we got an 401 response.
Limiting the request types which are expected to be authenticated on the backend fixed the issue.
Either you forgot to import the Headers class. Either you need to set the withCredentials property to true on the underlying XHR of your request.
To do that, you can do the following. First extend the BrowserXhr:
#Injectable()
export class CustomBrowserXhr extends BrowserXhr {
constructor() {}
build(): any {
let xhr = super.build();
xhr.withCredentials = true;
return <any>(xhr);
}
}
and override the BrowserXhr provider with the extended class:
bootstrap(AppComponent, [
HTTP_PROVIDERS,
provide(BrowserXhr, { useClass: CustomBrowserXhr })
]);
Can you verify but it seems that you getting this error from an OPTIONS request ? and not a GET request.

AngularJS POST fails with No 'Access-Control-Allow-Origin' when using data payload object but works using query params like payload

I am facing a weird issue. I am running my angularjs app in nodejs server locally which calls a POST API from my app located on Google App Engine. The API is configured with all CORS headers required as follows:
def post(self):
self.response.headers.add_header("Access-Control-Allow-Origin", "*")
self.response.headers.add_header("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS")
self.response.headers.add_header("Access-Control-Allow-Headers", "X-Requested-With, content-type, accept, myapp-domain")
self.response.headers["Content-Type"] = “application/json; charset=utf-8”
GET requests to the API work without issues.
POST requests to the API work but ONLY when I send the post data as a 'string of params' and NOT when post data is sent as an object which is the right way to do. Eventually I need to be able to upload pictures using this API so the first solution below might not work for me. Please help!
METHOD 1: This works:
postMessageAPI = "https://myapp-qa.appspot.com/message";
var postData = "conversationid=1c34b4f2&userid=67e80bf6&content='Hello champs! - Web App'";
var postConfig = {
headers: {
"MYAPP-DOMAIN" : "myapp.bz",
'Content-Type': 'application/json; charset=UTF-8'
}
};
$http.post(postMessageAPI, postData, postConfig).
success(function(data){
$log.log("POST Message API success");
}).
error(function(data, status) {
$log.error("POST Message API FAILED. Status: "+status);
$log.error(JSON.stringify(postData));
});
METHOD 2: This fails:
postMessageAPI = "https://myapp-qa.appspot.com/message";
var postData = ({
'conversationid' : '1c34b4f2',
'userid' : '67e80bf6',
'content' : 'Hello champs! - Web App'
});
var postConfig = {
headers: {
"MYAPP-DOMAIN" : "myapp.bz"
'Content-Type': 'application/json; charset=UTF-8'
}
};
$http.post(postMessageAPI, postData, postConfig).
success(function(data){
$log.log("POST Message API success");
}).
error(function(data, status) {
$log.error("POST Message API FAILED. Status: "+status);
$log.error(JSON.stringify(postData));
});
When I use METHOD 2 it fails with the following error in the console:
XMLHttpRequest cannot load https://myapp-qa.appspot.com/message.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://0.0.0.0:8000' is therefore not allowed access.
Please let me know if you have any solution. Thanks in advance.
The issue is most likely with Angular sending a pre-flight OPTIONS request to check the access headers from the server. I am not sure how OPTIONS requests are handled in your API, but I am betting these headers are not being added. I suggest installing Fiddler to monitor the actual requests to see what is going on with the headers. You may only be adding them to your POST responses.
See this answer for details on why METHOD 1 may work in this scenario, while METHOD 2 does not.
Here are some more details about pre-flight requests.

Resources