OAuth2 Implicit Grant with Ajax - javascript

I'm implementing OAuth's implicit grant type in a Javascript application using Angular. I would like to request a token using an AJAX request. When I make the request to my OAuth server, it responds with a 302 and a location header. As per the spec, the location is my configured redirect URL with a hash fragment containing the access token.
My problem is I have no way to read the hash fragments if it is an ajax request, since XmlHttpRequest automatically follows the location header. I can't seem to get any information about the initial response (the one whose status is 302, and has the location header.) If there were some way to intercept that response and read out the location header, I would be set.
I would like requesting a token to happen behind the scenes, without the user knowing it is even happening. (That is, without the browser's URL jumping all around.) I should also mention that I am working within an environment where SSO is available, and the user will most likely have already authenticated via SSO. As long as my OAuth server detects an SSO cookie, it will know the user has been previously authenticated and to just issue a token for this application, without needing to redirect to a login page.
Is there any way to read either the location header of the first response, or to read the URL that is redirected to? It seems that xhr itself goes to great lengths to prevent this, so I'm also wondering what is the reason for that?

oauth Implicit Grant usually opens the service login window so the user can accept the service integrator then provide their login credentials. Using JavaScript you can read the hash by implementing code similar to:
var browserRef = window.open(add your URL here, '_blank','location=yes,clearsessioncache=yes,clearcache=yes');
browserRef.addEventListener ('loadstart', function(event) { add code to read the hash)}

Related

HTTP redirects when working with REST APIs

One of my very obvious knowledge gaps is HTTP redirects and what role they play when working with AJAX requests and APIs. I've read this MDN guide on redirects. I know that redirects return a 3xxstatus code and provide a Location response header. Furthermore, the MDN guide states this:
When browsers receive a redirect, they immediately load the new URL
provided in the Location header.
This makes sense to me when dealing with HTML directly (e. g. submitting a form and receiving a redirect after).
When dealing with API's it all becomes a little fuzzy. I've read this quora post where most participants say that redirects don't play any role in REST. When I tested this, the browser made a request to the URL provided in the Location header but did not navigate to it. Instead, I would just load the HTML response if the location was on the same origin and fail with a CORS error for different origins.
I'm currently trying to solve this problem: I send an AJAX payload to an API and want to redirect the user to a different domain (& origin) upon completion. My question is this: Do I send back the redirect URL as a payload and navigate the user to the new origin with the window.location object (javascript) or can this be handled with a HTTP redirect?
I'd love some help.

Javascript: How can I force a POST request to treat the user as not authenticated

I'm using Javascript and XmlHttpRequest to POST to another URL on the same site. Users must be authenticated to access the page where the Javascript runs, but I need to submit the POST to the second URL as a non-authenticated user (to prevent the server from running code which is always run for authenticated users). Is there any way to submit the POST so that it appears to come from a non-authenticated user (so the server doesn't pull the user's authentication information from their session and treat them as authenticated for the POST)?
For example, is there a way to open a new session just for the POST, or to change the session ID just for the POST?
Note:
I tried to explicitly perform authorization using credentials for a non-existent user, but that didn't make any difference.
If this can be done using ajax instead of XmlHttpRequest, that's an acceptable solution.
Unfortunately this can not be achieved only in JavaScript, so you will have to make some changes on your server. You have two options:
Either you mark your session cookie as HttpOnly, so it won't be sent together with your request. But this will mean, that all your requests are sent as unauthenticated user.
Second option is use of a subdomain for this endpoint. Cookies are sent with XmlHttpRequests only on the same domain to prevent cross-site scripting. So if you move the server endpoint from www.example.com/myresource to api.example.com/myresource, the cookie will not be sent.

How to protect Instagram access key in Javascript? [duplicate]

I've been reading up on REST and there are a lot of questions on SO about it, as well as on a lot of other sites and blogs. Though I've never seen this specific question asked...for some reason, I can't wrap my mind around this concept...
If I'm building a RESTful API, and I want to secure it, one of the methods I've seen is to use a security token. When I've used other APIs, there's been a token and a shared secret...makes sense. What I don't understand is, requests to a rest service operation are being made through javascript (XHR/Ajax), what is to prevent someone from sniffing that out with something simple like FireBug (or "view source" in the browser) and copying the API key, and then impersonating that person using the key and secret?
We're exposing an API that partners can only use on domains that they have registered with us. Its content is partly public (but preferably only to be shown on the domains we know), but is mostly private to our users. So:
To determine what is shown, our user must be logged in with us, but this is handled separately.
To determine where the data is shown, a public API key is used to limit access to domains we know, and above all to ensure the private user data is not vulnerable to CSRF.
This API key is indeed visible to anyone, we do not authenticate our partner in any other way, and we don't need REFERER. Still, it is secure:
When our get-csrf-token.js?apiKey=abc123 is requested:
Look up the key abc123 in the database and get a list of valid domains for that key.
Look for the CSRF validation cookie. If it does not exist, generate a secure random value and put it in a HTTP-only session cookie. If the cookie did exist, get the existing random value.
Create a CSRF token from the API key and the random value from the cookie, and sign it. (Rather than keeping a list of tokens on the server, we're signing the values. Both values will be readable in the signed token, that's fine.)
Set the response to not be cached, add the cookie, and return a script like:
var apiConfig = apiConfig || {};
if(document.domain === 'example.com'
|| document.domain === 'www.example.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Notes:
The above does not prevent a server side script from faking a request, but only ensures that the domain matches if requested by a browser.
The same origin policy for JavaScript ensures that a browser cannot use XHR (Ajax) to load and then inspect the JavaScript source. Instead, a regular browser can only load it using <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123"> (or a dynamic equivalent), and will then run the code. Of course, your server should not support Cross-Origin Resource Sharing nor JSONP for the generated JavaScript.
A browser script can change the value of document.domain before loading the above script. But the same origin policy only allows for shortening the domain by removing prefixes, like rewriting subdomain.example.com to just example.com, or myblog.wordpress.com to wordpress.com, or in some browsers even bbc.co.uk to co.uk.
If the JavaScript file is fetched using some server side script then the server will also get the cookie. However, a third party server cannot make a user’s browser associate that cookie to our domain. Hence, a CSRF token and validation cookie that have been fetched using a server side script, can only be used by subsequent server side calls, not in a browser. However, such server side calls will never include the user cookie, and hence can only fetch public data. This is the same data a server side script could scrape from the partner's website directly.
When a user logs in, set some user cookie in whatever way you like. (The user might already have logged in before the JavaScript was requested.)
All subsequent API requests to the server (including GET and JSONP requests) must include the CSRF token, the CSRF validation cookie, and (if logged on) the user cookie. The server can now determine if the request is to be trusted:
The presence of a valid CSRF token ensures the JavaScript was loaded from the expected domain, if loaded by a browser.
The presence of the CSRF token without the validation cookie indicates forgery.
The presence of both the CSRF token and the CSRF validation cookie does not ensure anything: this could either be a forged server side request, or a valid request from a browser. (It could not be a request from a browser made from an unsupported domain.)
The presence of the user cookie ensures the user is logged on, but does not ensure the user is a member of the given partner, nor that the user is viewing the correct website.
The presence of the user cookie without the CSRF validation cookie indicates forgery.
The presence of the user cookie ensures the current request is made through a browser. (Assuming a user would not enter their credentials on an unknown website, and assuming we don’t care about users using their own credentials to make some server side request.) If we also have the CSRF validation cookie, then that CSRF validation cookie was also received using a browser. Next, if we also have a CSRF token with a valid signature, and the random number in the CSRF validation cookie matches the one in that CSRF token, then the JavaScript for that token was also received during that very same earlier request during which the CSRF cookie was set, hence also using a browser. This then also implies the above JavaScript code was executed before the token was set, and that at that time the domain was valid for the given API key.
So: the server can now safely use the API key from the signed token.
If at any point the server does not trust the request, then a 403 Forbidden is returned. The widget can respond to that by showing a warning to the user.
It's not required to sign the CSRF validation cookie, as we're comparing it to the signed CSRF token. Not signing the cookie makes each HTTP request shorter, and the server validation a bit faster.
The generated CSRF token is valid indefinitely, but only in combination with the validation cookie, so effectively until the browser is closed.
We could limit the lifetime of the token's signature. We could delete the CSRF validation cookie when the user logs out, to meet the OWASP recommendation. And to not share the per-user random number between multiple partners, one could add the API key to the cookie name. But even then one cannot easily refresh the CSRF validation cookie when a new token is requested, as users might be browsing the same site in multiple windows, sharing a single cookie (which, when refreshing, would be updated in all windows, after which the JavaScript token in the other windows would no longer match that single cookie).
For those who use OAuth, see also OAuth and Client-Side Widgets, from which I got the JavaScript idea. For server side use of the API, in which we cannot rely on the JavaScript code to limit the domain, we're using secret keys instead of the public API keys.
api secret is not passed explicitly, secret is used to generate a sign of current request, at the server side, the server generate the sign following the same process, if the two sign matches, then the request is authenticated successfully -- so only the sign is passed through the request, not the secret.
This question has an accepted answer but just to clarify, shared secret authentication works like this:
Client has public key, this can be shared with anyone, doesn't
matter, so you can embed it in javascript. This is used to identify the user on the server.
Server has secret key and this secret MUST be protected. Therefore,
shared key authentication requires that you can protect your secret
key. So a public javascript client that connects directly to another
service is not possible because you need a server middleman to
protect the secret.
Server signs request using some algorithm that includes the secret
key (the secret key is sort of like a salt) and preferably a timestamp then sends the request to the service. The timestamp is to prevent "replay" attacks. A signature of a request is only valid for around n seconds. You can check that on the server by getting the timestamp header that should contain the value of the timestamp that was included in the signature. If that timestamp is expired, the request fails.
The service gets the request which contains not only the signature
but also all the fields that were signed in plain text.
The service then signs the request in the same way using the shared
secret key and compares the signatures.
I will try to answer the the question in it's original context. So question is "Is the secret (API) key safe to be placed with in JavaScript.
In my opinion it is very unsafe as it defeats the purpose of authentication between the systems. Since the key will be exposed to the user, user may retrieve information he/she is not authorized to. Because in a typical rest communication authentication is only based on the API Key.
A solution in my opinion is that the JavaScript call essentially pass the request to an internal server component who is responsible from making a rest call. The internal server component let's say a Servlet will read the API key from a secured source such as permission based file system, insert into the HTTP header and make the external rest call.
I hope this helps.
I supose you mean session key not API key. That problem is inherited from the http protocol and known as Session hijacking. The normal "workaround" is, as on any web site, to change to https.
To run the REST service secure you must enable https, and probably client authentification. But after all, this is beyond the REST idea. REST never talks about security.
What you want to do on the server side is generate an expiring session id that is sent back to the client on login or signup.
The client can then use that session id as a shared secret to sign subsequent requests.
The session id is only passed once and this MUST be over SSL.
See example here
Use a nonce and timestamp when signing the request to prevent session hijacking.

Sending JWT alongside html document in http response

How to send the JWT to a client just after client has authenticated without using Cookies when an html document body is needed to be sent too?
There are docs, blog posts, and tutorials, explaining the cookie-less jwt authentication and leveraging the use of Web Storage API to save the jwt client side. But all of them are trivial examples without sending an html document in http response body upon an authentication which is necessary in some real world applications I can imagine. A cookie can be sent in cookie http response header alongside with an html document in same response's body, I could not still come across a post explaining to do this with a jwt in response instead of a cookie. As I know there is not an API to reach the response headers from javascript in browser if one want to send the jwt in response headers alongside html document in response body.
I have handled your scenario in my project and it can be done in two ways depending on your technology stack you are using and environment constraints, and using OAuth is not mandatory.
Method 1
Send the JWT embedded in the HTML page as a tag. It wont be rendered on the page but can be parsed by you. However, it will be visible in the source window of the browser but it doesnt matter as that would be a protected page and once the next page is rendered, it will not be available.
Method 2
You can send the JWT in a cookie for the first time with a http-only constraint. Handling it over https would bring in extra leverage. Also, like you mentioned, you can delete the cookie.
In case you are using AngularJS on your client side, you have the provision of securing cookies by restricting XHR from the same domain which would avoid the extra task of deleting the cookie.
In fact, #user981375 was mentioning about redirection which can be handled too by Method 1 above. In my case, server provided the redirection URL after successful login however, ajax wouldnt be able to see a 302 header instead would see a 200. So we intercepted that part on server and embedded the token into the 200 response page, i.e. redirected page which is parsed by the client.
I'm in the same boat, I could not figure out how to send JWT token to the client upon successful (or not) social login(s) where redirect was required. Things are simple when when you present user with a login/password and authenticate against your own server via AJAX, but no so simple when you 1) load your login page, 2) do a redirect to OAuth provider, 3) callback to your own server, 4) issue your own JWT token and ... then what?
There is a library out there that provides OAuth support from the client side. You authenticate against Facebook/Google (whatever) get their token back and then you make AJAX request to your own server for token validation. When token is validated by Facebook/Google (whatever) you can then issue your own JWT token with claims and send it as a response (AJAX) to your webpage.
Here is the library and nice article describing how to use it.
HTML documents are usually retrieved from a web application. Web applications are protected by a form of implicit authentication.
Web APIs are usually protected by explicit authentication and the JWT tokens are sent in an HTTP header (Authorization). This is not done automatically by the browser. You have to do this explicitly through JavaScript.
You could of course store the JWT token in a cookie and have it automatically sent to the server on each request.
See also my answer here.

How to redirect to different domain with a cookie in Express js

I'm developing a web app using Express on Node. I'm trying to implement a proxy login functionality where an user is directly logged in and redirected to another site after he logs into to my site.
In my routing function I'm writing the following code
res.cookie('fanws', 'value' );
res.redirect('http://hostname/path'); // another site
I used the debugger in chrome and saw that the cookie is not getting added in the redirected page.
I'm running the app on localhost and the site which i'm redirecting to is hosted on another server on local network.
What should I do to add the cookie on the redirected path?
In a nutshell, you can't set a cookie in a browser or read a cookie for a site that you do not control the server for or have your own client code in that page. The cookie system is designed that way on purpose for security reasons. So, from a page or server for http://www.domain1.com, you cannot read or set cookies for some other domain.
If you have code in the pages of both domains, then you can pass some info to the second page (most likely as a query parameter) that tells the code in the redirected page to take some action (like set a cookie), but you must control the Javascript or server in that second page in order to be able to do that.
The cookie in your nodejs code goes on the current request/response which means it is associated with that domain in the browser when the response from the current request is processed by the browser.
res.redirect(...) returns a 302 response with a new URL as the response to the current request. The browser then sees this response code and makes a new web request to the new page. You cannot set cookies from the server for that new domain unless you have the server for that domain also. This is a fundamental aspect of cookie security. Cookies can only be accessed via Javascript in the browser from the page in the same origin as the cookie belongs and servers can only set cookies for the particular origin in the particular request that they are processing.
#jfriend00 nice explanation.
#Kiran G you can pass in query param in the same redirect, no need to set cookies in express just sent in query param as below.
i.e.
res.redirect(`http://hostname/path?fanws=${value}`);

Categories

Resources