Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid state passed in parameters callback url #347

Open
nitrique opened this issue Dec 17, 2021 · 19 comments
Open

Invalid state passed in parameters callback url #347

nitrique opened this issue Dec 17, 2021 · 19 comments

Comments

@nitrique
Copy link

Hi,

Using this module with Keycloak client, I randomly get the error "Invalid state passed in parameters callback url", in both Firefox and Brave (chromium).

Using Safari with localhost (without https), this error is shown every time and I cannot access to my interface.
I've put some breakpoint in this bundle, and found out that the cookie is cleared between navigations.

It seems that Safari try to protect navigation and tracking when using redirect from another site, which is indeed problematic when using oauth2. Note that the cookie is set in "lax" mode. But in reality it clear the "phpsessid" cookie :( .

Do some of you guys already had this matter and found out a solution ?

Thanks,
Best regards.

Nicolas

@tacman
Copy link
Contributor

tacman commented Dec 25, 2021

I'm running into this same issue, using Symfony 5.4 and the new authentication manager.

I'm guessing it has something to do with the recent changes to session management, but not sure. Open to suggestions.

@tacman
Copy link
Contributor

tacman commented Dec 25, 2021

In the end, the issue was an invalid secret. The 404 threw me off.

@kevincerro
Copy link

kevincerro commented Jan 4, 2022

I'm facing the same issue with Google login.

Symfony 5.4 with new authenticator manager

Session key "knpu.oauth2_client_state" is not found, so "expectedState" is null.
This condition is resolved to "true" and exception is triggered.

if (!$actualState || ($actualState !== $expectedState)) {
throw new InvalidStateException('Invalid state');
}

@HysteriaKa
Copy link

Hey, maube it's too late, but i had that error too, because i went one page back, instead of the origin call. (hope you understand what i mean)

@nitrique
Copy link
Author

Not too late, problem is not solved and this package looks to be not actively maintained. We have planed to implement it by ourself.

@bocharsky-bw
Copy link
Member

Could anyone create a PR that would fix this issue? I'd be happy to review it

@lu43n
Copy link

lu43n commented Apr 17, 2022

I have the same problem with Safari 15.1. The session with the state key is set in the redirect () method in /Client/OAuth2Client.php, while after redirecting in the getAccessToken method, the session with the key self :: OAUTH2_SESSION_STATE_KEY is missing.

It's weird that everything works fine in Chrome. Anyone can help?

@ikerib
Copy link

ikerib commented Dec 14, 2022

One more with the same issue
I created my server following this https://davegebler.com/post/php/build-oauth2-server-php-symfony#what-we'll-be-building
and then another app with this bundle. The user is logged in on the oauth server but when it comes back and try to find this session variable is null

@ikerib
Copy link

ikerib commented Dec 14, 2022

I'm facing the same issue with Google login.

Symfony 5.4 with new authenticator manager

Session key "knpu.oauth2_client_state" is not found, so "expectedState" is null. This condition is resolved to "true" and exception is triggered.

if (!$actualState || ($actualState !== $expectedState)) {
throw new InvalidStateException('Invalid state');
}

did you found a solution?

@HysteriaKa
Copy link

hey, sorry for my late answer !
I don t really remind all i did, but i had to follow a tutoriel for Symfony 6, there has been somes changes, even i run a symfony 5.4. I remember i had to change some specific stuff in the bundle, the auth thing is not the same anymore.
It was that tutoriel https://www.dev-web.io/2022/03/07/symfony-6-sauthentifier-avec-google-facebook-github/
and i think it's the extends i had to change but not only in my controller
image
I really hope, it helps

@GARINED
Copy link

GARINED commented Mar 20, 2023

Hi,

the problem is still not solved... I already tried to do this in my construct but it still doesn't work

@dpi
Copy link

dpi commented Apr 4, 2023

For me the error

Invalid state parameter passed in callback URL.

Was simply due to having multiple PHP servers (via Kubernetes) serving users. If you got unlucky (highly likely) then you get a different server with different sessions.

From the user perspective the browser will show the browser received a cookie for a brief subsecond, then it is immediately purged.

Switching to an alternative session management strategy worked. Notably from the linked page, this line is particularly relevant:

Symfony stores sessions in files by default. If your application is served by multiple servers, you'll need to use a database instead to make sessions work across different servers.

gl

@cyraid
Copy link

cyraid commented Jul 11, 2023

I had invalid state pop up recently, but that's because I had changed my cookie_samesite from lax to strict.. But apparently Google login doesn't like strict, so I changed it back to lax.

@ManoloTonto1
Copy link

ManoloTonto1 commented Jun 19, 2024

Ok I solved this, here is what I had:

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_connect";
	}

and it should be

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_callback";
	}

You should start authenticating once the user reaches back to your page, not while sending them to the OAuth provider's page.

The reason no state is set is because you really don't have a state. This is also documented somewhere in the middle of the README.

Hope this helps!!!

@zerowebcorp
Copy link

Ok I solved this, here is what I had:

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_connect";
	}

and it should be

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_callback";
	}

You should start authenticating once the user reaches back to your page, not while sending them to the OAuth provider's page.

The reason no state is set is because you really don't have a state. This is also documented somewhere in the middle of the README.

Hope this helps!!!

Can you help me understand this better?

This is what I've

 public function supports(Request $request): ?bool
    {
        return $request->attributes->get('_route') === 'connect_google_check';
    }

image

I am facing this issue where a lot of users cannot login and getting Invalid State error. Mostly on iphone/mac

@ManoloTonto1
Copy link

Ok I solved this, here is what I had:

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_connect";
	}

and it should be

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_callback";
	}

You should start authenticating once the user reaches back to your page, not while sending them to the OAuth provider's page.
The reason no state is set is because you really don't have a state. This is also documented somewhere in the middle of the README.
Hope this helps!!!

Can you help me understand this better?

This is what I've

 public function supports(Request $request): ?bool
    {
        return $request->attributes->get('_route') === 'connect_google_check';
    }

image

I am facing this issue where a lot of users cannot login and getting Invalid State error. Mostly on iphone/mac

Ok, to understand why you are getting the error you need to understand OAUTH in general and also how symfony handles authentication using authenticators.
Disclaimer, I am using Symfony 5.4.

Simplified oauth flow

image

As you can see our server has 2 points of interaction,

  1. handling the initialization of the flow /oauth/connect/{provider}
  2. having a callback once the provider is done processing the user's log in request /oauth/connect/{provider}/callback

Now, here is where things get weird with symfony and its authenticators:
The callback route should be intercepted by the authenticator (think of it as some kind of middleware) to handle the login logic.

Init

	/**
	 * Link to this controller to start the "connect" process
	 *
	 * @Route("/oauth/connect/{provider}", name="oauth_connect")
	 */
	public function startOauthFlow(
		Request $request,
		ClientRegistry $clientRegistry
	) {
		$provider = $request->get("provider");
		try {
			return $clientRegistry
				->getClient($provider)
				->redirect(["openid", "public_profile", "email"], []);
		} catch (Exception $e) {
			$this->addFlash("danger", self::START_ERROR . $e->getMessage());
			return $this->redirectToRoute("app_login");
		}
	}

Callback

	/**
	 * After going to the provider, you're redirected back here
	 * because this is the "redirect_route" you configured
	 * in config/packages/knpu_oauth2_client.yaml
	 *
	 * @Route("/oauth/connect/{provider}/callback", name="oauth_callback", schemes={"https"})
	 */
	public function callback(Request $request, ClientRegistry $clientRegistry) {
		// this can be empty, it will get intercepted by the authenticator
		return;
	}

The callback gets intercepted by this function in your authenticator:

	public function authenticate(Request $request): PassportInterface {
		$provider = $request->get("provider");
		$client = $this->clientRegistry->getClient($provider);
		$accessToken = $this->fetchAccessToken($client);
		$handler = new OauthAuthentication(
			$this->clientRegistry,
			$this->entityManager,
			$accessToken
		);
		return $handler->Authenticate($request);
	}

after that the onSuccess Logic

The last thing you need for this to work out is to add your authenticator to your main firewall and it should work.

firewalls:
       main:
             custom_authenticator: App\Security\Authenticators\OAuthAuthenticator 

as for support, it's only for symfony to know which authenticator it will use. so if you just keep you oauth_callback route you should be fine.

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") == "oauth_callback";
	}

I hope this helps out!

@ikerib
Copy link

ikerib commented Jun 20, 2024

in my case, the problem was I pressed the back button on the browser

@zerowebcorp
Copy link

Ok I solved this, here is what I had:

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_connect";
	}

and it should be

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") === "oauth_callback";
	}

You should start authenticating once the user reaches back to your page, not while sending them to the OAuth provider's page.
The reason no state is set is because you really don't have a state. This is also documented somewhere in the middle of the README.
Hope this helps!!!

Can you help me understand this better?
This is what I've

 public function supports(Request $request): ?bool
    {
        return $request->attributes->get('_route') === 'connect_google_check';
    }

image
I am facing this issue where a lot of users cannot login and getting Invalid State error. Mostly on iphone/mac

Ok, to understand why you are getting the error you need to understand OAUTH in general and also how symfony handles authentication using authenticators. Disclaimer, I am using Symfony 5.4.

Simplified oauth flow

image

As you can see our server has 2 points of interaction,

  1. handling the initialization of the flow /oauth/connect/{provider}
  2. having a callback once the provider is done processing the user's log in request /oauth/connect/{provider}/callback

Now, here is where things get weird with symfony and its authenticators: The callback route should be intercepted by the authenticator (think of it as some kind of middleware) to handle the login logic.

Init

	/**
	 * Link to this controller to start the "connect" process
	 *
	 * @Route("/oauth/connect/{provider}", name="oauth_connect")
	 */
	public function startOauthFlow(
		Request $request,
		ClientRegistry $clientRegistry
	) {
		$provider = $request->get("provider");
		try {
			return $clientRegistry
				->getClient($provider)
				->redirect(["openid", "public_profile", "email"], []);
		} catch (Exception $e) {
			$this->addFlash("danger", self::START_ERROR . $e->getMessage());
			return $this->redirectToRoute("app_login");
		}
	}

Callback

	/**
	 * After going to the provider, you're redirected back here
	 * because this is the "redirect_route" you configured
	 * in config/packages/knpu_oauth2_client.yaml
	 *
	 * @Route("/oauth/connect/{provider}/callback", name="oauth_callback", schemes={"https"})
	 */
	public function callback(Request $request, ClientRegistry $clientRegistry) {
		// this can be empty, it will get intercepted by the authenticator
		return;
	}

The callback gets intercepted by this function in your authenticator:

	public function authenticate(Request $request): PassportInterface {
		$provider = $request->get("provider");
		$client = $this->clientRegistry->getClient($provider);
		$accessToken = $this->fetchAccessToken($client);
		$handler = new OauthAuthentication(
			$this->clientRegistry,
			$this->entityManager,
			$accessToken
		);
		return $handler->Authenticate($request);
	}

after that the onSuccess Logic

The last thing you need for this to work out is to add your authenticator to your main firewall and it should work.

firewalls:
       main:
             custom_authenticator: App\Security\Authenticators\OAuthAuthenticator 

as for support, it's only for symfony to know which authenticator it will use. so if you just keep you oauth_callback route you should be fine.

	public function supports(Request $request): ?bool {
		return $request->attributes->get("_route") == "oauth_callback";
	}

I hope this helps out!

Hi
Thanks for the explanation. I believe I have the proper configuration. I am using Symfony 7.1 and this often gets Invalid State error. I even tried to disable the state option in the configuration.

My clients were frustrated. I finally resolved this yesterday by switching to HWIOAuthBundle and I no longer get the issue. This is clearly a bug in the knpuniversity eco system, unfortunately I don't know how to troubleshoot this. I posted my configuration in the thephpleague repo for the google auth, but it wasn't helpful as well. thephpleague/oauth2-google#124

@tacman
Copy link
Contributor

tacman commented Jun 20, 2024

Thanks for posting this. I also believe that #436 is a bug in the library, but it's related to something deep in the cache or nginx configuration or something related to http v https. While I don't think my issue is related to yours, I guess I should consider your solution (switching to HWIOAuthBundle).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests