How to connect my server with my ember cli App - ember.js

my server is working on the port :3000 and i am using node, express on server side and this is working fine while using curl or REST client.
ember cli server is working on port :4200
I added this code on environment.js for connecting server to client.
contentSecurityPolicy: {
'default-src': "'none'",
'script-src': "'self' 'unsafe-inline' 'unsafe-eval'",
'font-src': "'self'",
'connect-src': "'self' http://localhost:3000",
'img-src': "'self'",
'report-uri':"'localhost'",
'style-src': "'self' 'unsafe-inline'",
'frame-src': "'none'"
}
and my application adapter code is:
import JSONAPIAdapter from 'ember-data/adapters/json-api';
export default JSONAPIAdapter.extend({
"host": "http://localhost:3000",
"namespace": "api"
});
running my ember server on this way
ember server --proxy http://127.0.0.1:3000
on controller i use ajax call :
$.ajax({
type: 'POST',
url: '/blog',
contentType: 'application/json',
data: param,
processData: false,
success :
});
it getting an error:
POST http://localhost:4200/blog 500 (Internal Server Error)
i have done all these but i don't know why it's not working.
please help me if you have any idea for that .

You need to understand the difference between two ways to connect to your API:
Proxy
You can use the --proxy option of ember-cli to proxy all requests made to your ember development server that are not handled by ember to your backend.
This is a good way to do this because from the view of your application and browser your backend and your application are on the same host.
If you do so you should not specify the host on your adapter or add your backend to your CSP configuration.
However you can do this only if in production you serve your ember application from your backend.
CORS
The other way to connect to your backend is to make your requests directly to another origin. However this will be CORS requests so they need additional configuration on your backend!
If you want to directly fetch data from a different origin, in browser land your server there needs to deliver additional HTTP headers (CORS Headers).
However you should do this only do this if you will not deliver your frontend from your backend server in production.
If you want to use CORS you should not specify the --proxy flag when you run ember serve, but specify the host and configure your CSP correctly.
I also mentioned that you missed a = in your ember serve call.
Its not ember server --proxy http://127.0.0.1:3000 but ember server --proxy=http://127.0.0.1:3000!
Generally if you do ember serve --proxy=http://127.0.0.1:3000 then a HTTP AJAX GET call to http://localhost:4200/blog should return the same as http://127.0.0.1:3000/blog.
To test this I recommend you to use a HTTP development client like this one.
I assume you get also a HTTP 500 on http://127.0.0.1:3000/blog. Then something is wrong on your backend. Maybe the correct URI is http://127.0.0.1:3000/blogs? that would be embers default.
If you do a call directly to http://127.0.0.1:3000/blogs from your ember application this will however always result in an error if you don't deliver the CORS headers.

Related

How to troubleshoot zoom Oauth2 integration with ngrok and cookie usage in a MERN stack application?

I'm testing a local zoom app build. To be specific, zoom docs differentiate their app types, and what i want is a web view opened in the zoom client app view, therefore what ive developed is technically referred to as a "Zoom App" (more info here)
In the zoom docs, it mentions you cant setup the redirect urls to be localhost:8080 for example, it has to be set up with ngrok or a public url (more info here)
So ngrok is properly working (setup with cli command ngrok http 8080 ). I also tried this command with other values set for the --host-header flag. some attempts include --host=header=rewrite, --host-header=localhost, and --host-header=localhost:8080
Express server on :8080, react client on :3000
Express is linked into multiple oauth providers, google and zoom are 2 examples. google integration is working properly with the same oauth middleware, and route controllers on the api side (but google integration doesnt require the ngrok setup)
with zoom, and the ngrok setup, the request to the /callback route once the user confirms the zoom authorization, everything works fine, except the cookie that is returned by the server setting the header set-cookie is not set into the browsers application storage. nothing is registered in the cookies tab for oauth that goes through ngrok
the very next request confirms no cookie is stored, as there is no cookie: ... header in the request. interestingly, there are no errors on this cookie that is sent in the initial response headers of the servers /callback endpoint
Oauth Requests through Ngrok:
Oauth Requests without Ngrok:
Heres the controller that run after successful oauth verification/tokenization, triggered in both cases:
const oauth = catchAsync(async (req, res) => {
const user = req.user;
const tokens = await tokenService.generateAuthTokens(user);
res
.cookie('refreshToken', tokens.refresh.token, {
maxAge: tokens.refresh.maxAge,
httpOnly: true,
sameSite: "none",
secure: true,
// domain: "8796-2603-6011-223-7a04-2830-4c71-6f20-54c0.ngrok.io" // test
})
.redirect(`${config.clientURL}/app`)
});
I tried manually setting the domain value of the cookie config. Some values i tried include
localhost
localhost:8080
some-ngrok.io
, but to no avail
Heres the devserver webpack config, which usually has nothing extra, although i did also try with all for allowedHosts
But Im hopeful for a solution that works in both production and development
module.exports = {
// Extend/override the dev server configuration used by CRA
// See: https://github.com/timarney/react-app-rewired#extended-configuration-options
devServer: function (configFunction) {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
// config.headers = {
// // "Cross-Origin-Embedder-Policy": "credentialless",
// // "Cross-Origin-Opener-Policy": "same-origin",
// // 'Cross-Origin-Resource-Policy': 'cross-origin',
// // 'Access-Control-Allow-Origin': '*'
// };
config.allowedHosts = ['all']
return config;
};
},
};
So maybe this is just a development environment issue? After all, google oauth works fine in prod/dev, maybe its just ngrok. So i've tested this by adding my live api url to the oauth redirect/allowedhost in zoom app web portal and ran this in production, and got the same issue.
Any one else go through this with a zoom app?

Cookie not being set on angular client

I have a backend app in django python and it is being served on http://localhost:8000.
I have a angular frontend which is being served on http://localhost:4200.
I have disabled CORS on django.
On hitting the login api on http://localhost:8000/auth/login/, I am getting a valid response
along with the Set-Cookie header.
Here is my angular code to print the cookies:
this.http.post<any>('http://localhost:8000/auth/login/', this.LoginForm, { observe: 'response' }).subscribe(response => {
console.log("response is ", response);
var cookies = this.cookieService.getAll();//('cookies');
console.log("cookies is :", cookies);
It prints an empty object on console.
How do I make this work? I want to use cookies for authentication.
You are trying to set cross domain cookies, which will not work straight away. There are a few steps to follow to be able to do that.
Set withCredentials: true when making the authentication request from angular
this.http.post<any>('http://localhost:8000/auth/login/', this.LoginForm, { observe: 'response', withCredentials: true })
Configure your server to return the following CORS headers: Access-Control-Allow-Credentials: true and Access-Control-Allow-Origin: http://localhost:4200
Note
One of the cookies that you are setting is HttpOnly. As such, you cannot access it from Javascript (see documentation).
You may not need to access the cookies with JS anyway. If you just want to send the cookies in the next API requests, just pass withCredentials: true to HttpClient other api calls
this.http.get('http://localhost:8000/path/to/get/resource',
{ withCredentials: true }).subscribe(response => {
Set-Cookies:
In the example in the Question, both client and server are in the same domain, localhost.
On deployment, this may not be the case.
Let us assume the domains as below,
Client : client1.client.com
Server: server1.server.com
A http request from the Angular web app in client1.client.com to https://server1.server.com/api/v1/getSomething has Set-Cookie: JSESSIONID=xyz in the response header.
The cookie will be set on server1.server.com and NOT on client1.client.com.
You can enter server1.server.com in the URL bar and see the cookie being set.
withCredentials:
There is no need for the angular app to read the cookie and send it in the following requests. withCredentials property of http request can be used for this.
Refer: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
Example:
public getSomething(): Observable<object> {
const httpOptions = {
withCredentials: true
};
return this.http.get(`${this.serverUrl}/getSomething`, httpOptions);
}
Refer: https://angular.io/api/common/http/HttpRequest
withCredentials will set the cookies from the server's domain in the requests to the server.
As mentioned before Set-Cookie: JSESSIONID=xyz in the response from server1.server.com will be set in server1.server.com. The Angular app in client1.client.com need not read it. withCredentials will take care of it.
cross domain issues:
When the server and client are in different domains, using withCredentials may not work in all browsers, as they are considered as third party cookies.
In my recent testing on May 2020, I found that withCredentials is not working in certain browsers when the client and server are in different domains.
In Safari, the issue occurs when "Prevent cross-site tracking" is enabled (by default). The issue is prevented by disabling the same. https://support.apple.com/en-in/guide/safari/sfri40732/mac
In Android apps, the issue can be avoided by using Chrome Custom Tabs instead of Android WebView. https://github.com/NewtonJoshua/custom-tabs-client , https://developer.chrome.com/multidevice/android/customtabs
Same domain:
Looks like mainstream browsers are moving to block third-party cookies.
Safari - Full Third-Party Cookie Blocking and More
Chrome (by 2022) - Building a more private web: A path towards making third party cookies obsolete
The solution is to have both the client and server in the same domain.
Client: client1.myapp.com
Server: server1.myapp.com
And in the Set-Cookie response include the root domain too.
Example: "JSESSIONID=xyz; Domain=.myapp.com; Path=/"
This will make sure the cookies are set in all cases.

Ionic app not issuing POST request

I have an interesting problem with my Ionic application with a Django API server backend.
In previous versions of my app (0.0.1-.0.0.5) this hasn't come up, but starting now I'm not able to issue a POST request to get my authentication token.
Using Ionic locally ionic serve against my deployed server, when I attempt a login, my server registers an OPTIONS /token-auth/ and then POST /token-auth/. When I run the application on my device ionic run android and attempt to login the server only registers the OPTIONS request but does not register the POST, according to the server logs.
I've found out this is due to CORS, it issues an OPTIONS first to see what it is allowed to issue. For some reason the OPTIONS request comes back with absolutely nothing. In my other Django Rest Framework projects, the OPTIONS comes back with a proper response. I think this is related, but it's strange that it works from localhost to the deployed server with the OPTIONS request responding the same.
My login function is pretty basic:
$http({
method: 'POST',
url: config.apiUrl + 'token-auth/',
data: {
"username": usernameElement[0].value,
"password": passwordElement[0].value
}
}).then(function(data) {
console.log('success', data);
...
}, function(err) {
console.log('error', err);
...
});
Another thing I find interesting is that it runs the error function but instead of giving me back a normal error object in the err variable, I get back what appears to be the request object. I have never seen this before.
{
"data":null,
"status":0,
"config":{
"method":"POST","transformRequest":[null],
"transformResponse":[null],
"url":"http://example.com/api/token-auth/",
"data":{
"username":"myuser",
"password":"mypassword"
},
"headers":{
"Accept":"application/json, text/plain, */*",
"Content-Type":"application/json;charset=utf-8"}
},
"statusText":""
}
Runs fine local app to deployed server
Device app to deployed server doesn't register POST request, only OPTIONS
AJAX error return is the request object, not the error response object
I've been stuck on this for a couple days now and am looking for any ideas.
This was in fact a CORS problem. After debugging through django-cors-headers I found that it was not passing the CORS. I later found out that instead of serving on http://192.168.1.36:8100 for some reason ionic switched to serve on http://192.168.1.10:8100

How to setup development environment for Ember.js + Express

I'm in the process of splitting into two different projects an Ember.js app and its Express REST API counterpart. I assumed that things would be cleaner this way.
Until then, my Express app was both serving REST endpoints and all static files like index.html and app.js. But now, ember-cli is in charge of serving the static files and the Express app handles authentication + REST.
The last issue I'm having is that I now have two different ports: ember-cli uses http://localhost:4200 and express uses http://localhost:3333. When I get the session cookie from Express upon authentication, it's never being sent on subsequent request because of same origin policy (see: How do I send an AJAX request on a different port with jQuery?).
Now if I understand correctly I have two solutions:
Setup Express to support JSONP and make sure Ember uses it too
Install a local Nginx or Apache and setup a proxy pass
The first solution is not ok because after deployment both apps will use the same domain/port. The second solution would probably work but it seems a bit ridiculous to ask developers to install a local web server to simply run an app.
I'm sure many of you have encountered that issue before. What would you suggest to make development easy?
Thanks!
Hmm. Seems like I found another solution:
Following instructions found there: http://discuss.emberjs.com/t/ember-data-and-cors/3690/2
export default DS.RESTAdapter.extend({
host: 'http://localhost:3333',
namespace: 'api',
ajax: function(url, method, hash) {
hash = hash || {}; // hash may be undefined
hash.crossDomain = true;
hash.xhrFields = { withCredentials: true };
return this._super(url, method, hash);
})
});
You will also need to add the following headers in the Express app:
// Add support for cross-origin resource sharing (localhost only)
app.all('*', function(req, res, next) {
if (app.get('env') === 'development') {
res.header('Access-Control-Allow-Origin', 'http://localhost:4200');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
}
next();
});
That's it! Last step is to make sure that Ember uses CORS only in the dev environment.
UPDATE
Ember-cli now has an integrated proxy feature that makes all the above obsolete.
From documentation: "Use --proxy flag to proxy all ajax requests to the given address. For example ember server --proxy http://127.0.0.1:8080 will proxy all your apps XHR to your server running at port 8080."

EmberJs as a PhoneGap with external API

please can you advise on the following:
I have a web application written in emberjs with Rails as back-end. And now I'm going to port this application with phonegap to iOS, and the thing that I'm struggling is how to set my API endpoint that will be working in iPhone?
As I understand EmberJs when used on the web via browser, uses your current location to issue API requests, but this approach doesn't working when using the application as iOS app.
I'm really looking for some elegant solution to simply replace the host name or something?
Thanks for help!
UPDATE:
This one works for changing the API URL
DS.RESTAdapter.reopen({
url: 'http://somedomain.com'
});
But now, there is access-controll issue:
Origin http://somedomain.com is not allowed by Access-Control-Allow-Origin.
Since you haven't posted any code on how your adapter is configured, this is the right way to set a custom url for your adapter:
DS.RESTAdapter.reopen({
url: 'https://somedomain.com/api'
});
Then if you have a model e.g. App.User, the requests for the list of App.User would now go to https://somedomain.com/api/user/ and for a specific user id to https://somedomain.com/api/user/123 respectively.
Update
When testing from the browser you have to start the browser (assuming chrome) with the flag --disable-web-security to make cross origin work. But in real live you have to configure your server to set the response HTTP HEADERS using:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, ...
So in the case of rails you could do something like this to configure your controllers serverside to accept cross origin requests and set the headers accordingly:
...
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT' # etc. etc.
headers['Access-Control-Max-Age'] = "1728000"
end
...
For more extensive examples on how to configure CORS for rails you could search for "CORS for JSON and Rails" for example.
Hope it helps