How to read server set cookie in angular 2 application, and how to send same cookie in request from angular 2 application? - rest

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-Credenti‌​als", "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',fcRenderI‌​nput,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 :)

Related

shiro pac4j cas ajax 401 when accessing another client

I am using cas 5.x.
I have cas-server and two web apps client-1 and client-2.
currently, I can single sign on and single sign out, but there is one problem in following steps:
access client-1, it will ask me for login in cas server, then redirect me back to client-1 after login success.
click one button to access the protected resources of client-2 via ajax in page of client-1, however this ajax call return 401.
if i access protected resources of client-2 from browser address bar directly in step 2, it works.
ajax cannot handle the redirect cause this problem, thus how to solve this problem?
my ajax call is :
//test() is in client-1
function test() {
jQuery.ajax({
url:"http://192.168.0.14:8445/client-2/user/userInfo",
headers: {'X-Requested-With': 'XMLHttpRequest'},
success: function(res) {
//...
}
});
}
Per the pac4j documentation,
When you're using an Indirect Client, if the user tries to access a protected URL, the request will be redirected to the identity provider for login. Though, if the incoming HTTP request is an AJAX one, no redirection will be performed and a 401 error page will be returned.
So what you're seeing is expected behavior.
Next, the HTTP request is considered to be an AJAX one if the value of the X-Requested-With header is XMLHttpRequest or if the is_ajax_request parameter or header is true. This is the default behavior/condition when handling/detecting AJAX requests, and by default, pac4j will only compute the redirection URL and add it as a header (assuming the addRedirectionUrlAsHeader is set to true for the indirect client) when it passes back the 401 http status.
ajax cannot handle the redirect cause this problem
It's not designed to handle the redirects. You need to catch the 401 in your AJAX call, take the redirect url from the header that is passed back to you and do the redirect yourself automatically, or do any other activity/action that is correct behavior for your application (display message, redirect to another URL, etc).

Cannot preserve POST request in PingAccess

I am getting this error when trying to authenticate to my application.
Log extract: level":"ERROR","message":"Cannot preserve POST request with content type 'application/json; charset=UTF-8' (only 'application/x-www-form-urlencoded' is supported)
My question: Is there a way to configure PingFederate, so it can handle POST requests?
That's not a PingFederate error, that's a PingAccess error.
This issue is typically seen on AJAX requests from a Single Page Application, where that Application definition in PingAccess has not been appropriately defined as "Web+API" with the "SPA Support" box checked. It happens when the PingAccess web session has expired, and the application tries to POST an update to the backend.
It must be understood, however, that the application will need to be coded so that it either handles the 401 or follows the 302 redirect that it gets back, once you add SPA Support.

Get location fragment with Fetch API redirect response

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).

Chrome don't send back cookie

I have a web app. To work it usestwo server:
Application server (based on Delphi datasnap) SERV_A
WebServer apache SERV_W
These are the user steps:
STEP1 Login
The user call index page from SERV_W, write user and password and call a procedure by HTTP POST to SERV_A. SERV_A respond by a session_id passed by a Cookie (response header has Set-Cookie: sessionid=123456)
STEP2 Get url list
The user call another SERV_A procedure by HTTP GET to retrieve a list of url
For example an url is: http://host_serv_a:port/datasnap/rest/TServerMethods1/getPDF/003
STEP3 Click on a link
The user sees a list of link and click on one of those.
Automatically the browser do an HTTP GET to retrieve the resource to SERV_A.
Ok, this is my problem:
On STEP3 SERV_A want the sessionId, passed in a cookie but the browser never send the cookie. Why? My browser (Chrome) don't have limitation to manage cookie.
I have found a solution here https://divshot.com/blog/static-apps/cookies-and-cors/ (Web Standards Are Awesome)
To manage cookies correctly server and client have to agree:
Client: set withCredentials option to true in the ajax call
Server: set Access-Control-Allow-Credentials: true header in the response

Logging a user out when using HTTP Basic authentication

I want users to be able to log in via HTTP Basic authentication modes.
The problem is that I also want them to be able to log out again - weirdly browsers just don't seem to support that.
This is considered to be a social-hacking risk - user leaves their machine unlocked and their browser open and someone else can easily visit the site as them. Note that just closing the browser-tab is not enough to reset the token, so it could be an easy thing for users to miss.
So I've come up with a workaround, but it's a total cludge:
1) Redirect them to a Logoff page
2) On that page fire a script to ajax load another page with dummy credentials:
$j.ajax({
url: '<%:Url.Action("LogOff401", new { id = random })%>',
type: 'POST',
username: '<%:random%>',
password: '<%:random%>',
success: function () { alert('logged off'); }
});
3) That should always return 401 the first time (to force the new credentials to be passed) and then only accept the dummy credentials:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOff401(string id)
{
// if we've been passed HTTP authorisation
string httpAuth = this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(httpAuth) &&
httpAuth.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
// build the string we expect - don't allow regular users to pass
byte[] enc = Encoding.UTF8.GetBytes(id + ':' + id);
string expected = "basic " + Convert.ToBase64String(enc);
if (string.Equals(httpAuth, expected, StringComparison.OrdinalIgnoreCase))
{
return Content("You are logged out.");
}
}
// return a request for an HTTP basic auth token, this will cause XmlHttp to pass the new header
this.Response.StatusCode = 401;
this.Response.StatusDescription = "Unauthorized";
this.Response.AppendHeader("WWW-Authenticate", "basic realm=\"My Realm\"");
return Content("Force AJAX component to sent header");
}
4) Now the random string credentials have been accepted and cached by the browser instead. When they visit another page it will try to use them, fail, and then prompt for the right ones.
Note that my code examples are using jQuery and ASP.Net MVC, but the same thing should be possible with any technology stack.
There's another way to do this in IE6 and above:
document.execCommand("ClearAuthenticationCache");
However that clears all authentication - they log out of my site and they're logged out of their e-mail too. So that's out.
Is there any better way to do this?
I've seen other questions on this, but they're 2 years old - is there any better way now in IE9, FX4, Chrome etc?
If there is no better way to do this can this cludge be relied upon? Is there any way to make it more robust?
The short anser is:
There is no reliable procedure for achieving a "logoff" using
HTTP Basic or Digest authentication given current implemenations of basic auth.
Such authentication works by having the client add an Authorization header
to the request.
If for a certain resource the server is not satisfied with the credentials provided (e.g. if there are none), it will responde with a
"401 Unauthorized" status code and request authentication. For that purpose it will provide a WWW-Authenticate header with the response.
A client need not wait for a server requesting authentication.
It may simply provide an Authorization header based on some local
assumptions (e.g. cached information from the last successful attempt).
While your outlined approach on "clearing" out authentication info has a good chance of working with a wide range of clients (namely widespread browsers),
there is absolutely no guarantee that a nother client might be "smarter" and
simply discriminate proper authentication data for your "logout" page and any other pages of the target site.
You will recognize a similar "problem" with using client side certificate based authentication.
As long as there is no explicit support from clients you might fight on lost ground.
So, if "logoff" is a concern, move over to any session based authentication.
If you have access to the implementation of authentication on the server side you might be able implementing a functionality that will disregard authentication information presented with Authorization header (if still identical to what has been presented during current "session) on request of your application level code (or provide some "timout" after which any credentials will be re-requested), so that the client will ask the user for providing "new" credentials (performing a new login).