Skip to content

Commit

Permalink
Support OIDC token in Facebook provider (Facebook Limited Login) (#698)
Browse files Browse the repository at this point in the history
* Support OIDC token

* Lower firebase/php-jwt min version

* Formatting

* Add docblocks

* formatting

* formatting

---------

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
gdebrauwer and taylorotwell authored Apr 15, 2024
1 parent 97db2cc commit a03e9b2
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 4 deletions.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
"require": {
"php": "^7.2|^8.0",
"ext-json": "*",
"firebase/php-jwt": "^6.4",
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"league/oauth1-client": "^1.10.1"
"league/oauth1-client": "^1.10.1",
"phpseclib/phpseclib": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
Expand Down
73 changes: 70 additions & 3 deletions src/Two/FacebookProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

namespace Laravel\Socialite\Two;

use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Arr;
use phpseclib3\Crypt\RSA;
use phpseclib3\Math\BigInteger;

class FacebookProvider extends AbstractProvider implements ProviderInterface
{
Expand Down Expand Up @@ -93,6 +98,64 @@ protected function getUserByToken($token)
{
$this->lastToken = $token;

return $this->getUserByOIDCToken($token) ??
$this->getUserFromAccessToken($token);
}

/**
* Get user based on the OIDC token.
*
* @param string $token
* @return array
*/
protected function getUserByOIDCToken($token)
{
$kid = json_decode(base64_decode(explode('.', $token)[0]), true)['kid'] ?? null;

if ($kid === null) {
return null;
}

$data = (array) JWT::decode($token, $this->getPublicKeyOfOIDCToken($kid));

throw_if($data['aud'] !== $this->clientId, new Exception('Token has incorrect audience.'));
throw_if($data['iss'] !== 'https://www.facebook.com', new Exception('Token has incorrect issuer.'));

$data['id'] = $data['sub'];
$data['first_name'] = $data['given_name'];
$data['last_name'] = $data['family_name'];

return $data;
}

/**
* Get the public key to verify the signature of OIDC token.
*
* @param string $id
* @return \Firebase\JWT\Key
*/
protected function getPublicKeyOfOIDCToken(string $kid)
{
$response = $this->getHttpClient()->get('https://limited.facebook.com/.well-known/oauth/openid/jwks/');

$key = Arr::first(json_decode($response->getBody()->getContents(), true)['keys'], function ($key) use ($kid) {
return $key['kid'] === $kid;
});

$key['n'] = new BigInteger(JWT::urlsafeB64Decode($key['n']), 256);
$key['e'] = new BigInteger(JWT::urlsafeB64Decode($key['e']), 256);

return new Key((string) RSA::load($key), 'RS256');
}

/**
* Get user based on the access token.
*
* @param string $token
* @return array
*/
protected function getUserFromAccessToken($token)
{
$params = [
'access_token' => $token,
'fields' => implode(',', $this->fields),
Expand All @@ -117,15 +180,19 @@ protected function getUserByToken($token)
*/
protected function mapUserToObject(array $user)
{
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$user['id'].'/picture';
if (! isset($user['sub'])) {
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$user['id'].'/picture';

$avatarOriginalUrl = $avatarUrl.'?width=1920';
}

return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nickname' => null,
'name' => $user['name'] ?? null,
'email' => $user['email'] ?? null,
'avatar' => $avatarUrl.'?type=normal',
'avatar_original' => $avatarUrl.'?width=1920',
'avatar' => $avatarUrl ?? $user['picture'] ?? null,
'avatar_original' => $avatarOriginalUrl ?? $user['picture'] ?? null,
'profileUrl' => $user['link'] ?? null,
]);
}
Expand Down

0 comments on commit a03e9b2

Please sign in to comment.