DutchCodingCompany / filament-socialite

Add OAuth login through Laravel Socialite to Filament.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: Visiting OAuth callback URL directly throws an exception

caendesilva opened this issue · comments

Visiting http://localhost:8000/oauth/callback/github directly results in an InvalidCallbackPayload exception: "The panel could not be decrypted from the OAuth callback."

For a smoother experience (at least in production where this renders as a 500 error) I think that visiting the callback URL directly without any request data or parameters should redirect to the HOME route or similar.

commented

Sadly we can't know to which panel we have to redirect (since the decrypt exception is exactly that: we cannot decrypt the panel-id from the URL). However, you can make this exception easily renderable by adding it to your exception handler, and add your own logic that way:

// App\Exceptions\Handler.php

/**
 * Register the exception handling callbacks for the application.
 */
public function register(): void
{
    $this->renderable(function (
        \DutchCodingCompany\FilamentSocialite\Exceptions\InvalidCallbackPayload $e,
        \Illuminate\Http\Request $request
    ) {
        return redirect()->to('/');
    });
}

Sadly we can't know to which panel we have to redirect (since the decrypt exception is exactly that: we cannot decrypt the panel-id from the URL). However, you can make this exception easily renderable by adding it to your exception handler, and add your own logic that way:

// App\Exceptions\Handler.php

/**
 * Register the exception handling callbacks for the application.
 */
public function register(): void
{
    $this->renderable(function (
        \DutchCodingCompany\FilamentSocialite\Exceptions\InvalidCallbackPayload $e,
        \Illuminate\Http\Request $request
    ) {
        return redirect()->to('/');
    });
}

Understood. Can we add some kind of handling to the package, do you think?

The custom exception is so a not working configuration is not hidden by eg. a redirect. A developer can choose to have this exception redirect application-side if he so desires, as suggested by Bert.

The custom exception is so a not working configuration is not hidden by eg. a redirect. A developer can choose to have this exception redirect application-side if he so desires, as suggested by Bert.

Just came across this same error when being redirected from the GitHub UI after updating the installation permissions to add the app on a new repo. Maybe that's an actual bug? Is there a way to add the state needed to GitHub UI triggered actions, or maybe we can for projects with only one panel assume that one panel is the intended one?

Just came across this same error when being redirected from the GitHub UI after updating the installation permissions to add the app on a new repo. Maybe that's an actual bug? Is there a way to add the state needed to GitHub UI triggered actions, or maybe we can for projects with only one panel assume that one panel is the intended one?

I am not sure what flow you are describing. We validated the package works as expected with github's oauth flow. Could you describe in detail what steps are done?

Just came across this same error when being redirected from the GitHub UI after updating the installation permissions to add the app on a new repo. Maybe that's an actual bug? Is there a way to add the state needed to GitHub UI triggered actions, or maybe we can for projects with only one panel assume that one panel is the intended one?

I am not sure what flow you are describing. We validated the package works as expected with github's oauth flow. Could you describe in detail what steps are done?

I'll try to repro today!

Just came across this same error when being redirected from the GitHub UI after updating the installation permissions to add the app on a new repo. Maybe that's an actual bug? Is there a way to add the state needed to GitHub UI triggered actions, or maybe we can for projects with only one panel assume that one panel is the intended one?

I am not sure what flow you are describing. We validated the package works as expected with github's oauth flow. Could you describe in detail what steps are done?

Reproduction steps:

  1. Visit https://github.com/settings/applications
  2. Select tab Installed GitHub Apps
  3. Hit the Configure button for your App
  4. Under Repository access, (assuming "Only select repositories" is already selected), select a new repo, and hit Save
  5. GitHub redirects back to the OAuth callback URL, which leads to the InvalidCallbackPayload exception
    • The GitHub redirect adds the &setup_action=update state parameter, which could possibly be useful in a fix

Came across this again when trying to reauthenticate the application.

Sadly we can't know to which panel we have to redirect (since the decrypt exception is exactly that: we cannot decrypt the panel-id from the URL).

@dododedodonl Is there a way we can provide the data needed for decryption? Or since most apps only have one panel, can we assume they want to log into that? (Or with a config option to specify which panel uses OAuth?)

For example

public static function decrypt(Request $request): string
{
    try {
        return Crypt::decrypt($request->query('state'));
    } catch (DecryptException $e) {
        if (config('filament-socialite.oauth-panel')) {
            return config('filament-socialite.oauth-panel');
        }

        throw InvalidCallbackPayload::make($e);
    }
}
commented

What is the expected behaviour when GitHub sends a call to the oauth endpoint with &setup_action=update? There are ways to have the correct panel inferred (like you suggested) but what happens then? The route will still point to the SocialiteController@processCallback() with the default plugin logic (logging in a user).

I'm also struggling with this on the SAML2 driver. $request->get('state') is just null here:
https://github.com/DutchCodingCompany/filament-socialite/blob/main/src/Http/Middleware/PanelFromUrlQuery.php#L31

Do I need to set up this relay state URL?
Screenshot 2024-06-21 at 21 49 14

There is something in the request but it's called RelayState.
Screenshot 2024-06-21 at 21 52 16

But even if I try to decrypt that instead in the PanelFromUrlQuery::decrypt() method by changing $request->get('state') to $request->get('RelayState'), it fails to decrypt it and errors. How can you setting the state? Is it not possible in my case and I need to catch the error?

commented

@juliangums could you try this branch "dutchcodingcompany/filament-socialite": "dev-improve-routing" ? I have made a change to the callback route so it should no longer depend on a decryptable panel in the ?state= parameter.

Note that the panel is now part of the url prefix, so you will have to change your redirect URL to:

{yourpanelid}/oauth/callback/{yourprovider}

Additionally, you might need to enable Provider::make('yourprovider')->stateless() depending on your oauth provider (this bypasses any CSRF check though).

I do not know the meaning of your SAMLResponse and RelayState variables, so you'll have to check yourself what they are used for and what they mean.

Thanks @bert-w
Tried it just now and for some reason I'm getting a 419 / page expired even if I use ->stateless().
Will look into it a bit more tomorrow.

I also found out that SAML2 kind of ignores 'state' as it looks for a 'RelayState' parameter. That one is a random string set by the driver. I'm working out how it actually works but the SAML2 driver seems to work differently when it comes to using the stateless methods. I am trying to get to the bottom of this in this issue SocialiteProviders/Providers#1218 and see if it is something that might need to be fixed there rather than here. I'll keep you updated.

commented

@juliangums that 419 sounds like a CSRF error, in other words you need to add the callback URL to the exception list, because it will not contain a CSRF token (see https://laravel.com/docs/10.x/csrf#csrf-excluding-uris).

@bert-w yes, I have done just that this morning and it worked perfectly.
I am still struggling to get it to work in a stateful way as it should be possible. But it seems to be an issue with the provider or my session settings as it isn't matching the session.

Other than that this works perfectly. Thanks so much for this! I think this can be merged.

@juliangums that 419 sounds like a CSRF error, in other words you need to add the callback URL to the exception list, because it will not contain a CSRF token (see https://laravel.com/docs/10.x/csrf#csrf-excluding-uris).

Perhaps you should mention this in the readme when merging the feature.