问题
I'm using Laravel 5.4
and Socialite
so my users can log in with Facebook
.
My website works with subdomain newyork.example.com
, paris.example.com
example.com
The Facebook URL
redirect for the callback login has to be absolute
so I set http://example.com
login/facebook
public function redirectToProvider()
{
$_SESSION['originalURL'] ="http://paris.example.com";
return Socialite::driver('facebook')
->scopes(['rsvp_event', 'public_profile'])
->redirect();
}
login/facebook/callback
public function handleProviderCallback(SocialAccountService $service)
{
$user = $service->createOrGetUser(Socialite::driver('facebook')->user());
// $user->token;
Auth::login($user, true);
$originalURL = $_SESSION['originalURL'];
return redirect()->to($originalURL);
}
Problems
When I am in the route
login/facebook I can view the original URL paris.example.com
with HTTP_POST
When I am in the route login/facebook/callback
the HTTP_POST
is example.com
since the URL of the redirection is example.com
. I tries to save the URL in a session var
but $_SESSION
is empty.
Questions
How I can do to get the original url after the facebook login callback redirection. ? So if I started the login process with paris.example.com
I'm redirected to example.com
and then I get the save url to be redirected
sessions.php
'cookie' => 'laravel_session',
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application but you are free to change this when necessary.
|
*/
'path' => '/',
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| Here you may change the domain of the cookie used to identify a session
| in your application. This will determine which domains the cookie is
| available to in your application. A sensible default has been set.
|
*/
'domain' => env('SESSION_DOMAIN', '.localhost.com'),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you if it can not be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE', false),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. You are free to modify this option if needed.
|
*/
'http_only' => true,
回答1:
I use this way, it's work
public function socialConnectRedirect($type, Request $request)
{
Session::put('redirect', $request->input('redirectTo'));
if($type=='facebook'){
return Socialite::driver($type)->scopes(['email', 'public_profile', 'user_birthday', 'user_location'])->redirect();
}
return Socialite::driver($type)->redirect();
}
In handleSocialCallback function, after user login
Auth::login($checkUser);
return redirect(Session::get('redirect'));
Session::forget('redirect');
回答2:
If you can't share the session between the websites, here's a way to use the state
OAuth parameter to carry the value along.
This code was tested in a custom OAuth provider. Here's how a Facebook implementation could look like (this specific code is untested).
There are security implications to keep in mind when changing the way the state
works. Here's an interesting writing on that http://www.thread-safe.com/2014/05/the-correct-use-of-state-parameter-in.html There's even an IETF draft on how one would store and sign data into the state
parameter https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state-00 (My implentation below uses JSON and is not signed).
<?php
namespace App\Socialite;
use Laravel\Socialite\Two\FacebookProvider;
use Laravel\Socialite\Two\User;
class CustomFacebookProvider extends FacebookProvider
{
protected $statePreviousUrl = null;
public function withPreviousUrl($url)
{
$this->statePreviousUrl = $url;
return $this;
}
protected function getState()
{
// The state becomes a JSON object with both the XRSF protection token and the url
return json_encode([
'state' => parent::getState(),
'url' => $this->statePreviousUrl,
]);
}
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$storedState = $this->request->session()->pull('state');
$requestState = $this->request->input('state');
$requestStateData = json_decode($requestState, true);
// If the JSON is valid we extract the url here
if (!is_null($requestStateData) && array_key_exists('url', $requestStateData)) {
// Don't forget, this value is unsafe. Do additional checks before redirecting to that url
$this->statePreviousUrl = $requestStateData['url'];
}
// If you don't share your session between your instances you can play it "stateless" by always returning false here
// Doing so you loose all XRSF protection ! (but this might be the only way if you don't share your cookies)
// return false;
// If the session is shared, we finish by checking the full state
// We compare the full json objects, no need to extract the state parameter
return ! (strlen($storedState) > 0 && $requestState === $storedState);
}
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
// Data here will vary from provider to provider. The Facebook one is a bit more complex
'id' => $user['id'],
'email' => $user['email'],
// We add the extracted URL here so it can be access from the controller
'previous_url' => $this->statePreviousUrl,
]);
}
}
Register the custom controller:
<?php
namespace App\Socialite;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
use Laravel\Socialite\Contracts\Factory;
class ServiceProvider extends BaseServiceProvider
{
public function boot()
{
// @see https://medium.com/laravel-news/adding-auth-providers-to-laravel-socialite-ca0335929e42
$socialite = $this->app->make(Factory::class);
$socialite->extend(
'custom-facebook',
function ($app) use ($socialite) {
$config = $app['config']['services.facebook'];
return $socialite->buildProvider(CustomFacebookProvider::class, $config);
}
);
}
}
Usage:
<?php
namespace App\Http\Controllers;
use App\User;
use Laravel\Socialite\Contracts\Factory;
class FacebookLoginController extends Controller
{
/**
* @var Factory
*/
protected $socialite;
public function __construct(Factory $socialite)
{
$this->socialite = $socialite;
}
public function redirectToProvider()
{
return $this->socialite->driver('custom-facebook')->withPreviousUrl('https://paris.example.com/')->redirect();
}
public function handleProviderCallback()
{
$data = $this->socialite->driver('custom-facebook')->user();
dd($data->previous_url);
}
}
回答3:
The key was not to use $_SESSION
but session(['city' => 'paris'])
; and session('city')
to retrieve the value.
来源:https://stackoverflow.com/questions/44621555/laravel-socialite-save-url-before-redirection