symfony 5 web debug toolbar showing anonymous and cannot redirect after onAuthenticationSuccess - reactjs

I have been following along with the following Symfony tutorials, but I believe they are using version 4 and I am using version 5. They reach a point in the tutorial which shows that the web debug toolbar shows the user's email logged and they even pointed out that if you see logged as anonymous, then just refresh. I did refresh, but it still shows as anon.
As you can see by the following screen shot, login was successful and it shows the correct username as well:
I started to watch the first part of the tutorial - listed below - when I reached a point in the second part that pointed out that I should watch the first part, which made sense, that I might have missed something, but that was an even older version of Symfony and things have changed in version 5.
First part of the tutorial
Second part of the tutorial
After going through the tutorials, I still have the web debug tool showing anon. Now, I am using React as a form to POST the email and password - see next screen shot - would that effect how the web debug toolbar, but I do not see how, because the console shows that the system knows the user.
Does anyone know a config that needs to be changed?
I have tried changing the following within src\Security\TokenAuthenticator - getUser from:
return $this->em->getRepository(User::class)
->findOneBy(['apiToken' => $credentials])
;
To:
return $this->em->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
But no change, still shows anon
Also, as the subject states, I cannot redirect via onAuthenticationSuccess
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
I do not see why this does not work. Again, is it because I am posting via a React app?

Turns that it is because I am running an older version of the browser Firefox and the log in is working. You can see by the screen shot of both Firefox and Chrome, that it is working Chrome
As far as the redirect goes, PHPStorm was saying that I did not have urlGenerator available in the TokenAuthenticator class. As a result, I should have noticed before and this is what I did to correct it:
In my src\Security\TokenAuthenticator I have the following:
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
In my constructor:
private $em;
private $urlGenerator;
public function __construct(EntityManagerInterface $em, UrlGeneratorInterface $urlGenerator)
{
$this->em = $em;
$this->urlGenerator = $urlGenerator;
}
My onAuthenticationSuccess:
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
// redirect to some "app_homepage" route - of wherever you want
return new RedirectResponse($this->urlGenerator->generate('app_homepage'));
}
But it is still not working
I have tried
use Symfony\Component\HttpFoundation\RedirectResponse;
private $redirectResponse;
public function __construct(EntityManagerInterface $em, RedirectResponse $redirectResponse)
{
$this->em = $em;
$this->redirectResponse = $redirectResponse;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
// redirect to some "app_homepage" route - of wherever you want
return $this->redirectResponse->redirectToRoute('app_homepage');
}
But PHPStorm tells me that it cannot find method redirectToRoute within class RedirectResponse
The only thing that I have found to work with redirecting users to the home page after successful is login, is within my React login app. I have an async to my handleClick method, after the fetch POST, I have a setTimeout of 3000 that uses a plain javascript:
window.location.href = '/';
I would love to know the answer to why I cannot redirect via the Authenticator class that I have created, but at least someone who is using Firefox will not have to wonder why their web debug tool is not showing that the user has successfully logged in while still showing anon

Related

getInitialProps causing ERR_TOO_MANY_REDIRECTS error on my live site, but not on my local version

So, I've been trying to implement cookies on my website, to keep track of a list of JavaScript objects, so the page stays consistent when the user comes back to it. I've been following this tutorial here.
On my local machine, using npm run dev on localhost:3000, it works absolutely perfect. However, when I push the commit to GitHub, it builds on Vercel without any issue, but when I try and access the live website on the internet, it gives me a 'ERR_TOO_MANY_REDIRECTS' error.
I'm pretty confused as to why it would work perfectly fine on my locally hosted site, but freaks out and does not work when it's put into production.
I think I have narrowed the problem down to getInitialProps because when I comment out the implementation in my index.js file, it still doesn't work, but when I comment out getInitialProps, it works again.
Here is the code I think may be the problem.
Home.getInitialProps = async ({req, res}) => {
const data = parseCookies(req)
if (res) {
if (Object.keys(data).length === 0 && data.constructor === Object) {
res.writeHead(301, { Location: "/" })
res.end()
}
}
return {
data: data && data,
}
}
And here is the code for that parseCookies method, which is imported as
import { parseCookies } from "../helpers/index"
within my index.js
import cookie from "cookie"
export function parseCookies(req) {
return cookie.parse(req ? req.headers.cookie || "" : document.cookie)
}
I'm super confused at this point, I've walked myself through the code a dozen times now and still have no idea what I might be doing wrong. Any help would be much appreciated! And please lemme know if there's anymore info I can provide!
The ERR_TOO_MANY_REDIRECTS error occurs because Object.keys(data).length === 0 && data.constructor === Object returns true when no cookies are set and you access the homepage. When this happens the redirect takes you back to / (the homepage) which then makes the check again and a new redirect occurs, and so on.
Locally, you probably have cookies set, so you don't experience the issue. However, when you access the website hosted on Vercel, no cookies are present initially, which triggers the infinite redirect cycle.
To fix the issue simply remove the logic from the homepage, since that's the redirect destination. You can still have it on other pages and redirect to the homepage, though.

auth0 parseHash can't create property '__enableIdPInitiatedLogin' on hash string

I'm trying to upgrade my React web app from auth0-js 9.6.1 to 9.7.3. After installing the new library, my Slack login flow no longer works, it appears to be breaking in the callback.
TypeError: Cannot create property '__enableIdPInitiatedLogin' on string '#access_token={token string}&token_type=Bearer&state={state string}'
My parseHash call is:
this.auth0.parseHash(hash, (err, authResult) => {
if (authResult && authResult.idToken) {
AuthService.setToken(authResult.idToken); // JWT returned from Auth0;
// Redirect user to content.
const returnUrl = localStorage.getItem(Variables.RETURN_URL_KEY);
localStorage.removeItem(Variables.RETURN_URL_KEY);
returnUrl
? window.location.replace(returnUrl)
: window.location.replace("/");
} else if (err) {
console.log("Error with auth callback", err);
window.location.replace("https://foo.com"); // If auth fails, send user to home page.
}
}
This works fine in 9.6.1, but fails in 9.7.x and I can't find anything about any breaking changes that would cause it to start failing. Any ideas?
I had the same issue as you so I opened a ticket on the Auth0.js library github page.
This is the response I got from the developers:
It was working by accident then (also, the string is being ignored in your case), considering that we expect the first parameter to either be an object or a callback function.
All of our docs mention that:
https://github.com/auth0/auth0.js#api
https://auth0.github.io/auth0.js/global.html#parseHash
https://auth0.com/docs/libraries/auth0js/v9#extract-the-authresult-and-get-user-info
In your case, the simplest fix is to just remove the first parameter and keep only the callback. window.location.hash is already used when there's no options object.
(emphasis on the fix mine)
I tested 9.7.3 with this.auth.auth0.parseHash((err, result) => ... and it worked like a charm.
I hope this'll help!

Moved my login page, cant login now

I moved my login controls from account/login to the home page and removed the account controller and it's views. It all works fine on my machine (of course) but when I deploy to my test environment I get an error:
No webpage was found for the web address: https://identity.blah.blah/account/login?returnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dclient.js%26redirect_uri%3Dhttp%253A%252F%252Fblah.blah%26respons etc
My front end is Angular 6 and Im using angular-auth-oidc-client.
There's nothing in my code that creates this url. The sts server url is just the root uri, no account/login.
{
"stsServer":"https://identity.blah.blah",
"redirect_url":"http://app.blah.blah",
"client_id":"app.client.js",
"response_type":"id_token token",
"scope":"openid profile api.v1",
"post_logout_redirect_uri":"http://app.blah.blah",
"start_checksession":true,
"silent_renew":true,
"startup_route":"/",
"forbidden_route":"/forbidden",
"unauthorized_route":"/unauthorized",
"log_console_warning_active":true,
"log_console_debug_active":false,
"max_id_token_iat_offset_allowed_in_seconds":"10"
}
Anyone got any ideas? Maybe this is something hard coded somewhere I need to override?
Found it finally. Not the best documentation... :/
var builder = services.AddIdentityServer(SetupIdentityServer)
private static void SetupIdentityServer(IdentityServerOptions
identityServerOptions)
{
identityServerOptions.UserInteraction.LoginUrl = "/";
identityServerOptions.UserInteraction.LogoutUrl = "/Home/Logout";
}

Why is session storage being drawn upon between different browser instances?

Background
In an application I'm working on, I've found that I can define values in sessionStorage in Chrome 62 on Windows 10, and that apparently changing that value in one tab affects other tabs that point to the same key.
I was operating under the assumption that localStorage is supposed to persist information across all browser windows, while sessionStorage is only supposed to persist information for a specific window or tab.
More specifically, I have an AngularJS service I'm using as a layer for sessionStorage interactions:
export class PersistenceSvc {
public static $inject: string[] = ['$window'];
public constructor(public $window: ng.IWindowService) {}
public save<T>(name: string, data: T): void {
const saveData: string = JSON.stringify(data);
this.$window.sessionStorage.setItem(name, saveData);
}
public load<T>(name: string): T {
const loadData: string = this.$window.sessionStorage.getItem(name);
const result: T = JSON.parse(loadData) as T;
return result;
}
}
...That I use from a run block in order to implement some data persistence in my application.
export function persistSomeData(
someSvc: Services.SomeService,
userAgentSvc: Services.UserAgentSvc,
persistenceSvc: Services.PersistenceSvc,
$window: ng.IWindowService) {
if(userAgentSvc.isMobileSafari()) {
// Special instructions for iOS devices.
return;
}
const dataToPersist: Models.DataModel = persistenceSvc.load<Models.DataModel>('SomeData');
if(dataToPersist) {
// Set up the state of someSvc with the data loaded.
} else {
// Phone home to the server to get the data needed.
}
$window.onbeforeunload = () => {
persistenceSvc.save<Models.DataModel>('SomeData', someSvc.dataState);
};
}
persistSomeData.$inject = [
// All requisite module names, omitted from example because lazy.
];
angular
.module('app')
.run(persistSomeData);
When only operating using a single tab, this works fine (unless running from an iOS device, but that's tangential to what I'm encountering.) When you do the following though, you start seeing some more interesting behavior...
Steps:
1. Open a Chrome instance. Create a new tab, and drag that out such that it becomes its own window.
2. Navigate to your site, that's using the above code.
3. Do things on your site that cause someSvc's data state to have different data in the first browser.
4. Do things on your site that cause someSvc's data state to have different data in the second browser.
5. Do something on your site that draws upon someSvc's data state in the first browser.
6. Observe that the data utilized on the first browser instance, was sourced by the second browser instance. (This is the problem, right here.)
Question:
In the past I haven't done a lot of cookie/localStorage/sessionStorage programming, so it's very possible that I've terribly misunderstood something. Bearing that in mind, why is it that window.sessionStorage is behaving in a way that the MDN documentation as well as the winning answer to this SO question says it shouldn't be behaving in?
EDIT: It turns out there is a problem, but it's not clientside. Closing this question, as I was operating under the assumption that the client was the problem.
There is something wrong with your code as a quick and easy test on the browser console shows that sessionStorage only impacts the browser tab that is open. A change in the right tab is not reflecting to the left tab:

Redirect to maintenance page in CakePHP doesn't work

I am trying to set a maintenance page so that when the site is disabled, it should appear no matter what page was requested.
I currently tried doing this with $this->cakeError():
in app_controller.php:
function beforeFilter(){
....
if($this->__get_config('maintenance_status') == 1){
$this->cakeError('maintenance', array('message' => $this->__get_config('maintenance_message')));
}
....
}
and in app_error.php:
function maintenance($message){
$this->controller->set('message', $message['message']);
($this->controller->RequestHandler->isAjax()) ? $this->_outputMessage('ajax_maintenance') : $this->_outputMessage('maintenance');
}
The problem is that a Fatal Error occurs, which says: Call to a member function isAjax() on a non-object. But I have obviously set the RequestHandler Component in app_controller.php. Moreover, I have tried calling this error from within another controller and it doesn't give me any Fatal Error.
What could be the problem? Why doesn't it recognize that I have initalized the Component?
From the CakePHP book:
Calling this method will show an error page to the user and halt any further processing in your application
I am assuming that you're calling the error in some callback in AppController.
If that is the case you may very likely be halting execution of your script before your components are instantiated. This would certainly cause your error.
Now, I think this error is a good chance to reevaluate how you're dealing with the problem. Is this really an error? You know the maintenance status is set so it's expected that the user be shown this page. It isn't an error. Furthermore, you certainly wouldn't want 10,000 messages in your log telling you that you turned maintenance on!
I think this could be better solved by utilizing some controller callbacks and a little bit of code.
I don't know what _get_config() is so I assume it is a custom user function that you can call in this callback.
We'll be using the beforeFilter() controller callback.
class AppController extends Controller {
public function beforeFilter() {
if ($this->_get_config('maintenance_status') === 1) {
$this->redirect('/maintenance');
}
}
}
Now, you can just setup a maintenance controller, attached to its own view, that will properly show your maintenance message, and won't log all those connection attempts during maintenance in your error log.
Slightly better would also be to use the Configure::read( "System.maintenance" ) or similar. (I tend to namespace my config data, System being the namespace for stuff like maintenance flags etc.)
Also, as Charles said - don't use an error page for an expected event. Errors are to show the user, and for the application to handle notifications etc, about unexpected failures. The maintenance page could simply be a view file in the /app/views/pages/ folder. Redirect to that if the config key is set to true/1.
Your approach seems to be intelligent, but you might be overdoing it a little.
I have a similar setup in a site I am currently developing and I simply use the auth component to take care of it for me.
To help out, I setup a new offline layout that I force the application to use if status of the site is 0 (offline). If the status is 0 app_controller denies access to the entire site.
$this->Auth->deny('*');
$this->layout = "offline";
Also, in this layout I have a hidden login form that appears if the user clicks the message.If user is able to authenticate (all users for now - development) access is granted to the entire site using the default template.
Check it out, it might help you out...
Click Here
Some of the code, but you can read more about it in the link above
function beforeFilter(){
// Site Offline = 0 , Site Online = 1
if($this->Configuration->get_site_status() == 1){
// Allow access to the site to all users and perform all required
// beforeFilter code
}else{
...
// If site is OFFLINE but User is logged in allow access.
// Later I will need to change it to only allow admin access if logged in as I am still developing
// Everyone else will be denied access even if they are able to authenticate
if(!$this->Auth->user() == null){
$this->layout = 'default';
$this->Auth->allow('*');
}else{
$this->layout = 'offline';
$this->Auth->deny('*');
}
...
}
}

Resources