Symfony2 - AuthenticationSuccessHandler redirect to original request - authentication

I've overridden AuthenticationSuccessHandler but I'd like to keep the referer redirection behavior on success.
I saw that the default handler uses $request->headers->get('Referer'), I tried to do the same on my custom handler:
if (($targetUrl = $request->headers->get('Referer'))
&& $targetUrl !== $this->options['login_path']) {
die($targetUrl);
Returns me the login path:
http://my_host/login
instead of the original request. I think that it's because the firewall has previously redirected the user to the login page.
The default handler redirects me fine, so I'm wondering why mine does not.
How can I get the original request from my custom AuthenticationSuccessHandler

You should look here. determineTargetUrl is what you're asking for.
Added: It saves return path in session and then restores it (line 98). If there is no return path in user's session, handler takes Referer.

Related

Cloudflare Worker redirect stripping auth headers

I set up a Cloudflare worker to redirect to our API gateway since we don't have control of the DNS and can't just set up a CNAME. The redirect works and it passes along the body and all the headers except Authorization. It receives it, and when I look at the worker console it lists it as redacted. It also redacts the user_key param I'm passing but it passes that through.
const base = 'https://myurl.com'
const statusCode = 308;
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url);
const { pathname, search } = url;
const destinationURL = base + pathname + search;
return Response.redirect(destinationURL, statusCode);
}
First, note that the redactions you are seeing are purely for display in the workers console. This is a feature to protect sensitive secrets from being logged, but it doesn't affect the content of any live request.
Now, with regard to what your Worker is actually doing:
This worker returns a 308 redirect response back to the client. It is then up to the client to follow the redirect, sending the same request to the new URL.
It is the client, then, that decides whether to send the Authorization header to the new location -- the behavior is NOT controlled by Cloudflare Workers. As it turns out, many clients intentionally drop the Authorization header when following redirects to a different domain name. For example, the Go HTTP client library does this, and node-fetch recently started doing this as well. (I happen to disagree with this change, for reasons I explained in a comment.)
If the client is a web browser, then the behavior is complicated. If the Authorization header was added to the request as part of HTTP basic auth (i.e. the user was prompted by the browser for a username and password), then the header will be removed when following the redirect. However, if the Authorization header was provided by client-side JavaScript code when it called fetch(), then the header will be kept through the redirect.
Probably the best way to solve this is: Don't use a 3xx redirect. Instead, have the Worker directly forward the request to the new URL. That is, instead of this:
return Response.redirect(destinationURL, statusCode);
Try this:
return fetch(destinationURL, request);
With this code, the client will not receive a redirect. Instead, the Worker will directly forward the request to the new URL, and then forward the response back to the client. The Worker acts as a middleman proxy in this case. From the client's point of view, no forwarding took place, the original URL simply handled the request.

ASP.NET Identity redirect to full URL after login

I have a Blazor website (IdentityApp) that includes scaffolded ASP.NET identity integration. That website is processing logins and setting a shared cookie that my other localhost Blazor application (LogicApp) can see.
When I access LogicApp (https://localhost:9876), I check the context for a recognised user (using the shared cookie) and then manually redirect anonymous users to the scaffolded IdentityApp login page: https://localhost:1234/identity/account/login.
I also pass through the fully encoded URL of LogicApp home page in the returnUrl, for example; https://localhost:1234/identity/account/login?returnUrl=https%3A%2F%2Flocalhost%3A9876%2F
The login process is working OK (the shared cookie is set correctly), but then I get an exception;
InvalidOperationException: The supplied URL is not local. A URL with an absolute path is considered local if it does not have a host/authority part. URLs using virtual paths ('~/') are also local.
Within ASP.NET Identity (.NET Core 3.1);
How can I configure IdentityApp to allow full urls within returnUrl?
or,
How can I override the scaffolded Login logic to take control of the redirect and use my returnUrl value?
So I found an answer to my own problem (but still interested to hear of alternative solutions)..
Within the scaffolded pages locate Login.cshtml.cs and replace the reference to LocalRedirect(...) in in the OnPostAsync(string returnUrl = null) method, for example;
...
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
//return LocalRedirect(returnUrl); // remove this.
return Redirect(returnUrl); //TODO: define a list of acceptable return Urls (localhost / yourdomain.com etc)
}
...
You may also need to do this in Login2FA.cshtml.cs as well as other places that deal with this ReturnUrl value.
NOTE: LocalRedirect is used to prevent malicious redirects occurring so it would be wise to also add some logic to check the returnUrls that have been passed in.

httpd/mod_auth_form How to display an error message on invalid credentials?

Using inline form with ErrorDocument 401, how can I get an error message to be displayed when the user fails to login?
Expected features are to still work (e.g. login redirect). No login/logout URLs seen in user's browser. When the user logs out, the message should not be shown when they are 401'd back to the inline login page.
I am currently using an onsubmit function to set a flag in sessionStorage. If the page loads (body onload function) and sees this flag, it will show the error message (e.g. user entered wrong credentials and are back here again). This works fine, until they logout. They are 401'd to the login page and the flag is still there, so they see the error message.
I need to clear the message somehow.
Well, GET vs POST! The initial page load is a GET and the logout is a GET; so, only failed logins would be loading this page with a POST.
Let's activate SSI and use an env var (this is on both login and logout Location directives):
SetEnvIf Request_Method "^GET$" LOGIN-ERROR-CLEAR="1"
Inject some JS to our init to clear the sessionStorage:
<!--#if expr="reqenv('LOGIN-ERROR-CLEAR') == '1'" -->
sessionStorage.removeItem('login-attempted');
<!--#endif -->
Sounds logical...but, it doesn't work. The JS is not injected.
GET /logout -> 307 to "/" (due to AuthFormLogoutLocation "/")
GET / -> 401 with content of /login.html
error message is wrongly shown
All GETs, yet the JS was not injected. What is going on? I am back where I started before using SetEnvIf - there is no difference. It's not doing anything!
Is there a better way to trigger an error message and clear the trigger after successful login or logout? This shouldn't be this hard!
Found out I could use the variables directly in the SSI; so, I removed the SetEnvIfs from my vhost. Also, REQUEST_METHOD is the wrong one to use. REDIRECT_REQUEST_METHOD is the one to look for. In the end, this seems to be working:
<!--#comment This block uses SSI/mod_include. -->
<!--#if expr='v("REDIRECT_REQUEST_METHOD") == "GET"' -->
sessionStorage.removeItem('login-attempted');
<!--#endif -->

loopback protected routes/ensure login

How do I ensure that a user is logged in before I render a view using loopback?
I can loggin in the front end using my angular app. But I wanted to block anonymous users from viewing the page.
I thought it would be a header, something like headers.authorization_token, but it does not seem to be there.
I am looking for something like connect-ensurelogin for passport, without having to use passport.
This is the $interceptor that solves your problem.
This code detects 401 responses (user not logged in or the access token is expired) from Loopback REST server and redirect the user to the login page:
// Inside app config block
$httpProvider.interceptors.push(function($q, $location) {
return {
responseError: function(rejection) {
if (rejection.status == 401) {
$location.nextAfterLogin = $location.path();
$location.path('/login');
}
return $q.reject(rejection);
}
};
});
And this code will redirect to the requested page once the user is logged in
// In the Login controller
User.login($scope.credentials, function() {
var next = $location.nextAfterLogin || '/';
$location.nextAfterLogin = null;
$location.path(next);
});
Here is one possible approach that has worked for me (details may vary):
Design each of the Pages in your Single Page Angular App to make at one of your REST API calls when the Angular Route is resolved.
Secure all of your REST API Routes using the AccessToken/User/Role/ACL scheme that LoopBack provides.
When no valid Access Token is detected on the REST Server side, pass back a 401 Unauthorized Error.
On the Client Side Data Access, when you detect a 401 on your REST Call, redirect to your Logic Route.
For the smoothest User Experience, whenever you redirect to Login, store the Route the User wanted to access globally
(localStore, $RootScope, etc.) and redirect back there when the User
Logs in and gets a valid Access Token.
Here is the LoopBack Access Control sample: https://github.com/strongloop/loopback-example-access-control

Symfony2 - FOS UserBundle - Original request redirection

I'm using FOS UserBundle and I have defined a custom AuthenticationSuccessHandler to show a different home page depending on the roles, but I think it should be called only if the user originally requested the login page, shouldn't it ?
On login success I'd like to be redirected to the original request.
As described in the docs, it seems to be the default behavior, but in my case, it still uses my authentication handler.
Can someone help me to redirect the user to his original request ?
For the record, here is how I registered my authentication success handler service:
services:
security.success_handler:
class: Glide\SecurityBundle\[...]\AuthenticationSuccessHandler
public: false
arguments: ['#router', '#security.context']
Yes, the default behavior is to redirect the user to the page they originally requested. However, since you are overriding the default authentication handler, you need to handle redirecting them to that page yourself.
I recommend you look at symfonys authentication handler and mimic its process for figuring out the users original request.