$response->withRedirect() method in slim not rendering global variable for twig created in middleware - slim

<?php
namespace Auto\middleware;
class registrationMiddleware{
public $container;
public function __construct($container){
$this->container=$container;
// $container is the $app instance passed from the route
}
public function __invoke($request,$response,$next){
$this->container->getContainer()->view->getEnvironment()->addGlobal('error','error');
$response=$next($request,$response);
return $response->withRedirect($this->container->getContainer()->get('router')->pathFor('redirect'));
}
}
It does not output global variable (error) on rendering.

It's important to remember that your browser knows nothing about views, and templates, and global variables. All it sees is an HTTP response, which contains a status code, some headers, and optionally a body.
A normal response has a status code of 200, and a body containing the content the browser should display.
The Slim $response->withRedirect method creates an HTTP response with a status code telling the browser to redirect (301, 302, or 307). Although such a response can contain a body, it will generally just be a fallback for browsers that don't automatically redirect. A standard modern browser will never render this content, because as soon as it sees the status code, it will send a new request to the URL given in the response's Location header, and display the content from that to the user instead.
Once the user has been redirected, there is no context left over from before the redirect. The browser has requested a new URL, and your application will receive that new request and process it, with no way of seeing what happened previously. There will be no global variables, views, or anything else, left over from before the redirect. The only information retained will be things you've explicitly saved to cookies, or to the session (which is referenced by a cookie).
The end result of all this being that you cannot both display something with a view and issue an HTTP redirect in the same piece of code.

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

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

Force POST form submission to send cookies

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.

What is the difference between redirect and navigation/forward and when to use what?

What is difference between a navigation in JSF
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler().handleNavigation(context, null, url);
and a redirect
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.sendRedirect(url);
and how to decide when to use what?
The issue with navigation is that page URL does not change unless faces-redirect=true is added to the query string of the navigation URL. However, in my case appending faces-redirect=true throws error if I want to redirect to a non-JSF page like a plain HTML page.
And another option is as BalusC suggested at JSF 2.0 redirect error
First of all, the term "redirect" is in web development world the action of sending the client an empty HTTP response with just a Location header with therein the new URL on which the client has to send a brand new GET request. So basically:
Client sends a HTTP request to somepage.xhtml.
Server sends a HTTP response back with Location: newpage.xhtml header
Client sends a HTTP request to newpage.xhtml (this get reflected in browser address bar!)
Server sends a HTTP response back with content of newpage.xhtml.
You can track it with the webbrowser's builtin/addon developer toolset. Press F12 in Chrome/IE9/Firebug and check the "Network" section to see it.
The JSF navigationhandler doesn't send a redirect. Instead, it uses the content of the target page as HTTP response.
Client sends a HTTP request to somepage.xhtml.
Server sends a HTTP response back with content of newpage.xhtml.
However as the original HTTP request was to somepage.xhtml, the URL in browser address bar remains unchanged. If you are familiar with the basic Servlet API, then you should understand that this has the same effect as RequestDispatcher#forward().
As to whether pulling the HttpServletResponse from under the JSF hoods and calling sendRedirect() on it is the proper usage; no, that isn't the proper usage. Your server logs will get cluttered with IllegalStateExceptions because this way you aren't telling JSF that you've already taken over the control of the response handling and thus JSF shouldn't do its default response handling job. You should in fact be executing FacesContext#responseComplete() afterwards.
Also, everytime whenever you need to import something from javax.servlet.* package in a JSF artifact like a managed bean, you should absolutely stop writing code and think twice if you're really doing things the right way and ask yourself if there isn't already a "standard JSF way" for whatever you're trying to achieve and/or if the task really belongs in a JSF managed bean (there are namely some cases wherein a simple servlet filter would have been a better place).
The proper way of performing a redirect in JSF is using faces-redirect=true query string in the action outcome:
public String submit() {
// ...
return "/newpage.xhtml?faces-redirect=true";
}
Or using ExternalContext#redirect() when you're not inside an action method such as an ajax or prerender listener method:
public void listener() throws IOException {
// ...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/newpage.xhtml");
}
(yes, you do not need to put a try-catch around it on IOException, just let the exception go through throws, the servletcontainer will handle it)
Or using NavigationHandler#handleNavigation() in specific cases if you're using XML navigation cases and/or a custom navigation handler with some builtin listener:
public void listener() {
// ...
FacesContext fc = FacesContext.getCurrentInstance();
NavigationHandler nh = fc.getApplication().getNavigationHandler();
nh.handleNavigation(fc, null, "/newpage.xhtml?faces-redirect=true");
}
As to why the navigation handler fails for "plain HTML" files, that is simply because the navigation handler can process JSF views only, not other files. You should be using ExternalContext#redirect() then.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
When should I use h:outputLink instead of h:commandLink?

Wicket: redirect to shared resource

i've got a wicket form, which when submitted should give users a file to download. the file is generated by an external servlet (based on the form), which writes generated content to response's output stream.
normally this can be done using getRequestCycle().setRequestTarget(new RedirectRequestTarget(redirectUrl); on form submit, where redirectUrl will be the external servlet's url with parameters.
however, redirectUrl shows up in browser's address bar, which is not good as users see parameter values, which they shouldn't.
in order to overcome that, i've implemented a custom DynamicWebResource, which fetches the servlet output as byte[] and is bound to a virtual url through usual wicket shared resource mechanism.
on form submit i do the following:
RequestParameters rps = new RequestParameters();
rps.setResourceKey(new ResourceReference("myResource").getSharedResourceKey());
getRequestCycle().setRequestTarget(new SharedResourceRequestTarget(rps));
and then rely on wicket's internal ways of handling.
when debugging i can clearly see that my shared resource is correctly invoked, the external servlet's response is correctly generated, fetched and then written to current response with all the headers and stuff, but nothing happens after that, no errors, just nothing.
when i call my shared resource directly specifying the url wicket has bound it to (like http://localhost:8080/webAppRoot/wicket/myResource?param1=value) - everything works, file is well-formed and downloadable.
is there something additional i have to do? it's most probably something stupid, but i've been banging my head agains the wall for a day already...
problem solved. the reason for the response not to be processed by the browser was the fact that the form was submitted using AJAX. changing submit type to plain solved the issue.