- 框架初始化
 - 安装插件
 - 修复PHP8.4报错
This commit is contained in:
2025-04-19 17:21:20 +08:00
commit c6a4e1f5f6
5306 changed files with 967782 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
use ArrayAccess;
use InvalidArgumentException;
use JsonSerializable;
/**
* Class AccessToken.
*/
class AccessToken implements AccessTokenInterface, ArrayAccess, JsonSerializable
{
use HasAttributes;
/**
* AccessToken constructor.
*
* @param array $attributes
*/
public function __construct(?array $attributes)
{
if (empty($attributes['access_token'])) {
throw new InvalidArgumentException('The key "access_token" could not be empty.');
}
$this->attributes = $attributes;
}
/**
* Return the access token string.
*
* @return string
*/
public function getToken()
{
return $this->getAttribute('access_token');
}
/**
* Return the refresh token string.
*
* @return string
*/
public function getRefreshToken()
{
return $this->getAttribute('refresh_token');
}
/**
* Set refresh token into this object.
*
* @param string $token
*/
public function setRefreshToken($token)
{
$this->setAttribute('refresh_token', $token);
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return strval($this->getAttribute('access_token', ''));
}
/**
* {@inheritdoc}
*/
public function jsonSerialize()
{
return $this->getToken();
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
/**
* Interface AccessTokenInterface.
*/
interface AccessTokenInterface
{
/**
* Return the access token string.
*
* @return string
*/
public function getToken();
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
class AuthorizeFailedException extends \RuntimeException
{
/**
* Response body.
*
* @var array
*/
public $body;
/**
* Constructor.
*
* @param string $message
* @param array $body
*/
public function __construct($message, $body)
{
parent::__construct($message, -1);
$this->body = $body;
}
}

180
vendor/overtrue/socialite/src/Config.php vendored Normal file
View File

@@ -0,0 +1,180 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
use ArrayAccess;
use InvalidArgumentException;
/**
* Class Config.
*/
class Config implements ArrayAccess
{
/**
* @var array
*/
protected $config;
/**
* Config constructor.
*
* @param array $config
*/
public function __construct(?array $config)
{
$this->config = $config;
}
/**
* Get an item from an array using "dot" notation.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key, $default = null)
{
$config = $this->config;
if (is_null($key)) {
return $config;
}
if (isset($config[$key])) {
return $config[$key];
}
foreach (explode('.', $key) as $segment) {
if (!is_array($config) || !array_key_exists($segment, $config)) {
return $default;
}
$config = $config[$segment];
}
return $config;
}
/**
* Set an array item to a given value using "dot" notation.
*
* @param string $key
* @param mixed $value
*
* @return array
*/
public function set($key, $value)
{
if (is_null($key)) {
throw new InvalidArgumentException('Invalid config key.');
}
$keys = explode('.', $key);
$config = &$this->config;
while (count($keys) > 1) {
$key = array_shift($keys);
if (!isset($config[$key]) || !is_array($config[$key])) {
$config[$key] = [];
}
$config = &$config[$key];
}
$config[array_shift($keys)] = $value;
return $config;
}
/**
* Determine if the given configuration value exists.
*
* @param string $key
*
* @return bool
*/
public function has($key)
{
return (bool) $this->get($key);
}
/**
* Whether a offset exists.
*
* @see http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned
*
* @since 5.0.0
*/
public function offsetExists($offset)
{
return array_key_exists($offset, $this->config);
}
/**
* Offset to retrieve.
*
* @see http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
*
* @return mixed Can return all value types
*
* @since 5.0.0
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* Offset to set.
*
* @see http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
/**
* Offset to unset.
*
* @see http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @since 5.0.0
*/
public function offsetUnset($offset)
{
$this->set($offset, null);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
/**
* Interface FactoryInterface.
*/
interface FactoryInterface
{
/**
* Get an OAuth provider implementation.
*
* @param string $driver
*
* @return \Overtrue\Socialite\ProviderInterface
*/
public function driver($driver);
}

View File

@@ -0,0 +1,135 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
/**
* Trait HasAttributes.
*/
trait HasAttributes
{
/**
* @var array
*/
protected $attributes = [];
/**
* Return the attributes.
*
* @return array
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Return the extra attribute.
*
* @param string $name
* @param string $default
*
* @return mixed
*/
public function getAttribute($name, $default = null)
{
return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
}
/**
* Set extra attributes.
*
* @param string $name
* @param mixed $value
*
* @return $this
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}
/**
* Map the given array onto the user's properties.
*
* @param array $attributes
*
* @return $this
*/
public function merge(?array $attributes)
{
$this->attributes = array_merge($this->attributes, $attributes);
return $this;
}
/**
* {@inheritdoc}
*/
public function offsetExists($offset)
{
return array_key_exists($offset, $this->attributes);
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset)
{
return $this->getAttribute($offset);
}
/**
* {@inheritdoc}
*/
public function offsetSet($offset, $value)
{
$this->setAttribute($offset, $value);
}
/**
* {@inheritdoc}
*/
public function offsetUnset($offset)
{
unset($this->attributes[$offset]);
}
/**
* {@inheritdoc}
*/
public function __get($property)
{
return $this->getAttribute($property);
}
/**
* Return array.
*
* @return array
*/
public function toArray()
{
return $this->getAttributes();
}
/**
* Return JSON.
*
* @return string
*/
public function toJSON()
{
return json_encode($this->getAttributes(), JSON_UNESCAPED_UNICODE);
}
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
class InvalidArgumentException extends \InvalidArgumentException
{
}

View File

@@ -0,0 +1,16 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
class InvalidStateException extends \InvalidArgumentException
{
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
interface ProviderInterface
{
/**
* Redirect the user to the authentication page for the provider.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function redirect();
/**
* Get the User instance for the authenticated user.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return \Overtrue\Socialite\User
*/
public function user(AccessTokenInterface $token = null);
}

View File

@@ -0,0 +1,585 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use Overtrue\Socialite\AccessToken;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\AuthorizeFailedException;
use Overtrue\Socialite\Config;
use Overtrue\Socialite\InvalidStateException;
use Overtrue\Socialite\ProviderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* Class AbstractProvider.
*/
abstract class AbstractProvider implements ProviderInterface
{
/**
* Provider name.
*
* @var string
*/
protected $name;
/**
* The HTTP request instance.
*
* @var \Symfony\Component\HttpFoundation\Request
*/
protected $request;
/**
* Driver config.
*
* @var Config
*/
protected $config;
/**
* The client ID.
*
* @var string
*/
protected $clientId;
/**
* The client secret.
*
* @var string
*/
protected $clientSecret;
/**
* @var \Overtrue\Socialite\AccessTokenInterface
*/
protected $accessToken;
/**
* The redirect URL.
*
* @var string
*/
protected $redirectUrl;
/**
* The custom parameters to be sent with the request.
*
* @var array
*/
protected $parameters = [];
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = [];
/**
* The separating character for the requested scopes.
*
* @var string
*/
protected $scopeSeparator = ',';
/**
* The type of the encoding in the query.
*
* @var int Can be either PHP_QUERY_RFC3986 or PHP_QUERY_RFC1738
*/
protected $encodingType = PHP_QUERY_RFC1738;
/**
* Indicates if the session state should be utilized.
*
* @var bool
*/
protected $stateless = false;
/**
* The options for guzzle\client.
*
* @var array
*/
protected static $guzzleOptions = ['http_errors' => false];
/**
* Create a new provider instance.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param array $config
*/
public function __construct(Request $request, $config)
{
// 兼容处理
if (!\is_array($config)) {
$config = [
'client_id' => \func_get_arg(1),
'client_secret' => \func_get_arg(2),
'redirect' => \func_get_arg(3) ?: null,
];
}
$this->config = new Config($config);
$this->request = $request;
$this->clientId = $config['client_id'];
$this->clientSecret = $config['client_secret'];
$this->redirectUrl = isset($config['redirect']) ? $config['redirect'] : null;
}
/**
* Get the authentication URL for the provider.
*
* @param string $state
*
* @return string
*/
abstract protected function getAuthUrl($state);
/**
* Get the token URL for the provider.
*
* @return string
*/
abstract protected function getTokenUrl();
/**
* Get the raw user for the given access token.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return array
*/
abstract protected function getUserByToken(AccessTokenInterface $token);
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
abstract protected function mapUserToObject(?array $user);
/**
* Redirect the user of the application to the provider's authentication screen.
*
* @param string $redirectUrl
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function redirect($redirectUrl = null)
{
$state = null;
if (!is_null($redirectUrl)) {
$this->redirectUrl = $redirectUrl;
}
if ($this->usesState()) {
$state = $this->makeState();
}
return new RedirectResponse($this->getAuthUrl($state));
}
/**
* {@inheritdoc}
*/
public function user(AccessTokenInterface $token = null)
{
if (is_null($token) && $this->hasInvalidState()) {
throw new InvalidStateException();
}
$token = $token ?: $this->getAccessToken($this->getCode());
$user = $this->getUserByToken($token);
$user = $this->mapUserToObject($user)->merge(['original' => $user]);
return $user->setToken($token)->setProviderName($this->getName());
}
/**
* Set redirect url.
*
* @param string $redirectUrl
*
* @return $this
*/
public function setRedirectUrl($redirectUrl)
{
$this->redirectUrl = $redirectUrl;
return $this;
}
/**
* Set redirect url.
*
* @param string $redirectUrl
*
* @return $this
*/
public function withRedirectUrl($redirectUrl)
{
$this->redirectUrl = $redirectUrl;
return $this;
}
/**
* Return the redirect url.
*
* @return string
*/
public function getRedirectUrl()
{
return $this->redirectUrl;
}
/**
* @param \Overtrue\Socialite\AccessTokenInterface $accessToken
*
* @return $this
*/
public function setAccessToken(AccessTokenInterface $accessToken)
{
$this->accessToken = $accessToken;
return $this;
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessTokenInterface
*/
public function getAccessToken($code)
{
if ($this->accessToken) {
return $this->accessToken;
}
$guzzleVersion = \defined(ClientInterface::class.'::VERSION') ? \constant(ClientInterface::class.'::VERSION') : 7;
$postKey = (1 === version_compare($guzzleVersion, '6')) ? 'form_params' : 'body';
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
'headers' => ['Accept' => 'application/json'],
$postKey => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody());
}
/**
* Set the scopes of the requested access.
*
* @param array $scopes
*
* @return $this
*/
public function scopes(?array $scopes)
{
$this->scopes = $scopes;
return $this;
}
/**
* Set the request instance.
*
* @param Request $request
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Get the request instance.
*
* @return \Symfony\Component\HttpFoundation\Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Indicates that the provider should operate as stateless.
*
* @return $this
*/
public function stateless()
{
$this->stateless = true;
return $this;
}
/**
* Set the custom parameters of the request.
*
* @param array $parameters
*
* @return $this
*/
public function with(?array $parameters)
{
$this->parameters = $parameters;
return $this;
}
/**
* @throws \ReflectionException
*
* @return string
*/
public function getName()
{
if (empty($this->name)) {
$this->name = strstr((new \ReflectionClass(get_class($this)))->getShortName(), 'Provider', true);
}
return $this->name;
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Get the authentication URL for the provider.
*
* @param string $url
* @param string $state
*
* @return string
*/
protected function buildAuthUrlFromBase($url, $state)
{
return $url.'?'.http_build_query($this->getCodeFields($state), '', '&', $this->encodingType);
}
/**
* Get the GET parameters for the code request.
*
* @param string|null $state
*
* @return array
*/
protected function getCodeFields($state = null)
{
$fields = array_merge([
'client_id' => $this->config['client_id'],
'redirect_uri' => $this->redirectUrl,
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
'response_type' => 'code',
], $this->parameters);
if ($this->usesState()) {
$fields['state'] = $state;
}
return $fields;
}
/**
* Format the given scopes.
*
* @param array $scopes
* @param string $scopeSeparator
*
* @return string
*/
protected function formatScopes(?array $scopes, $scopeSeparator)
{
return implode($scopeSeparator, $scopes);
}
/**
* Determine if the current request / session has a mismatching "state".
*
* @return bool
*/
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$state = $this->request->getSession()->get('state');
return !(strlen($state) > 0 && $this->request->get('state') === $state);
}
/**
* Get the POST fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return [
'client_id' => $this->getConfig()->get('client_id'),
'client_secret' => $this->getConfig()->get('client_secret'),
'code' => $code,
'redirect_uri' => $this->redirectUrl,
];
}
/**
* Get the access token from the token response body.
*
* @param \Psr\Http\Message\StreamInterface|array $body
*
* @return \Overtrue\Socialite\AccessTokenInterface
*/
protected function parseAccessToken($body)
{
if (!is_array($body)) {
$body = json_decode($body, true);
}
if (empty($body['access_token'])) {
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
}
return new AccessToken($body);
}
/**
* Get the code from the request.
*
* @return string
*/
protected function getCode()
{
return $this->request->get('code');
}
/**
* Get a fresh instance of the Guzzle HTTP client.
*
* @return \GuzzleHttp\Client
*/
protected function getHttpClient()
{
return new Client(self::$guzzleOptions);
}
/**
* Set options for Guzzle HTTP client.
*
* @param array $config
*
* @return array
*/
public static function setGuzzleOptions($config = [])
{
return self::$guzzleOptions = $config;
}
/**
* Determine if the provider is operating with state.
*
* @return bool
*/
protected function usesState()
{
return !$this->stateless;
}
/**
* Determine if the provider is operating as stateless.
*
* @return bool
*/
protected function isStateless()
{
return !$this->request->hasSession() || $this->stateless;
}
/**
* Return array item by key.
*
* @param array $array
* @param string $key
* @param mixed $default
*
* @return mixed
*/
protected function arrayItem(?array $array, $key, $default = null)
{
if (is_null($key)) {
return $array;
}
if (isset($array[$key])) {
return $array[$key];
}
foreach (explode('.', $key) as $segment) {
if (!is_array($array) || !array_key_exists($segment, $array)) {
return $default;
}
$array = $array[$segment];
}
return $array;
}
/**
* Put state to session storage and return it.
*
* @return string|bool
*/
protected function makeState()
{
if (!$this->request->hasSession()) {
return false;
}
$state = sha1(uniqid(mt_rand(1, 1000000), true));
$session = $this->request->getSession();
if (is_callable([$session, 'put'])) {
$session->put('state', $state);
} elseif (is_callable([$session, 'set'])) {
$session->set('state', $state);
} else {
return false;
}
return $state;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class BaiduProvider.
*
* @see https://developer.baidu.com/wiki/index.php?title=docs/oauth [OAuth 2.0 授权机制说明]
*/
class BaiduProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base url of Weibo API.
*
* @var string
*/
protected $baseUrl = 'https://openapi.baidu.com';
/**
* The API version for the request.
*
* @var string
*/
protected $version = '2.0';
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = [''];
/**
* The uid of user authorized.
*
* @var int
*/
protected $uid;
protected $display = 'popup';
/**
* Get the authentication URL for the provider.
*
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth/'.$this->version.'/authorize', $state);
}
/**
* {@inheritdoc}.
*/
protected function getCodeFields($state = null)
{
return array_merge([
'response_type' => 'code',
'client_id' => $this->getConfig()->get('client_id'),
'redirect_uri' => $this->redirectUrl,
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
'display' => $this->display,
], $this->parameters);
}
/**
* Get the token URL for the provider.
*
* @return string
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/oauth/'.$this->version.'/token';
}
/**
* Get the Post fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* Get the raw user for the given access token.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return array
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get($this->baseUrl.'/rest/'.$this->version.'/passport/users/getInfo', [
'query' => [
'access_token' => $token->getToken(),
],
'headers' => [
'Accept' => 'application/json',
],
]);
return json_decode($response->getBody(), true);
}
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
protected function mapUserToObject(?array $user)
{
$realname = $this->arrayItem($user, 'realname');
return new User([
'id' => $this->arrayItem($user, 'userid'),
'nickname' => empty($realname) ? '' : $realname,
'name' => $this->arrayItem($user, 'username'),
'email' => '',
'avatar' => $this->arrayItem($user, 'portrait'),
]);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessToken;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class DouYinProvider.
*
* @author haoliang@qiyuankeji.vip
*
* @see http://open.douyin.com/platform
*/
class DouYinProvider extends AbstractProvider implements ProviderInterface
{
/**
* 抖音接口域名.
*
* @var string
*/
protected $baseUrl = 'https://open.douyin.com';
/**
* 应用授权作用域.
*
* @var array
*/
protected $scopes = ['user_info'];
/**
* 获取登录页面地址.
*
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/platform/oauth/connect', $state);
}
/**
* 获取授权码接口参数.
*
* @param string|null $state
*
* @return array
*/
public function getCodeFields($state = null)
{
$fields = [
'client_key' => $this->getConfig()->get('client_id'),
'redirect_uri' => $this->redirectUrl,
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
'response_type' => 'code',
];
if ($this->usesState()) {
$fields['state'] = $state;
}
return $fields;
}
/**
* 获取access_token地址.
*
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/oauth/access_token';
}
/**
* 通过code获取access_token.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
'query' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody()->getContents());
}
/**
* 获取access_token接口参数.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return [
'client_key' => $this->getConfig()->get('client_id'),
'client_secret' => $this->getConfig()->get('client_secret'),
'code' => $code,
'grant_type' => 'authorization_code',
];
}
/**
* 格式化token.
*
* @param \Psr\Http\Message\StreamInterface|array $body
*
* @return \Overtrue\Socialite\AccessTokenInterface
*/
protected function parseAccessToken($body)
{
if (!is_array($body)) {
$body = json_decode($body, true);
}
if (empty($body['data']['access_token'])) {
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
}
return new AccessToken($body['data']);
}
/**
* 通过token 获取用户信息.
*
* @param AccessTokenInterface $token
*
* @return array|mixed
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$userUrl = $this->baseUrl.'/oauth/userinfo/';
$response = $this->getHttpClient()->get(
$userUrl,
[
'query' => [
'access_token' => $token->getToken(),
'open_id' => $token['open_id'],
],
]
);
return json_decode($response->getBody(), true);
}
/**
* 格式化用户信息.
*
* @param array $user
*
* @return User
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'open_id'),
'username' => $this->arrayItem($user, 'nickname'),
'nickname' => $this->arrayItem($user, 'nickname'),
'avatar' => $this->arrayItem($user, 'avatar'),
]);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class DoubanProvider.
*
* @see http://developers.douban.com/wiki/?title=oauth2 [使用 OAuth 2.0 访问豆瓣 API]
*/
class DoubanProvider extends AbstractProvider implements ProviderInterface
{
/**
* {@inheritdoc}.
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://www.douban.com/service/auth2/auth', $state);
}
/**
* {@inheritdoc}.
*/
protected function getTokenUrl()
{
return 'https://www.douban.com/service/auth2/token';
}
/**
* {@inheritdoc}.
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get('https://api.douban.com/v2/user/~me', [
'headers' => [
'Authorization' => 'Bearer '.$token->getToken(),
],
]);
return json_decode($response->getBody()->getContents(), true);
}
/**
* {@inheritdoc}.
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'id'),
'nickname' => $this->arrayItem($user, 'name'),
'name' => $this->arrayItem($user, 'name'),
'avatar' => $this->arrayItem($user, 'large_avatar'),
'email' => null,
]);
}
/**
* {@inheritdoc}.
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* {@inheritdoc}.
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
'form_params' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody()->getContents());
}
}

View File

@@ -0,0 +1,168 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class FacebookProvider.
*
* @see https://developers.facebook.com/docs/graph-api [Facebook - Graph API]
*/
class FacebookProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base Facebook Graph URL.
*
* @var string
*/
protected $graphUrl = 'https://graph.facebook.com';
/**
* The Graph API version for the request.
*
* @var string
*/
protected $version = 'v3.3';
/**
* The user fields being requested.
*
* @var array
*/
protected $fields = ['first_name', 'last_name', 'email', 'gender', 'verified'];
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['email'];
/**
* Display the dialog in a popup view.
*
* @var bool
*/
protected $popup = false;
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://www.facebook.com/'.$this->version.'/dialog/oauth', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->graphUrl.'/oauth/access_token';
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
'query' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody());
}
/**
* {@inheritdoc}
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$appSecretProof = hash_hmac('sha256', $token->getToken(), $this->getConfig()->get('client_secret'));
$response = $this->getHttpClient()->get($this->graphUrl.'/'.$this->version.'/me?access_token='.$token.'&appsecret_proof='.$appSecretProof.'&fields='.implode(',', $this->fields), [
'headers' => [
'Accept' => 'application/json',
],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(?array $user)
{
$userId = $this->arrayItem($user, 'id');
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$userId.'/picture';
$firstName = $this->arrayItem($user, 'first_name');
$lastName = $this->arrayItem($user, 'last_name');
return new User([
'id' => $this->arrayItem($user, 'id'),
'nickname' => null,
'name' => $firstName.' '.$lastName,
'email' => $this->arrayItem($user, 'email'),
'avatar' => $userId ? $avatarUrl.'?type=normal' : null,
'avatar_original' => $userId ? $avatarUrl.'?width=1920' : null,
]);
}
/**
* {@inheritdoc}
*/
protected function getCodeFields($state = null)
{
$fields = parent::getCodeFields($state);
if ($this->popup) {
$fields['display'] = 'popup';
}
return $fields;
}
/**
* Set the user fields to request from Facebook.
*
* @param array $fields
*
* @return $this
*/
public function fields(?array $fields)
{
$this->fields = $fields;
return $this;
}
/**
* Set the dialog to be displayed as a popup.
*
* @return $this
*/
public function asPopup()
{
$this->popup = true;
return $this;
}
}

View File

@@ -0,0 +1,192 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessToken;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\AuthorizeFailedException;
use Overtrue\Socialite\InvalidStateException;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class FeiShuProvider.
*
* @author qijian.song@show.world
*
* @see https://open.feishu.cn/
*/
class FeiShuProvider extends AbstractProvider implements ProviderInterface
{
/**
* 飞书接口域名.
*
* @var string
*/
protected $baseUrl = 'https://open.feishu.cn';
/**
* 应用授权作用域.
*
* @var array
*/
protected $scopes = ['user_info'];
/**
* 获取登录页面地址.
*
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/open-apis/authen/v1/index', $state);
}
/**
* 获取授权码接口参数.
*
* @param string|null $state
*
* @return array
*/
protected function getCodeFields($state = null)
{
$fields = [
'redirect_uri' => $this->redirectUrl,
'app_id' => $this->getConfig()->get('client_id'),
];
if ($this->usesState()) {
$fields['state'] = $state;
}
return $fields;
}
/**
* 获取 app_access_token 地址.
*
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/open-apis/auth/v3/app_access_token/internal';
}
/**
* 获取 app_access_token.
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code = '')
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
'headers' => ['Content-Type' => 'application/json'],
'json' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody()->getContents());
}
/**
* 获取 app_access_token 接口参数.
*
* @return array
*/
protected function getTokenFields($code)
{
return [
'app_id' => $this->getConfig()->get('client_id'),
'app_secret' => $this->getConfig()->get('client_secret'),
];
}
/**
* 格式化 token.
*
* @param \Psr\Http\Message\StreamInterface|array $body
*
* @return \Overtrue\Socialite\AccessTokenInterface
*/
protected function parseAccessToken($body)
{
if (!is_array($body)) {
$body = json_decode($body, true);
}
if (empty($body['app_access_token'])) {
throw new AuthorizeFailedException('Authorize Failed: '.json_encode($body, JSON_UNESCAPED_UNICODE), $body);
}
$data['access_token'] = $body['app_access_token'];
return new AccessToken($data);
}
/**
* 获取用户信息.
*
* @return array|mixed
*/
public function user(AccessTokenInterface $token = null)
{
if (is_null($token) && $this->hasInvalidState()) {
throw new InvalidStateException();
}
$token = $token ?: $this->getAccessToken();
$user = $this->getUserByToken($token, $this->getCode());
$user = $this->mapUserToObject($user)->merge(['original' => $user]);
return $user->setToken($token)->setProviderName($this->getName());
}
/**
* 通过 token 获取用户信息.
*
* @return array|mixed
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$userUrl = $this->baseUrl.'/open-apis/authen/v1/access_token';
$response = $this->getHttpClient()->post(
$userUrl,
[
'json' => [
'app_access_token' => $token->getToken(),
'code' => $this->getCode(),
'grant_type' => 'authorization_code',
],
]
);
$result = json_decode($response->getBody(), true);
return $result['data'];
}
/**
* 格式化用户信息.
*
* @return User
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'open_id'),
'username' => $this->arrayItem($user, 'name'),
'nickname' => $this->arrayItem($user, 'name'),
'avatar' => $this->arrayItem($user, 'avatar_url'),
]);
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Exception;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class GitHubProvider.
*/
class GitHubProvider extends AbstractProvider implements ProviderInterface
{
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['user:email'];
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://github.com/login/oauth/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://github.com/login/oauth/access_token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$userUrl = 'https://api.github.com/user';
$response = $this->getHttpClient()->get(
$userUrl,
$this->createAuthorizationHeaders($token)
);
$user = json_decode($response->getBody(), true);
if (in_array('user:email', $this->scopes)) {
$user['email'] = $this->getEmailByToken($token);
}
return $user;
}
/**
* Get the email for the given access token.
*
* @param string $token
*
* @return string|null
*/
protected function getEmailByToken($token)
{
$emailsUrl = 'https://api.github.com/user/emails';
try {
$response = $this->getHttpClient()->get(
$emailsUrl,
$this->createAuthorizationHeaders($token)
);
} catch (Exception $e) {
return;
}
foreach (json_decode($response->getBody(), true) as $email) {
if ($email['primary'] && $email['verified']) {
return $email['email'];
}
}
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'id'),
'username' => $this->arrayItem($user, 'login'),
'nickname' => $this->arrayItem($user, 'login'),
'name' => $this->arrayItem($user, 'name'),
'email' => $this->arrayItem($user, 'email'),
'avatar' => $this->arrayItem($user, 'avatar_url'),
]);
}
/**
* Get the default options for an HTTP request.
*
* @param string $token
*
* @return array
*/
protected function createAuthorizationHeaders(?string $token)
{
return [
'headers' => [
'Accept' => 'application/vnd.github.v3+json',
'Authorization' => sprintf('token %s', $token),
],
];
}
}

View File

@@ -0,0 +1,119 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use GuzzleHttp\ClientInterface;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class GoogleProvider.
*
* @see https://developers.google.com/identity/protocols/OpenIDConnect [OpenID Connect]
*/
class GoogleProvider extends AbstractProvider implements ProviderInterface
{
/**
* The separating character for the requested scopes.
*
* @var string
*/
protected $scopeSeparator = ' ';
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
];
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://accounts.google.com/o/oauth2/v2/auth', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.googleapis.com/oauth2/v4/token';
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return string
*/
public function getAccessToken($code)
{
$guzzleVersion = \defined(ClientInterface::class.'::VERSION') ? \constant(ClientInterface::class.'::VERSION') : 7;
$postKey = (1 === version_compare($guzzleVersion, '6')) ? 'form_params' : 'body';
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
$postKey => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody());
}
/**
* Get the POST fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* {@inheritdoc}
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get('https://www.googleapis.com/userinfo/v2/me', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$token->getToken(),
],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'id'),
'username' => $this->arrayItem($user, 'email'),
'nickname' => $this->arrayItem($user, 'name'),
'name' => $this->arrayItem($user, 'name'),
'email' => $this->arrayItem($user, 'email'),
'avatar' => $this->arrayItem($user, 'picture'),
]);
}
}

View File

@@ -0,0 +1,181 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class LinkedinProvider.
*
* @see https://developer.linkedin.com/docs/oauth2 [Authenticating with OAuth 2.0]
*/
class LinkedinProvider extends AbstractProvider implements ProviderInterface
{
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['r_liteprofile', 'r_emailaddress'];
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()
->post($this->getTokenUrl(), ['form_params' => $this->getTokenFields($code)]);
return $this->parseAccessToken($response->getBody());
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.linkedin.com/oauth/v2/accessToken';
}
/**
* Get the POST fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* {@inheritdoc}
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$basicProfile = $this->getBasicProfile($token);
$emailAddress = $this->getEmailAddress($token);
return array_merge($basicProfile, $emailAddress);
}
/**
* Get the basic profile fields for the user.
*
* @param string $token
*
* @return array
*/
protected function getBasicProfile($token)
{
$url = 'https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))';
$response = $this->getHttpClient()->get($url, [
'headers' => [
'Authorization' => 'Bearer '.$token,
'X-RestLi-Protocol-Version' => '2.0.0',
],
]);
return (array) json_decode($response->getBody(), true);
}
/**
* Get the email address for the user.
*
* @param string $token
*
* @return array
*/
protected function getEmailAddress($token)
{
$url = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))';
$response = $this->getHttpClient()->get($url, [
'headers' => [
'Authorization' => 'Bearer '.$token,
'X-RestLi-Protocol-Version' => '2.0.0',
],
]);
return (array) $this->arrayItem(json_decode($response->getBody(), true), 'elements.0.handle~');
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(?array $user)
{
$preferredLocale = $this->arrayItem($user, 'firstName.preferredLocale.language').'_'.$this->arrayItem($user, 'firstName.preferredLocale.country');
$firstName = $this->arrayItem($user, 'firstName.localized.'.$preferredLocale);
$lastName = $this->arrayItem($user, 'lastName.localized.'.$preferredLocale);
$name = $firstName.' '.$lastName;
$images = (array) $this->arrayItem($user, 'profilePicture.displayImage~.elements', []);
$avatars = array_filter($images, function ($image) {
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 100;
});
$avatar = array_shift($avatars);
$originalAvatars = array_filter($images, function ($image) {
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 800;
});
$originalAvatar = array_shift($originalAvatars);
return new User([
'id' => $this->arrayItem($user, 'id'),
'nickname' => $name,
'name' => $name,
'email' => $this->arrayItem($user, 'emailAddress'),
'avatar' => $avatar ? $this->arrayItem($avatar, 'identifiers.0.identifier') : null,
'avatar_original' => $originalAvatar ? $this->arrayItem($originalAvatar, 'identifiers.0.identifier') : null,
]);
}
/**
* Set the user fields to request from LinkedIn.
*
* @param array $fields
*
* @return $this
*/
public function fields(?array $fields)
{
$this->fields = $fields;
return $this;
}
/**
* Determine if the provider is operating as stateless.
*
* @return bool
*/
protected function isStateless()
{
return true;
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class OutlookProvider.
*/
class OutlookProvider extends AbstractProvider implements ProviderInterface
{
/**
* {@inheritdoc}
*/
protected $scopes = ['User.Read'];
/**
* {@inheritdoc}
*/
protected $scopeSeparator = ' ';
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://login.microsoftonline.com/common/oauth2/v2.0/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get(
'https://graph.microsoft.com/v1.0/me',
['headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$token->getToken(),
],
]
);
return json_decode($response->getBody()->getContents(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'id'),
'nickname' => null,
'name' => $this->arrayItem($user, 'displayName'),
'email' => $this->arrayItem($user, 'userPrincipalName'),
'avatar' => null,
]);
}
/**
* {@inheritdoc}
*/
protected function getTokenFields($code)
{
return array_merge(parent::getTokenFields($code), [
'grant_type' => 'authorization_code',
]);
}
}

View File

@@ -0,0 +1,206 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class QQProvider.
*
* @see http://wiki.connect.qq.com/oauth2-0%E7%AE%80%E4%BB%8B [QQ - OAuth 2.0 登录QQ]
*/
class QQProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base url of QQ API.
*
* @var string
*/
protected $baseUrl = 'https://graph.qq.com';
/**
* User openid.
*
* @var string
*/
protected $openId;
/**
* get token(openid) with unionid.
*
* @var bool
*/
protected $withUnionId = false;
/**
* User unionid.
*
* @var string
*/
protected $unionId;
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['get_user_info'];
/**
* The uid of user authorized.
*
* @var int
*/
protected $uid;
/**
* Get the authentication URL for the provider.
*
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth2.0/authorize', $state);
}
/**
* Get the token URL for the provider.
*
* @return string
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/oauth2.0/token';
}
/**
* Get the Post fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
'query' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody()->getContents());
}
/**
* Get the access token from the token response body.
*
* @param string $body
*
* @return \Overtrue\Socialite\AccessToken
*/
public function parseAccessToken($body)
{
parse_str($body, $token);
return parent::parseAccessToken($token);
}
/**
* @return self
*/
public function withUnionId()
{
$this->withUnionId = true;
return $this;
}
/**
* Get the raw user for the given access token.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return array
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$url = $this->baseUrl.'/oauth2.0/me?access_token='.$token->getToken();
$this->withUnionId && $url .= '&unionid=1';
$response = $this->getHttpClient()->get($url);
$me = json_decode($this->removeCallback($response->getBody()->getContents()), true);
$this->openId = $me['openid'];
$this->unionId = isset($me['unionid']) ? $me['unionid'] : '';
$queries = [
'access_token' => $token->getToken(),
'openid' => $this->openId,
'oauth_consumer_key' => $this->getConfig()->get('client_id'),
];
$response = $this->getHttpClient()->get($this->baseUrl.'/user/get_user_info?'.http_build_query($queries));
return json_decode($this->removeCallback($response->getBody()->getContents()), true);
}
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->openId,
'unionid' => $this->unionId,
'nickname' => $this->arrayItem($user, 'nickname'),
'name' => $this->arrayItem($user, 'nickname'),
'email' => $this->arrayItem($user, 'email'),
'avatar' => $this->arrayItem($user, 'figureurl_qq_2'),
]);
}
/**
* Remove the fucking callback parentheses.
*
* @param string $response
*
* @return string
*/
protected function removeCallback($response)
{
if (false !== strpos($response, 'callback')) {
$lpos = strpos($response, '(');
$rpos = strrpos($response, ')');
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
}
return $response;
}
}

View File

@@ -0,0 +1,242 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class TaobaoProvider.
*
* @author mechono <haodouliu@gmail.com>
*
* @see https://open.taobao.com/doc.htm?docId=102635&docType=1&source=search [Taobao - OAuth 2.0 授权登录]
*/
class TaobaoProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base url of Taobao API.
*
* @var string
*/
protected $baseUrl = 'https://oauth.taobao.com';
/**
* Taobao API service URL address.
*
* @var string
*/
protected $gatewayUrl = 'https://eco.taobao.com/router/rest';
/**
* The API version for the request.
*
* @var string
*/
protected $version = '2.0';
/**
* @var string
*/
protected $format = 'json';
/**
* @var string
*/
protected $signMethod = 'md5';
/**
* Web 对应 PC 端(淘宝 logo 浏览器页面样式Tmall 对应天猫的浏览器页面样式Wap 对应无线端的浏览器页面样式。
*/
protected $view = 'web';
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['user_info'];
/**
* Get the authentication URL for the provider.
*
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/authorize', $state);
}
/**
* 获取授权码接口参数.
*
* @param string|null $state
*
* @return array
*/
public function getCodeFields($state = null)
{
$fields = [
'client_id' => $this->getConfig()->get('client_id'),
'redirect_uri' => $this->redirectUrl,
'view' => $this->view,
'response_type' => 'code',
];
if ($this->usesState()) {
$fields['state'] = $state;
}
return $fields;
}
/**
* Get the token URL for the provider.
*
* @return string
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/token';
}
/**
* Get the Post fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code', 'view' => $this->view];
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
'query' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody()->getContents());
}
/**
* Get the access token from the token response body.
*
* @param string $body
*
* @return \Overtrue\Socialite\AccessToken
*/
public function parseAccessToken($body)
{
return parent::parseAccessToken($body);
}
/**
* Get the raw user for the given access token.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return array
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->post($this->getUserInfoUrl($this->gatewayUrl, $token));
return json_decode($response->getBody(), true);
}
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'open_id'),
'nickname' => $this->arrayItem($user, 'nick'),
'name' => $this->arrayItem($user, 'nick'),
'avatar' => $this->arrayItem($user, 'avatar'),
]);
}
/**
* @param $params
*
* @return string
*/
protected function generateSign($params)
{
ksort($params);
$stringToBeSigned = $this->getConfig()->get('client_secret');
foreach ($params as $k => $v) {
if (!is_array($v) && '@' != substr($v, 0, 1)) {
$stringToBeSigned .= "$k$v";
}
}
$stringToBeSigned .= $this->getConfig()->get('client_secret');
return strtoupper(md5($stringToBeSigned));
}
/**
* @param \Overtrue\Socialite\AccessTokenInterface $token
* @param array $apiFields
*
* @return array
*/
protected function getPublicFields(AccessTokenInterface $token, ?array $apiFields = [])
{
$fields = [
'app_key' => $this->getConfig()->get('client_id'),
'sign_method' => $this->signMethod,
'session' => $token->getToken(),
'timestamp' => date('Y-m-d H:i:s'),
'v' => $this->version,
'format' => $this->format,
];
$fields = array_merge($apiFields, $fields);
$fields['sign'] = $this->generateSign($fields);
return $fields;
}
/**
* {@inheritdoc}.
*/
protected function getUserInfoUrl($url, AccessTokenInterface $token)
{
$apiFields = ['method' => 'taobao.miniapp.userInfo.get'];
$query = http_build_query($this->getPublicFields($token, $apiFields), '', '&', $this->encodingType);
return $url.'?'.$query;
}
}

View File

@@ -0,0 +1,234 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\InvalidArgumentException;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
use Overtrue\Socialite\WeChatComponentInterface;
/**
* Class WeChatProvider.
*
* @see http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html [WeChat - 公众平台OAuth文档]
* @see https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN [网站应用微信登录开发指南]
*/
class WeChatProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base url of WeChat API.
*
* @var string
*/
protected $baseUrl = 'https://api.weixin.qq.com/sns';
/**
* {@inheritdoc}.
*/
protected $openId;
/**
* {@inheritdoc}.
*/
protected $scopes = ['snsapi_login'];
/**
* Indicates if the session state should be utilized.
*
* @var bool
*/
protected $stateless = true;
/**
* Return country code instead of country name.
*
* @var bool
*/
protected $withCountryCode = false;
/**
* @var WeChatComponentInterface
*/
protected $component;
/**
* Return country code instead of country name.
*
* @return $this
*/
public function withCountryCode()
{
$this->withCountryCode = true;
return $this;
}
/**
* WeChat OpenPlatform 3rd component.
*
* @param WeChatComponentInterface $component
*
* @return $this
*/
public function component(WeChatComponentInterface $component)
{
$this->scopes = ['snsapi_base'];
$this->component = $component;
return $this;
}
/**
* {@inheritdoc}.
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->get($this->getTokenUrl(), [
'headers' => ['Accept' => 'application/json'],
'query' => $this->getTokenFields($code),
]);
return $this->parseAccessToken($response->getBody());
}
/**
* {@inheritdoc}.
*/
protected function getAuthUrl($state)
{
$path = 'oauth2/authorize';
if (in_array('snsapi_login', $this->scopes)) {
$path = 'qrconnect';
}
return $this->buildAuthUrlFromBase("https://open.weixin.qq.com/connect/{$path}", $state);
}
/**
* {@inheritdoc}.
*/
protected function buildAuthUrlFromBase($url, $state)
{
$query = http_build_query($this->getCodeFields($state), '', '&', $this->encodingType);
return $url.'?'.$query.'#wechat_redirect';
}
/**
* {@inheritdoc}.
*/
protected function getCodeFields($state = null)
{
if ($this->component) {
$this->with(array_merge($this->parameters, ['component_appid' => $this->component->getAppId()]));
}
return array_merge([
'appid' => $this->getConfig()->get('client_id'),
'redirect_uri' => $this->redirectUrl,
'response_type' => 'code',
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
'state' => $state ?: md5(time()),
'connect_redirect' => 1,
], $this->parameters);
}
/**
* {@inheritdoc}.
*/
protected function getTokenUrl()
{
if ($this->component) {
return $this->baseUrl.'/oauth2/component/access_token';
}
return $this->baseUrl.'/oauth2/access_token';
}
/**
* {@inheritdoc}.
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$scopes = explode(',', $token->getAttribute('scope', ''));
if (in_array('snsapi_base', $scopes)) {
return $token->toArray();
}
if (empty($token['openid'])) {
throw new InvalidArgumentException('openid of AccessToken is required.');
}
$language = $this->withCountryCode ? null : (isset($this->parameters['lang']) ? $this->parameters['lang'] : 'zh_CN');
$response = $this->getHttpClient()->get($this->baseUrl.'/userinfo', [
'query' => array_filter([
'access_token' => $token->getToken(),
'openid' => $token['openid'],
'lang' => $language,
]),
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}.
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'openid'),
'name' => $this->arrayItem($user, 'nickname'),
'nickname' => $this->arrayItem($user, 'nickname'),
'avatar' => $this->arrayItem($user, 'headimgurl'),
'email' => null,
]);
}
/**
* {@inheritdoc}.
*/
protected function getTokenFields($code)
{
return array_filter([
'appid' => $this->getConfig()->get('client_id'),
'secret' => $this->getConfig()->get('client_secret'),
'component_appid' => $this->component ? $this->component->getAppId() : null,
'component_access_token' => $this->component ? $this->component->getToken() : null,
'code' => $code,
'grant_type' => 'authorization_code',
]);
}
/**
* Remove the fucking callback parentheses.
*
* @param mixed $response
*
* @return string
*/
protected function removeCallback($response)
{
if (false !== strpos($response, 'callback')) {
$lpos = strpos($response, '(');
$rpos = strrpos($response, ')');
$response = substr($response, $lpos + 1, $rpos - $lpos - 1);
}
return $response;
}
}

View File

@@ -0,0 +1,214 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class WeWorkProvider.
*
* @author mingyoung <mingyoungcheung@gmail.com>
*/
class WeWorkProvider extends AbstractProvider implements ProviderInterface
{
/**
* @var string
*/
protected $agentId;
/**
* @var bool
*/
protected $detailed = false;
/**
* Set agent id.
*
* @param string $agentId
*
* @return $this
*/
public function setAgentId($agentId)
{
$this->agentId = $agentId;
return $this;
}
/**
* @param string $agentId
*
* @return $this
*/
public function agent($agentId)
{
return $this->setAgentId($agentId);
}
/**
* Return user details.
*
* @return $this
*/
public function detailed()
{
$this->detailed = true;
return $this;
}
/**
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
// 网页授权登录
if (!empty($this->scopes)) {
return $this->getOAuthUrl($state);
}
// 第三方网页应用登录(扫码登录)
return $this->getQrConnectUrl($state);
}
/**
* OAuth url.
*
* @param string $state
*
* @return string
*/
protected function getOAuthUrl($state)
{
$queries = [
'appid' => $this->getConfig()->get('client_id'),
'redirect_uri' => $this->redirectUrl,
'response_type' => 'code',
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
'agentid' => $this->agentId,
'state' => $state,
];
return sprintf('https://open.weixin.qq.com/connect/oauth2/authorize?%s#wechat_redirect', http_build_query($queries));
}
/**
* Qr connect url.
*
* @param string $state
*
* @return string
*/
protected function getQrConnectUrl($state)
{
$queries = [
'appid' => $this->getConfig()->get('client_id'),
'agentid' => $this->agentId,
'redirect_uri' => $this->redirectUrl,
'state' => $state,
];
return 'https://open.work.weixin.qq.com/wwopen/sso/qrConnect?'.http_build_query($queries);
}
protected function getTokenUrl()
{
return null;
}
/**
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return mixed
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$userInfo = $this->getUserInfo($token);
if ($this->detailed && isset($userInfo['user_ticket'])) {
return $this->getUserDetail($token, $userInfo['user_ticket']);
}
$this->detailed = false;
return $userInfo;
}
/**
* Get user base info.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return mixed
*/
protected function getUserInfo(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get('https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo', [
'query' => array_filter([
'access_token' => $token->getToken(),
'code' => $this->getCode(),
]),
]);
return json_decode($response->getBody(), true);
}
/**
* Get user detail info.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
* @param $ticket
*
* @return mixed
*/
protected function getUserDetail(AccessTokenInterface $token, $ticket)
{
$response = $this->getHttpClient()->post('https://qyapi.weixin.qq.com/cgi-bin/user/getuserdetail', [
'query' => [
'access_token' => $token->getToken(),
],
'json' => [
'user_ticket' => $ticket,
],
]);
return json_decode($response->getBody(), true);
}
/**
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
protected function mapUserToObject(?array $user)
{
if ($this->detailed) {
return new User([
'id' => $this->arrayItem($user, 'userid'),
'name' => $this->arrayItem($user, 'name'),
'avatar' => $this->arrayItem($user, 'avatar'),
'email' => $this->arrayItem($user, 'email'),
]);
}
return new User(array_filter([
'id' => $this->arrayItem($user, 'UserId') ?: $this->arrayItem($user, 'OpenId'),
'userId' => $this->arrayItem($user, 'UserId'),
'openid' => $this->arrayItem($user, 'OpenId'),
'deviceId' => $this->arrayItem($user, 'DeviceId'),
]));
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite\Providers;
use Overtrue\Socialite\AccessTokenInterface;
use Overtrue\Socialite\ProviderInterface;
use Overtrue\Socialite\User;
/**
* Class WeiboProvider.
*
* @see http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E [OAuth 2.0 授权机制说明]
*/
class WeiboProvider extends AbstractProvider implements ProviderInterface
{
/**
* The base url of Weibo API.
*
* @var string
*/
protected $baseUrl = 'https://api.weibo.com';
/**
* The API version for the request.
*
* @var string
*/
protected $version = '2';
/**
* The scopes being requested.
*
* @var array
*/
protected $scopes = ['email'];
/**
* The uid of user authorized.
*
* @var int
*/
protected $uid;
/**
* Get the authentication URL for the provider.
*
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl.'/oauth2/authorize', $state);
}
/**
* Get the token URL for the provider.
*
* @return string
*/
protected function getTokenUrl()
{
return $this->baseUrl.'/'.$this->version.'/oauth2/access_token';
}
/**
* Get the Post fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
}
/**
* Get the raw user for the given access token.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return array
*/
protected function getUserByToken(AccessTokenInterface $token)
{
$response = $this->getHttpClient()->get($this->baseUrl.'/'.$this->version.'/users/show.json', [
'query' => [
'uid' => $token['uid'],
'access_token' => $token->getToken(),
],
'headers' => [
'Accept' => 'application/json',
],
]);
return json_decode($response->getBody(), true);
}
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
*
* @return \Overtrue\Socialite\User
*/
protected function mapUserToObject(?array $user)
{
return new User([
'id' => $this->arrayItem($user, 'id'),
'nickname' => $this->arrayItem($user, 'screen_name'),
'name' => $this->arrayItem($user, 'name'),
'email' => $this->arrayItem($user, 'email'),
'avatar' => $this->arrayItem($user, 'avatar_large'),
]);
}
}

View File

@@ -0,0 +1,251 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
use Closure;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* Class SocialiteManager.
*/
class SocialiteManager implements FactoryInterface
{
/**
* The configuration.
*
* @var \Overtrue\Socialite\Config
*/
protected $config;
/**
* The request instance.
*
* @var Request
*/
protected $request;
/**
* The registered custom driver creators.
*
* @var array
*/
protected $customCreators = [];
/**
* The initial drivers.
*
* @var array
*/
protected $initialDrivers = [
'facebook' => 'Facebook',
'github' => 'GitHub',
'google' => 'Google',
'linkedin' => 'Linkedin',
'weibo' => 'Weibo',
'qq' => 'QQ',
'wechat' => 'WeChat',
'douban' => 'Douban',
'wework' => 'WeWork',
'outlook' => 'Outlook',
'douyin' => 'DouYin',
'taobao' => 'Taobao',
'feishu' => 'FeiShu',
];
/**
* The array of created "drivers".
*
* @var ProviderInterface[]
*/
protected $drivers = [];
/**
* SocialiteManager constructor.
*
* @param array $config
* @param Request|null $request
*/
public function __construct(?array $config, Request $request = null)
{
$this->config = new Config($config);
if ($this->config->has('guzzle')) {
Providers\AbstractProvider::setGuzzleOptions($this->config->get('guzzle'));
}
if ($request) {
$this->setRequest($request);
}
}
/**
* Set config instance.
*
* @param \Overtrue\Socialite\Config $config
*
* @return $this
*/
public function config(Config $config)
{
$this->config = $config;
return $this;
}
/**
* Get a driver instance.
*
* @param string $driver
*
* @return ProviderInterface
*/
public function driver($driver)
{
$driver = strtolower($driver);
if (!isset($this->drivers[$driver])) {
$this->drivers[$driver] = $this->createDriver($driver);
}
return $this->drivers[$driver];
}
/**
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* @return \Symfony\Component\HttpFoundation\Request
*/
public function getRequest()
{
return $this->request ?: $this->createDefaultRequest();
}
/**
* Create a new driver instance.
*
* @param string $driver
*
* @throws \InvalidArgumentException
*
* @return ProviderInterface
*/
protected function createDriver($driver)
{
if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
}
if (isset($this->initialDrivers[$driver])) {
$provider = $this->initialDrivers[$driver];
$provider = __NAMESPACE__.'\\Providers\\'.$provider.'Provider';
return $this->buildProvider($provider, $this->formatConfig($this->config->get($driver)));
}
throw new InvalidArgumentException("Driver [$driver] not supported.");
}
/**
* Call a custom driver creator.
*
* @param string $driver
*
* @return ProviderInterface
*/
protected function callCustomCreator($driver)
{
return $this->customCreators[$driver]($this->config);
}
/**
* Create default request instance.
*
* @return Request
*/
protected function createDefaultRequest()
{
$request = Request::createFromGlobals();
$session = new Session();
$request->setSession($session);
return $request;
}
/**
* Register a custom driver creator Closure.
*
* @param string $driver
* @param \Closure $callback
*
* @return $this
*/
public function extend($driver, Closure $callback)
{
$driver = strtolower($driver);
$this->customCreators[$driver] = $callback;
return $this;
}
/**
* Get all of the created "drivers".
*
* @return ProviderInterface[]
*/
public function getDrivers()
{
return $this->drivers;
}
/**
* Build an OAuth 2 provider instance.
*
* @param string $provider
* @param array $config
*
* @return ProviderInterface
*/
public function buildProvider($provider, $config)
{
return new $provider($this->getRequest(), $config);
}
/**
* Format the server configuration.
*
* @param array $config
*
* @return array
*/
public function formatConfig(?array $config)
{
return array_merge([
'identifier' => $config['client_id'],
'secret' => $config['client_secret'],
'callback_uri' => $config['redirect'],
], $config);
}
}

204
vendor/overtrue/socialite/src/User.php vendored Normal file
View File

@@ -0,0 +1,204 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
use ArrayAccess;
use JsonSerializable;
/**
* Class User.
*/
class User implements ArrayAccess, UserInterface, JsonSerializable, \Serializable
{
use HasAttributes;
/**
* User constructor.
*
* @param array $attributes
*/
public function __construct(?array $attributes)
{
$this->attributes = $attributes;
}
/**
* Get the unique identifier for the user.
*
* @return string
*/
public function getId()
{
return $this->getAttribute('id');
}
/**
* Get the username for the user.
*
* @return string
*/
public function getUsername()
{
return $this->getAttribute('username', $this->getId());
}
/**
* Get the nickname / username for the user.
*
* @return string
*/
public function getNickname()
{
return $this->getAttribute('nickname');
}
/**
* Get the full name of the user.
*
* @return string
*/
public function getName()
{
return $this->getAttribute('name');
}
/**
* Get the e-mail address of the user.
*
* @return string
*/
public function getEmail()
{
return $this->getAttribute('email');
}
/**
* Get the avatar / image URL for the user.
*
* @return string
*/
public function getAvatar()
{
return $this->getAttribute('avatar');
}
/**
* Set the token on the user.
*
* @param \Overtrue\Socialite\AccessTokenInterface $token
*
* @return $this
*/
public function setToken(AccessTokenInterface $token)
{
$this->setAttribute('token', $token->getToken());
$this->setAttribute('access_token', $token->getToken());
if (\is_callable([$token, 'getRefreshToken'])) {
$this->setAttribute('refresh_token', $token->getRefreshToken());
}
return $this;
}
/**
* @param string $provider
*
* @return $this
*/
public function setProviderName($provider)
{
$this->setAttribute('provider', $provider);
return $this;
}
/**
* @return string
*/
public function getProviderName()
{
return $this->getAttribute('provider');
}
/**
* Get the authorized token.
*
* @return \Overtrue\Socialite\AccessToken
*/
public function getToken()
{
return new AccessToken([
'access_token' => $this->getAccessToken(),
'refresh_token' => $this->getAttribute('refresh_token')
]);
}
/**
* Get user access token.
*
* @return string
*/
public function getAccessToken()
{
return $this->getAttribute('token') ?: $this->getAttribute('access_token');
}
/**
* Get user refresh token.
*
* @return string
*/
public function getRefreshToken()
{
return $this->getAttribute('refresh_token');
}
/**
* Get the original attributes.
*
* @return array
*/
public function getOriginal()
{
return $this->getAttribute('original');
}
/**
* {@inheritdoc}
*/
public function jsonSerialize()
{
return $this->attributes;
}
public function serialize()
{
return serialize($this->attributes);
}
/**
* Constructs the object.
*
* @see https://php.net/manual/en/serializable.unserialize.php
*
* @param string $serialized <p>
* The string representation of the object.
* </p>
*
* @since 5.1.0
*/
public function unserialize($serialized)
{
$this->attributes = unserialize($serialized) ?: [];
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
/**
* Interface UserInterface.
*/
interface UserInterface
{
/**
* Get the unique identifier for the user.
*
* @return string
*/
public function getId();
/**
* Get the nickname / username for the user.
*
* @return string
*/
public function getNickname();
/**
* Get the full name of the user.
*
* @return string
*/
public function getName();
/**
* Get the e-mail address of the user.
*
* @return string
*/
public function getEmail();
/**
* Get the avatar / image URL for the user.
*
* @return string
*/
public function getAvatar();
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the overtrue/socialite.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Overtrue\Socialite;
/**
* Interface WeChatComponentInterface.
*/
interface WeChatComponentInterface
{
/**
* Return the open-platform component app id.
*
* @return string
*/
public function getAppId();
/**
* Return the open-platform component access token string.
*
* @return string
*/
public function getToken();
}