How do I get cross-site XMLHttpRequest invocations to send credentials using Sails? - authentication

I am trying to get a cross-origin front-end to behave correctly with a SailsJS back-end hosted at another domain. Specifically, the user authentication and credentials are what is broken.
Everything behaves correctly with the normal POST, GET, etc type of database behavior. For example, registration of new users already works.
However, after the user is logged in, the authentication step fails and I receive a 401 Unauthorized.
Here is what the console is telling me:
[Object, "post", "login", "https://small-change-api.herokuapp.com/user/login"]
User logged in: Object {firstName: "John", lastName: "Does", email: "test#work.com", activated: true, superUser: falseā€¦}
-- GOOD :)
[Array[0], "get", "authenticated", "https://small-change-api.herokuapp.com/user/authenticated"]
GET https://small-change-api.herokuapp.com/user/authenticated 401 (Unauthorized)
-- Not good
Now, all of this works 100% locally, but hosted on Heroku, something is going wrong :/
Here is some relevant server-side configuration:
This is in the file config/cors.js
module.exports.cors = {
allRoutes: true,
origin: 'https://thefrontendorigin.com',
credentials: true,
methods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH',
headers: 'content-type,Access-Control-Allow-Origin'
};
In the front-end withCredentials set to true:
RestangularProvider.setDefaultHttpFields({ withCredentials: true });
What else do I need to get right? It seems like it should work... :/
Let me know if I need to include any more information!
Thanks.
P.S. Both GH repos are public right now, so let me share those.
Front-end Github
Back-end Github

What I needed to do, was go into the CORS settings (Cross-Origin Requests) and add this line:
headers: 'content-type,Access-Control-Allow-Credentials'
That is the crucial bit I was missing, hope that helps somebody else.

Related

Is it possible to set an HttpOnly Cookie from one domain to another subdomain

I originally posted this question here: https://security.stackexchange.com/questions/255737/is-it-possible-to-set-an-httponly-cookie-from-one-domain-to-another-subdomain
Please keep in mind that this question is specific to cookies with the HttpOnly flag set to true.
I am pretty sure that the answer to my question is no, but I have been have a hard time finding an answer through official documentation or other posts here. Here is simple use case for some context:
Python backend web application (api.domain.com)
Frontend JavaScript SPA (app.domain.com)
post requests to api.domain.com/api/auth/login/ made from app.domain.com using axios with the correct username and password return a response with an access JWT token in the body and the response sets a refresh cookie with an HttpOnly flag [should fail, since I believe that the cookie cannot be set on app.domain.com from an API request to api.domain.com? -- this is my question]
the access token is stored in memory and passed with each API request
requests made to api.domain.com/api/auth/refresh/ are sent on a schedule to refresh the short-lived access token.
I typically host the frontend app and backend app on the same subdomain (app.domain.com) and do path-based routing with something like CloudFront or nginx, and this works well. For example, all requests starting with /api/* are sent to the backend, and all other requests are sent to the frontend app. Trying to use a separate subdomain for the API seems to fail no matter what options I use for setting the cookie on the server.
Can someone help me confirm that it is in fact not possible to set an HttpOnly cookie on a subdomain like app.domain.com from an API request hosted on api.domain.com? It would be great if anyone can also help me find where this could possibly be found in official documentation.
Searching for set httpOnly cookie across subdomains, I haven't found anything directly relevant. I also didn't find anything in these resources that directly answers my question:
https://owasp.org/www-community/HttpOnly
https://learn.microsoft.com/en-us/previous-versions//ms533046(v=vs.85)?redirectedfrom=MSDN
This is possible. In fact I just did it.
On your frontend, using Axios:
const baseURL = 'https://api.example.com';
const api = axios.create({
baseURL,
withCredentials: true,
});
On your backend, using Express:
app.use(
cors({
origin: 'https://www.example.com',
credentials: true,
}),
);
app.post('/login', async (req, res) => {
res.cookie('someCookie', someCookieValue, {
secure: true,
domain: 'example.com',
httpOnly: true,
});
});

STRAPI: Using auth0 for API Authentication in Strapi works and works not

Node: v12.16.1
Strapi: 3.0.0-beta.20.1
I'm new to strapi and have to say at first: I love it!
I want to use Strapi in addition with Angular 9 and for authentication I want to use Auth0.
For my setup with Auth0 I followed the guide in the strapi documentation and customizied my permissons.js in the user-permissions plugin.
Well!
I have a very confusing issue.
When I get back my access_token from Auth0 I put it to my calls as bearer authentication token, the request to the strapi endpoint works mostly for the first time, maybe sometimes the second and many more calls also, and then it fails. If I wait a few seconds then it works again, and after a successful call also it fails again. (also testing outside my angular-app with postman - same behavior)
If I work with the strapi jwt, it works permanantly, independet from my frequency of requests.
This is confusing me since a couple of days.
My response from strapi if it fails is
Invalid token: Token did not match with Strapi and Auth0 as catched in the error part.
If it would never work I would have to say I'm doing something wrong, but it works sometimes, so I would say my way is technically correct. But of course I will do something wrong.
Has everybody else had an issue like that by authentication with Auth0 and strapi and understand what I try to explain?
Here is my snippet of code (I would say exactly copied from docu with a bit personalization):
...
try {
const data = await axios({
method: 'post',
url: 'https://my.eu.auth0.com/userinfo',
headers: {
Authorization: ctx.request.header.authorization
}
});
console.debug('data from Auth0: ' + JSON.stringify(data.data));
// if you want do more validation test
// feel free to add your code here.
return await next();
} catch (error) {
return handleErrors(ctx, new Error('Invalid token: Token did not match with Strapi and Auth0'), 'unauthorized');
}
...
I hope anybody had a similar issue and any idea how this can be fixed. Maybe it is an issue in Auth0.
You'll have to update url: 'https://my.eu.auth0.com/userinfo' replacing my.eu with your actual subdomain on auth0.

Putting a react-django staging site behind basic auth, but auth clashes with token auth for endpoints

Whats the situation?
I've got staging site which is built with Django + React.
Parts of the API you have to login to access. I'm using Django's token authentication for that.
I then wanted to put the entire site behind basic auth, to prevent anyone of accidentally stumbling across it.
What's the problem?
This means I need to pass two authentication methods with my requests. This is possible as described here.
Authorization: Token lksdjf893kj2nlk2n3rl2dOPOnm, Basic YXNkZnNhZGZzYWRmOlZLdDVOMVhk
The token is set in my JS code after being provided to the user when they login in.
Basic authentication is triggered on the first page load, after this the browser stores it and I believe automatically appends it onto any requests where the server has the following header:
WWW-Authenticate: basic
I have configured Django to return the following header:
WWW-Authenticate: basic, token
This successfully causes a XHR request sent via axios to have the basic header appended, when the Authorization header is empty.
The problem is the Authorization header isn't empty, because I need to set a token value in there.
const axiosConfig = {
method: requestType,
url: `${url}`,
withCredentials: true,
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
data: payload
};
// If we're logged in then send our auth token
if (localStorage.auth_token) {
// Axios can't see the basic authentication header here so we can't append.
console.log(axiosConfig.headers.Authorization);
// Basic auth will only get sent if I don't set anything here
axiosConfig.headers.Authorization = `Token ${localStorage.auth_token}`;
}
At this point the browser doesn't seem to append the basic header anymore and so my authentication fails.
Is there a way around this?
I wanted a blanket basic auth because there's no way I can accidentally expose anything on staging, otherwise I have to rely entirely on the token authentication and robots.txt which is less than ideal.
The answer in the end was port forwarding.
I removed basic auth, turned off ports 80 and 443 and then used port forwarding to map my SSH to local host.
i.e. ssh -N -L 8755:127.0.0.1:443 user#ip_address

how to skip Preflight Requset in vue with content-type:application/json

error :"405 not allowed Method" in post method type call in request command vue
i need call api function with content-type:application/json and post Method type with request command in vue ,but browser add preflight request with options method type and it causes this error :"405 not allowed Method"
var options = {
method: "POST",
url: "http://api.sample.com/login",
headers: {
"Access-Control-Request-Method":"POST",
"cache-control": "no-cache",
"content-type": "application/json",
},
body: '{ Username: "demo", Password: "demo", Domain: "test" }'
};
request(options, function(error, response, body) {
if (error) throw new Error(error);
body.data;
alert("ok");
});
The OPTIONS call is done whenever you do a cross-origin request. This means the domain your application is running on is different from the domain where the api is. A pre-flight request is mandatory for these requests, because the browser needs to figure out if you are allowed to do these requests. A 405 error means that the server thinks you are not allowed to make that request.
To solve this problem you can move your api to the same domain as your frontend. Please note that it cannot be on a subdomain.
A different way of solving this, is by sending back the correct headers. In your case you seem to at least miss the Access-Control-Allow-Methods response header. Make sure to send this header and either dynamically figure out which methods are allowed, or do something like the following. That would allow the most common methods to work.
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
In the comments you said that you do not have control over the api, and as such cannot change the response header. In that case your best bet is to contact whoever maintains the api and ask how to best use their api.
In the comments you said that this worked fine when you did the same thing in ASP.NET. ASP.NET is a server-side language, which means that requests in that context do not have a concept of "cross-origin". Cross-origin only comes into play in the browser, where the application runs on an actual domain.
Assuming you can set up a proxy on your application domain, you can also create a proxy that proxies all requests to the api you actually want to communicate with. You would deploy your domain on https://example.com and do your requests to https://example.com/api/endpoint. Your proxy will listen for requests that begin with https://example.com/api and proxy it to https://whatever.the.api.is/ with the appropriate endpoint and data.
Please keep in mind that while some api's might just be configured incorrectly, a lack of cross-origin response headers might just mean that the api is nog meant to be consumed through the browser. Part of this could be that the request contains a secret that should not be exposed to users that use your application, but should instead only be on the server. Using a proxy in that case would set you up for impersonation attacks, because you would expose the secret to your application, but defeat the cross-origin headers by making it appear to the application that the api is on the same domain.

Keycloak API always returns 401

I'm trying to interact with Keycloak via its REST API. I have the master realm and the default admin user, and a test realm. Firstly, I get an access token for the admin account and test realm:
let data = {
grant_type : 'password',
client_id : 'test-realm',
username : 'admin',
password : 'admin'
};
let headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
axios.post(
'https://someurl.com:8080/auth/realms/master/protocol/openid-connect/token',
qs.stringify(data),
headers
)
That works ok. Then I try to make a call to create a user (or do anything else) and I get a 401 unauthorized error:
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${accessToken}`
};
data = {
rep: {
email: "test#email.com",
username: "test#email.com"
},
path: 'test-realm'
};
axios.post('https://someurl.com:8080/auth/admin/realms/test-realm/users',
qs.stringify(data),
headers
)
Is that not the correct way to include the token? Is the access token the one you use for authenticating other API calls? Shouldn't the admin account's token work for authenticating calls to other clients with the master realm? Would it be some setting in the master realm that I have to change in the admin console? Any help appreciated.
I got a 401 error because I generated the offline token by using http://localhost:8080 and then I tried to request the api by using http://keycloak:8080 which is not allowed. Unfortunately the log doesn't tell you that.
To debug JWT tokens I recommend https://jwt.io/
Is that not the correct way to include the token?
This is a correct way.
You just do something incorrectly.
Please, refer for an example from keycloak-request-token Node.js module:
https://github.com/keycloak/keycloak-request-token/blob/master/index.js#L43
You use
client_id : 'test-realm'
but there is
client_id: 'admin-cli'
there.
Also, to create a user, you should use
'Content-Type': 'application/json'
You can refer for Node.js examples of Keycloak REST API here:
https://github.com/v-ladynev/keycloak-nodejs-example/blob/master/lib/adminClient.js
Examples of other useful stuff like:
custom login
storing Keycloak token in the cookies
centralized permission middleware
can be found in the same project: keycloak-nodejs-example
I fixed it by enabling the below "Service Accounts Enabled" button under Settings for admin-cli
I had this issue and solved it by making sure that there is no more than 1 minute between the first and the second API request. So, if you are doing this manually (2 curl requests), the token may expire and you may get error 401. Nevertheless, you should use admin-cli as mentioned above.
I came this issue recently and after struggling for a while i figured. using a realm name containing white spaces will trigger 401 unauthorized error when interacting with via SDKs or API.
IN SUMMARY:
change: realm name
to: realm-name