I'm working on a feature for a Chrome extension which requires making a same-origin POST request to an endpoint. For brevity, I'll leave out the particular details of the website. This request creates a resource of a given kind. I've succeeded in being able to create many kinds of these resources, but there's one type in particular that always fails.
When you use the website's own UI to create this kind of resource, I noticed that the resulting POST request is sent with the cookie header, along with some other stuff that looks unfamiliar to me. Here's an excerpt of the request headers:
:authority:www.example.com
:method:POST
:path:/path/to/endpoint
:scheme:https
[...]
cookie: [...]
The cookies are not sent for any other resource type, just this one.
Now, since this passes along cookies, the website's own javascript can't be using ajax. In fact, the site is posting to an <iframe> by linking a <form> to an <iframe> of a particular name.
So, I modified my Chrome extension code to use forms to post to iframes instead of making an ajax request, just like it's done natively on the website. However, the resulting POST requests still do not pass cookies. I have found nothing unique about the parts of the website's UI which create these special resources which might cause the requests to pass cookies.
How does Chrome decide when to pass cookies in a web request? How can I force it to do this for a <form> submission?
EDIT: Here are some more details, as requested.
To create a resource, just POST multipart data to /resource-endpoint. In jQuery you might do something like
var data = new FormData();
data.append('property', 'value'); // Add payload values
$.ajax({
url: '/resource-endpoint'
type: 'POST',
cache: false,
contentType: false,
processData: false,
data: data
});
Doing it this way will create most resources, except for the "special" resource. Since AJAX requests cannot pass along cookies, and the request to create the "special" resource must include cookies, I have to mimic the website's UI more closely.
var id = 'some-id';
var iframe = $('<iframe name="' + id + '"></iframe>');
$(document.body).append(iframe);
var form = $('<form></form>');
form.attr({
target: id,
action: '/resource-endpoint,
method: 'POST',
enctype: 'multipart/form-data'
});
// Add payload values
form.append('<input name="property" value="value" />');
$(document.body).append(form);
form.submit();
This still sends along requests, but there appears to be something missing, because requests to create the "special" resource do not include cookies. I'm not sure how the native website javascript is doing this, as I can't find any difference between the forms that create regular resources and the form that creates "special" resources.
EDIT: Nevermind, I saw a native "special resource" POST request from the UI which doesn't pass along these cookies, so the secret must not be the cookies.
Related
Is it possbile to parse/add an authentication on the submit from a standard HTML for? E.g. I'm using oAuth to authentication logged-in users, and have a usecase where I need to use a standard HTML form with the action and method attributes. But I can't seem to find a way to parse the JWT Token I'm using for authentication. Is this possbile?
Thanks in advance.
No, you can't do it with a regular HTML form. If you need to add an Authorization header to the request, the only thing you can do is to make a request from the Javascript (as is shown in Subhashis's answer). It doesn't have to be jQuery though, you can use plain JS and fetch or some libraries for making http calls (e.g. axios). Whichever you use, remember that it will be an AJAX call, so your JS will have to handle the response properly (the response will not be handled by the browser automatically).
You may use JQuery form submit to send authorization header
var form = $('#form-id').get(0);
var formData = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.open("POST", "url");
xhr.setRequestHeader("Authorization", "jwt token");
xhr.send(formData);
I am getting a preflight error 405: Method not allowed from the HERE API when I request autocomplete as per the documentation.
UPDATE 2:
I have since determined that Axios was adding my default.common authentication headers from my app's API client onto the HERE API client. Axios is supposed to keep those defaults separate per-client, but it seems that it doesn't ... at least not the version I have. I replaced the defaults with a per-client request interceptor and it worked fine. The request no longer triggers an OPTION pre-flight. No issue with HERE's API other than that it doesn't support OPTION method.
UPDATE:
The reason it fails is because HERE does not support the OPTIONS method, only the GET. So now the question is: Why does axios trigger an OPTIONS request when I don't set any headers? An XMLHttpRequest() based GET request does not trigger OPTIONS for the same URL. Something is happening with axios but I don't know what and I can't seem to investigate the headers that axios is sending.
ORIGINAL:
I've tried to find information about this error, as well as HTTP vs HTTPS. I haven't seen others having this problem so I feel like I must be making a simple error. The URL is generated correctly because it works when pasted directly into the browser for example.
const hereClient = axios.create({
baseURL: 'https://autocomplete.geocoder.api.here.com/6.2/'
})
async function searchHere (query) {
let searchTerms = query.split(' ').join('+')
let result = await hereClient.get('suggest.json', {
params: {
app_id: '<APPID>',
app_code: '<APPCODE>',
query: searchTerms
}
})
return processHereSearchResults(result.data)
}
The GET request fails on the OPTION preflight with a 405: Method not allowed. But if I paste the generated URL into a browser then it returns the expected results. For example:
https://autocomplete.geocoder.api.here.com/6.2/suggest.json?app_id=APPID&app_code=APPCODE&query=8131
returns:
{"suggestions":[{"label":"Česko, Brandýs nad Orlicí, 3123","language":"cs","countryCode":"CZE","locationId":"N . . .
Same result whether http or https.
I have since determined that Axios was adding my default.common authentication headers from my app's API client onto the HERE API client. Axios is supposed to keep those defaults separate per-client, but it seems that it doesn't ... at least not the version I have. I replaced the default header setting with a per-client request interceptor to set my authentication and it worked fine. The request no longer triggers an OPTION pre-flight. No issue with HERE's API other than that it doesn't support OPTION method.
i know that Adobe CQ 5 was built on top of Apache Sling (which using JackRabbit). I'm a bit baffled as for why the website will accept arbitrary POST request from outside (unauthenticated user) into Publish Instance through the Dispatcher and then reply with HTTP 200 Content Updated. Should not content update only allowed from Author Instance in this case? Should not such request met with HTTP 403 response instead? - Why is it that even not logged in can get HTTP Response 200?
The response looked like this:
{
"changes": [],
"referer": "http://www.example.com/content/somesite/en.html",
"path": "/content/somesite/en",
"location": "/content/somesite/en",
"parentLocation": "/content/somesite",
"status.code": 200,
"status.message": "OK",
"title": "Content modified /content/somesite/en"
}
I've set POST Referrer Filter for now to prevent arbitrary POST request 'outside' the website got accepted, however i can still get this response by typing jquery ajax request in the browser console while opening the website.
I do wonder if this is bad or something, really new to Adobe CQ.
The JQuery Script for testing it is actually only these:
$.ajax({
url: 'http://www.example.com/content/fasfas',
type: 'post',
data: {},
headers: {
Accept: 'application/json',
},
dataType: 'json',
success: function (data) {
console.info(data);
}
});
Thanks in advance!
This is an issue of not taking the necessary steps to secure the AEM servers. There is a security checklist provided by Adobe to ensure that AEM installation is secure when deployed. Similar security checklist for the dispatcher is also present.
As for your case, there are few issues which are evident
The filter configuration within the dispatcher doesn't deny POST
requests, thereby allowing them to pass through the dispatcher and reach the AEM instance.
The anonymous user in the AEM publisher seems to have more than just
READ privileges on the repository thereby allowing him to make changes to the repo using POST requests.
The referrer filter configuration was allowing requests from external systems as well (which you have blocked now).
Your dispatcher should block all POST operations on the publisher. This is recommended in Adobe's official documentation for configuring dispatcher.
Publisher should also disable write permission for anonymous users and everyone group to paths that are not allowed to be modified by community. Unless you are using CUG, write should be disabled for anonymous across the publisher instance.
Requirement : Our application needs to support same user opening our web application as separated session.
The problem is not how to use cookies in angular 2, but how can sever get cookie from HTTPServletRequest object when angular 2 application makes a rest call to server.
Implementation: Server side restful application has one filter to set user's browser session in cookie and then in HttpServletResponse. Angular client is making one call upon application bootstrap, which is going through server filter to set user's browser session in cookie.
Problem statement: angular client is making first rest call which goes through server filter to set the browser session cookie. When i open chrome developer tool, i do see that rest api response has "set-cookie" which has cookie set by server, but when i open the application tag in developer tool, i do not see any cookie set.
After that if I make any other rest call through angular application, it does not send the cookie in either request or request headers. Now, our application rest api depends on this cookie value to be present in HttpServletRequest and now it is failing.
Can someone please guide me here? I must have done something wrong on angular 2 application side, which i am not able to catch.
I have tried passing "withCredentials =true", but no change.
Another thing I noticed, if i make "GET" request, then i do see cookie in request header, but for "POST" request, I do not see anything for cookie.
Please advice.
server side code to set cookie
String uniqueId = RandomStringUtils.randomAlphanumeric(32);
Cookie userSessionCookie = new Cookie("userSessionId", uniqueId);
if (getDefaultDomain() != null) {
userSessionCookie.setDomain(getDefaultDomain());
}
httpServletResponse.addCookie(userSessionCookie); httpServletResponse.addHeader("Access-Control-Allow-Credentials", "true"); httpServletResponse.addHeader("access-control-allow-methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
httpServletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type, token,withCredentials");
angular 2 post request which expects server to get cookie from HttpServletRequest
renderFF() {
//prepare renderFInput object
var fcRenderInput = {};
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers, withCredentials: true
});
this._http.post('/api/v1/render/feature/fc',fcRenderInput,options)
.subscribe((res) => {
console.log(res.json());
});
}
Just a suggestion if this is about only one browser and multiple tabs, in this case you can use the local storage while setting some flag in it. Also when you try to open the same application in the new tab. you check if the flag is there and user is trying to open the same web application in some other tab of the same browser. You also need to delete the local storage you had set after some point.
I hope if you can get some trick to solve this issue :)
I am trying to get the redirect response location fragment of a fetch API request. But I can't figure how to access it, if possible.
The context is that I am doing an OpenID Connect request in implicit flow, for a WebRTC Identity Proxy assertion generation.
OIDC specs define the answer of the request as:
When using the Implicit Flow, all response parameters are added to the
fragment component of the Redirection URI
HTTP/1.1 302 Found
Location: https://client.example.org/cb#
access_token=SlAV32hkKG
...
So I'm making the request with fetch set in manual mode. But the response is then an opaque-redirect filtered response, which hides the location header. (https://fetch.spec.whatwg.org/#concept-filtered-response-opaque-redirect)
Other mode for fetch are error and follow which would not help. While XHR automatically follows the redirect so would not help either. I may be missing something from the fetch API, but it seems to be something hidden on purpose.
Could someone gives me a way to access this information (or a confirmation it's impossible) ?
Is there any alternative to fetch and XHR to make this request, which would allow to access the redirect location header?
Since XHR automatically / opaquely follows redirects (in the event you're using the whatwg-fetch polyfill for example), one possible solution is to check the response.url of the fetch resolution, to see if it matches a redirect location that you expect.
This only helps if the possible redirect locations are limited or match some pattern --- for instance, if you could expect at any time to be redirect to /login:
function fetchMiddleware(response) {
const a = document.createElement('a');
a.href = response.url;
if (a.pathname === '/login') {
// ...
} else {
return response;
}
}
fetch(`/api`)
.then(fetchMiddleware)
.then(function (response) {
// ...
});
fetch isn't able to polyfill the entire standard. Some notable differences include:
Inability to set the redirect mode.
See David Graham comment on the Disable follow redirect:
This is a nice addition to the Fetch API, but we won't be able to polyfill it with XMLHttpRequest. The browser navigates all redirects before returning a result, so there is no opportunity to interrupt the redirect flow.
My Solution:
1). First solution: we are sending 200 status and redirect url(in the http header) from the server and client is redirecting based on that.
2). Second solution: Server could also redirect to with 301 and redirect url. I think, This is the best solution(i.e if we consider SEO).