master 3.5.4
ifornew 1 year ago
commit d6cf36aa61

10
.gitignore vendored

@ -0,0 +1,10 @@
/vendor
composer.phar
composer.lock
.DS_Store
/.idea
Thumbs.db
/*.php
sftp-config.json
.php_cs.cache
.php-cs-fixer.cache

@ -0,0 +1 @@
{"version":1,"defects":{"OAuthTest::test_it_can_get_token":4,"OAuthTest::test_it_can_get_user_by_code":4,"WechatTest::testWeChatProviderHasCorrectlyRedirectResponse":6},"times":{"OAuthTest::test_it_can_get_auth_url_without_redirect":0.001,"OAuthTest::test_it_can_get_auth_url_with_redirect":0,"OAuthTest::test_it_can_get_auth_url_with_scopes":0,"OAuthTest::test_it_can_get_auth_url_with_state":0,"OAuthTest::test_it_can_get_token":0.004,"OAuthTest::test_it_can_get_user_by_token":0,"OAuthTest::test_it_can_get_user_by_code":0,"Providers\\FeiShuTest::testProviderCanCreateCorrect":0,"Providers\\FeiShuTest::testProviderWithInternalAppModeWork":0,"Providers\\FeiShuTest::testProviderWithAppTicketWork":0,"Providers\\FeiShuTest::testConfigAppAccessTokenWithDefaultModeNoAppTicketWork":0.002,"Providers\\FeiShuTest::testConfigAppAccessTokenWithDefaultModeAndAppTicketWorkInBadResponse":0.001,"Providers\\FeiShuTest::testConfigAppAccessTokenWithDefaultModeAndAppTicketWorkInGoodResponse":0,"Providers\\FeiShuTest::testConfigAppAccessTokenWithInternalInBadResponse":0,"Providers\\FeiShuTest::testConfigAppAccessTokenWithInternalInGoodResponse":0,"WeWorkTest::testQrConnect":0,"WeWorkTest::testOAuthWithAgentId":0,"WeWorkTest::testOAuthWithoutAgentId":0,"WechatTest::testWeChatProviderHasCorrectlyRedirectResponse":0,"WechatTest::testWeChatProviderTokenUrlAndRequestFields":0,"WechatTest::testOpenPlatformComponent":0,"WechatTest::testOpenPlatformComponentWithCustomParameters":0,"SocialiteManagerTest::test_it_can_create_from_config":0.001,"SocialiteManagerTest::test_it_can_create_from_custom_creator":0,"SocialiteManagerTest::test_it_can_create_from_custom_provider_class":0,"UserTest::testJsonserialize":0,"UserTest::test_it_can_get_refresh_token":0,"UserTest::test_it_can_set_raw_data":0,"UserTest::test_ie_can_get_attribute_by_magic_method":0}}

@ -0,0 +1,13 @@
language: php
php:
- 7.0
- 7.1
- 7.2
sudo: false
dist: trusty
install: travis_retry composer install --no-interaction --prefer-source
script: vendor/bin/phpunit --verbose

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) overtrue <i@overtrue.me>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,599 @@
<h1 align="center"> Socialite</h1>
<p align="center">
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/unstable.svg" alt="Latest Unstable Version"></a>
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/build-status/master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/build.png?b=master" alt="Build Status"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/downloads" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/license" alt="License"></a>
</p>
<p align="center">Socialite is an <a href="https://oauth.net/2/">OAuth2</a> Authentication tool. It is inspired by <a href="https://github.com/laravel/socialite">laravel/socialite</a>, You can easily use it in any PHP project. <a href="/README_CN.md">中文文档</a></p>
<p align="center">This tool now supports platforms such as Facebook, GitHub, Google, LinkedIn, Outlook, QQ, Tapd, Alipay, Taobao, Baidu, DingTalk, Weibo, WeChat, Douyin, Feishu, Douban, WeWork, Tencent Cloud, Line, Gitee.</p>
[![Sponsor me](https://raw.githubusercontent.com/overtrue/overtrue/master/sponsor-me-button-s.svg)](https://github.com/sponsors/overtrue)
如果你喜欢我的项目并想支持它,[点击这里 :heart:](https://github.com/sponsors/overtrue)
- [Requirement](#requirement)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Custom app name](#custom-app-name)
- [Extends custom provider](#extends-custom-provider)
- [Platform](#platform)
- [Alipay](#alipay)
- [DingTalk](#dingtalk)
- [Douyin](#douyin)
- [Baidu](#baidu)
- [Feishu](#feishu)
- [Taobao](#taobao)
- [WeChat](#wechat)
- [Some Skill](#some-skill)
- [Scopes](#scopes)
- [Redirect URL](#redirect-url)
- [State](#state)
- [Redirect with `state` parameter](#redirect-with-state-parameter)
- [Validate the callback `state`](#validate-the-callback-state)
- [Additional parameters](#additional-parameters)
- [User interface](#user-interface)
- [Standard user api:](#standard-user-api)
- [Get raw response from OAuth API](#get-raw-response-from-oauth-api)
- [Get the token response when you use userFromCode()](#get-the-token-response-when-you-use-userfromcode)
- [Get user with access token](#get-user-with-access-token)
- [Enjoy it! :heart:](#enjoy-it-heart)
- [Reference](#reference)
- [PHP 扩展包开发](#php-扩展包开发)
- [License](#license)
# Requirement
```
PHP >= 7.4
```
# Installation
```shell
$ composer require "overtrue/socialite" -vvv
```
# Usage
Users just need to create the corresponding configuration variables, then create the authentication application for each platform through the tool, and easily obtain the access_token and user information for that platform. The implementation logic of the tool is referred to OAuth2 documents of major platforms for details.
The tool is used in the following steps:
1. Configurate platform config
2. Use this tool to create a platform application
3. Let the user redirect to platform authentication
4. The server receives a Code callback from the platform, and uses the Code to exchange the user information on the platform (including access_token).
Packages created for Laravel users are easier to integrate: [overtrue/laravel-socialite](https://github.com/overtrue/laravel-socialite)
`authorize.php`:
```php
<?php
use Ifornew\Socialite\SocialiteManager;
$config = [
'github' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$url = $socialite->create('github')->redirect();
return redirect($url);
```
`callback.php`:
```php
<?php
use Ifornew\Socialite\SocialiteManager;
$config = [
'github' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$code = request()->query('code');
$user = $socialite->create('github')->userFromCode($code);
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
$user->getEmail(); // "anzhengchao@gmail.com"
...
```
## Configuration
Each create uses the same configuration keys: `client_id`, `client_secret`, `redirect`.
Example:
```php
$config = [
'weibo' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
'facebook' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
```
### Custom app name
You can use any name you like as the name of the application, such as `foo`, and set provider using `provider` key
```php
$config = [
'foo' => [
'provider' => 'github', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
// another github app
'bar' => [
'provider' => 'github', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
//...
];
```
### Extends custom provider
You can create application from you custom provider easilyyou have to ways to do this:
1. Using custom creator:
As shown in the following code, the service provider name is defined for the Foo application, but the tool itself does not support it, so use the creator `extend()` to create an instance of the service provider as a closure function.
```php
$config = [
'foo' => [
'provider' => 'myprovider', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$socialite->extend('myprovider', function(array $config) {
return new MyCustomProvider($config);
});
$app = $socialite->create('foo');
```
2. Using provider:
>👋🏻 Your custom provider class must be implements of `Ifornew\Socialite\Contracts\ProviderInterface`.
```php
class MyCustomProvider implements \Ifornew\Socialite\Contracts\ProviderInterface
{
//...
}
```
then set `provider` with the class name:
```php
$config = [
'foo' => [
'provider' => MyCustomProvider::class, // <-- class name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$app = $socialite->create('foo');
```
## Platform
Different platforms have different configuration methods, so please check the platform Settings you are using.
### [Alipay](https://opendocs.alipay.com/open/200/105310#s2)
You must have the following configuration.
```php
$config = [
'alipay' => [
// This can also be named as 'app_id' like the official documentation.
'client_id' => 'your-app-id',
// Please refer to the official documentation, in the official management background configuration RSA2.
// Note: This is your own private key.
// Note: Do not allow the private key content to have extra characters.
// Recommendation: For security, you can read directly from the file. But here as long as the value, please remember to remove the head and tail of the decoration.
'rsa_private_key' => 'your-rsa-private-key',
// Be sure to set this value and make sure that it is the same address value as set in the official admin system.
// This can also be named as 'redirect_url' like the official documentation.
'redirect' => 'http://localhost/socialite/callback.php',
]
...
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('alipay')->userFromCode('here is auth code');
// See this documents "User interface"
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
...
```
Only RSA2 personal private keys are supported, so stay tuned if you want to log in with a certificate.
### [DingTalk](https://ding-doc.dingtalk.com/doc#/serverapi3/mrugr3)
Follow the documentation and configure it like following.
> Note: It only supported QR code access to third-part websites. i.e exchange for user information(opendid, unionid and nickname)
```php
$config = [
'dingtalk' => [
// or 'app_id'
'client_id' => 'your app id',
// or 'app_secret'
'client_secret' => 'your app secret',
// or 'redirect_url'
'redirect' => 'redirect URL'
]
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('dingtalk')->userFromCode('here is auth code');
// See this documents "User interface"
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
...
```
### [Douyin](https://open.douyin.com/platform/doc/OpenAPI-oauth2)
> Note using the Douyin create that if you get user information directly using access token, set up the openid first. the openid can be obtained by code when access is obtained, so call `userFromCode()` automatically configured for you openid, if call `userFromToken()` first call `withOpenId()`
```php
$config = [
'douyin' => [
'client_id' => 'your app id',
'client_secret' => 'your app secret',
'redirect' => 'redirect URL'
]
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('douyin')->userFromCode('here is auth code');
$user = $socialite->create('douyin')->withOpenId('openId')->userFromToken('here is the access token');
```
### [Baidu](https://developer.baidu.com/wiki/index.php?title=docs/oauth)
You can choose the form you want display by using `withDisplay()`.
- **page**
- **popup**
- **dialog**
- **mobile**
- **tv**
- **pad**
```php
$authUrl = $socialite->create('baidu')->withDisplay('mobile')->redirect();
```
`popup` mode is the default setting with display. `basic` is the default with scopes.
### [Feishu](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)
Some simple way to use by internal app mode and config app_ticket.
```php
$config = [
'feishu' => [
// or 'app_id'
'client_id' => 'your app id',
// or 'app_secret'
'client_secret' => 'your app secret',
// or 'redirect_url'
'redirect' => 'redirect URL',
// if you want to use internal way to get app_access_token
// set this key by 'internal' then you already turn on the internal app mode
'app_mode' => 'internal'
]
];
$socialite = new SocialiteManager($config);
$feishuDriver = $socialite->create('feishu');
$feishuDriver->withInternalAppMode()->userFromCode('here is code');
$feishuDriver->withDefaultMode()->withAppTicket('app_ticket')->userFromCode('here is code');
```
### [Taobao](https://open.taobao.com/doc.htm?docId=102635&docType=1&source=search)
You can choose the form you want display by using `withView()`.
```php
$authUrl = $socialite->create('taobao')->withView('wap')->redirect();
```
`web` mode is the default setting with display. `user_info` is the default with scopes.
### [WeChat](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html)
We support Open Platform Third-party Platform webpage authorizations on behalf of Official Account.
You just need input your config like below config. Official Accounts authorizations only doesn't need.
```php
...
[
'wechat' =>
[
'client_id' => 'client_id',
'client_secret' => 'client_secret',
'redirect' => 'redirect-url',
// Open Platform - Third-party Platform Need
'component' => [
'id' => 'component-app-id',
'token' => 'component-access-token', // or Using a callable as value.
]
]
],
...
```
## Some Skill
### Scopes
Before redirecting the user, you may also set "scopes" on the request using the `scopes()` method. This method will overwrite all existing scopes:
```php
$response = $socialite->create('github')
->scopes(['scope1', 'scope2'])->redirect();
```
### Redirect URL
You may also want to dynamically set `redirect_uri`you can use the following methods to change the `redirect_uri` URL:
```php
$url = 'your callback url.';
$socialite->redirect($url);
// or
$socialite->withRedirectUrl($url)->redirect();
```
### State
Your app can use a state parameter for making sure the response belongs to a request initiated by the same user, therefore preventing cross-site request forgery (CSFR) attacks. A CSFR attack occurs when a malicious attacker tricks the user into performing unwanted actions that only the user is authorized to perform on a trusted web application, and all will be done without involving or alerting the user.
Here's the simplest example of how providing the state can make your app more secure. in this example, we use the session ID as the state parameter, but you can use whatever logic you want to create value for the state.
### Redirect with `state` parameter
```php
<?php
session_start();
$config = [
//...
];
// Assign to state the hashing of the session ID
$state = hash('sha256', session_id());
$socialite = new SocialiteManager($config);
$url = $socialite->create('github')->withState($state)->redirect();
return redirect($url);
```
### Validate the callback `state`
Once the user has authorized your app, the user will be redirected back to your app's redirect_uri. The OAuth server will return the state parameter unchanged. Check if the state provided in the redirect_uri matches the state generated by your app:
```php
<?php
session_start();
$state = request()->query('state');
$code = request()->query('code');
// Check the state received with current session id
if ($state != hash('sha256', session_id())) {
exit('State does not match!');
}
$user = $socialite->create('github')->userFromCode($code);
// authorized
```
[Read more about `state` parameter](https://auth0.com/docs/protocols/oauth2/oauth-state)
### Additional parameters
To include any optional parameters in the request, call the `with()` method with an associative array:
```php
$response = $socialite->create('google')
->with(['hd' => 'example.com'])->redirect();
```
## User interface
### Standard user api:
```php
$user = $socialite->create('github')->userFromCode($code);
```
```json
{
"id": 1472352,
"nickname": "overtrue",
"name": "安正超",
"email": "anzhengchao@gmail.com",
"avatar": "https://avatars.githubusercontent.com/u/1472352?v=3",
"raw": {
"login": "overtrue",
"id": 1472352,
"avatar_url": "https://avatars.githubusercontent.com/u/1472352?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/overtrue",
"html_url": "https://github.com/overtrue",
...
},
"token_response": {
"access_token": "5b1dc56d64fffbd052359f032716cc4e0a1cb9a0",
"token_type": "bearer",
"scope": "user:email"
}
}
```
You can fetch the user attribute as a array keys like these:
```php
$user['id']; // 1472352
$user['nickname']; // "overtrue"
$user['name']; // "安正超"
$user['email']; // "anzhengchao@gmail.com"
...
```
Or using the method:
```php
mixed $user->getId();
?string $user->getNickname();
?string $user->getName();
?string $user->getEmail();
?string $user->getAvatar();
?string $user->getRaw();
?string $user->getAccessToken();
?string $user->getRefreshToken();
?int $user->getExpiresIn();
?array $user->getTokenResponse();
```
### Get raw response from OAuth API
The `$user->getRaw()` method will return an **array** of the API raw response.
### Get the token response when you use userFromCode()
The `$user->getTokenResponse()` method will return an **array** of the get token(access token) API response.
> Note: This method only return a **valid array** when you use `userFromCode()`, else will return **null** because use `userFromToken()` have no token response.
### Get user with access token
```php
$accessToken = 'xxxxxxxxxxx';
$user = $socialite->userFromToken($accessToken);
```
# Enjoy it! :heart:
# Reference
- [Alipay - 用户信息授权](https://opendocs.alipay.com/open/289/105656)
- [DingTalk - 扫码登录第三方网站](https://ding-doc.dingtalk.com/doc#/serverapi3/mrugr3)
- [Google - OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
- [Github - Authorizing OAuth Apps](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/)
- [Facebook - Graph API](https://developers.facebook.com/docs/graph-api)
- [Linkedin - Authenticating with OAuth 2.0](https://developer.linkedin.com/docs/oauth2)
- [微博 - OAuth 2.0 授权机制说明](http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E)
- [QQ - OAuth 2.0 登录 QQ](http://wiki.connect.qq.com/oauth2-0%E7%AE%80%E4%BB%8B)
- [腾讯云 - OAuth2.0](https://cloud.tencent.com/document/product/306/37730#.E6.8E.A5.E5.85.A5.E8.85.BE.E8.AE.AF.E4.BA.91-oauth)
- [微信公众平台 - OAuth 文档](http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html)
- [微信开放平台 - 网站应用微信登录开发指南](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN)
- [微信开放平台 - 代公众号发起网页授权](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN)
- [企业微信 - OAuth 文档](https://open.work.weixin.qq.com/api/doc/90000/90135/91020)
- [企业微信第三方应用 - OAuth 文档](https://open.work.weixin.qq.com/api/doc/90001/90143/91118)
- [豆瓣 - OAuth 2.0 授权机制说明](http://developers.douban.com/wiki/?title=oauth2)
- [抖音 - 网站应用开发指南](http://open.douyin.com/platform/doc)
- [飞书 - 授权说明](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)
- [Tapd - 用户授权说明](https://www.tapd.cn/help/show#1120003271001000093)
- [Line - OAuth 2.0](https://developers.line.biz/en/docs/line-login/integrate-line-login/)
- [Gitee - OAuth文档](https://gitee.com/api/v5/oauth_doc#/)
[![Sponsor me](https://raw.githubusercontent.com/overtrue/overtrue/master/sponsor-me.svg)](https://github.com/sponsors/overtrue)
## Project supported by JetBrains
Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue)
# PHP 扩展包开发
> 想知道如何从零开始构建 PHP 扩展包?
>
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
# License
MIT

@ -0,0 +1,610 @@
<h1 align="center"> Socialite</h1>
<p align="center">
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/v/unstable.svg" alt="Latest Unstable Version"></a>
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/build-status/master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/build.png?b=master" alt="Build Status"></a>
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/?branch=master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/quality-score.png?b=master" alt="Scrutinizer Code Quality"></a>
<a href="https://scrutinizer-ci.com/g/overtrue/socialite/?branch=master"><img src="https://scrutinizer-ci.com/g/overtrue/socialite/badges/coverage.png?b=master" alt="Code Coverage"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/downloads" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/overtrue/socialite"><img src="https://poser.pugx.org/overtrue/socialite/license" alt="License"></a>
</p>
<p align="center">Socialite 是一个 <a href="https://oauth.net/2/">OAuth2</a> 认证工具。 它的灵感来源于 <a href="https://github.com/laravel/socialite">laravel/socialite</a> 你可以很轻易的在任何 PHP 项目中使用它。</p>
<p align="center">该工具现已支持平台有FacebookGithubGoogleLinkedinOutlookQQTAPD支付宝淘宝百度钉钉微博微信抖音飞书豆瓣企业微信腾讯云LineGitee。</p>
- [版本要求](#版本要求)
- [安装](#安装)
- [使用指南](#使用指南)
- [配置](#配置)
- [自定义应用名](#自定义应用名)
- [扩展自定义服务提供程序](#扩展自定义服务提供程序)
- [平台](#平台)
- [支付宝](#支付宝)
- [钉钉](#钉钉)
- [抖音](#抖音)
- [百度](#百度)
- [飞书](#飞书)
- [淘宝](#淘宝)
- [微信](#微信)
- [其他一些技巧](#其他一些技巧)
- [Scopes](#scopes)
- [Redirect URL](#redirect-url)
- [State](#state)
- [带着 `state` 参数的重定向](#带着-state-参数的重定向)
- [检验回调的 `state`](#检验回调的-state)
- [其他的一些参数](#其他的一些参数)
- [User interface](#user-interface)
- [标准的 user api](#标准的-user-api)
- [从 OAuth API 响应中取得原始数据](#从-oauth-api-响应中取得原始数据)
- [当你使用 userFromCode() 想要获取 token 响应的原始数据](#当你使用-userfromcode-想要获取-token-响应的原始数据)
- [通过 access token 获取用户信息](#通过-access-token-获取用户信息)
- [Enjoy it! :heart:](#enjoy-it-heart)
- [参照](#参照)
- [PHP 扩展包开发](#php-扩展包开发)
- [License](#license)
# 版本要求
```
PHP >= 7.4
```
# 安装
```shell
$ composer require "overtrue/socialite" -vvv
```
# 使用指南
用户只需要创建相应配置变量,然后通过工具为各个平台创建认证应用,并轻松获取该平台的 access_token 和用户相关信息。工具实现逻辑详见参照各大平台 OAuth2 文档。
工具使用大致分为以下几步:
1. 配置平台设置
2. 创建对应平台应用
3. 让用户跳转至平台认证
4. 服务器收到平台回调 Code使用 Code 换取平台处用户信息(包括 access_token
为 Laravel 用户创建的更方便的整合的包: [overtrue/laravel-socialite](https://github.com/overtrue/laravel-socialite)
`authorize.php`: 让用户跳转至平台认证
```php
<?php
use Ifornew\Socialite\SocialiteManager;
$config = [
'github' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$url = $socialite->create('github')->redirect();
return redirect($url);
```
`callback.php`:
```php
<?php
use Ifornew\Socialite\SocialiteManager;
$config = [
'github' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$code = request()->query('code');
$user = $socialite->create('github')->userFromCode($code);
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
$user->getEmail(); // "anzhengchao@gmail.com"
...
```
## 配置
为每个平台设置相同的键值对后就能开箱即用:`client_id`, `client_secret`, `redirect`.
示例:
```php
$config = [
'weibo' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
'facebook' => [
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
```
### 自定义应用名
你可以使用任意你喜欢的名字对每个平台进行命名,比如说 `foo` 采用别名的方法后需要在配置中多设置一个 `provider` 键,这样才能告诉工具包如何正确找到你想要的程序:
```php
$config = [
// 为 github 应用起别名为 foo
'foo' => [
'provider' => 'github', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
// 另外一个名字叫做 bar 的 github 应用
'bar' => [
'provider' => 'github', // <-- provider name
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
//...
];
$socialite = new SocialiteManager($config);
$appFoo = $socialite->create('foo');
$appBar = $socialite->create('bar');
```
### 扩展自定义服务提供程序
你可以很容易的从自定义的服务提供中创建应用,只需要遵循如下两点:
1. 使用自定义创建器
如下代码所示,为 foo 应用定义了服务提供名,但是工具本身还未支持,所以使用创建器 `extend()`,以闭包函数的形式为该服务提供创建一个实例。
```php
$config = [
'foo' => [
'provider' => 'myprovider', // <--
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$socialite->extend('myprovider', function(array $config) {
return new MyCustomProvider($config);
});
$app = $socialite->create('foo');
```
2. 使用服务提供类
>👋🏻 你的自定义服务提供类必须实现`Ifornew\Socialite\Contracts\ProviderInterface` 接口
```php
class MyCustomProvider implements \Ifornew\Socialite\Contracts\ProviderInterface
{
//...
}
```
接下来为 `provider` 设置该类名让工具可以找到该类并实例化:
```php
$config = [
'foo' => [
'provider' => MyCustomProvider::class, // <--
'client_id' => 'your-app-id',
'client_secret' => 'your-app-secret',
'redirect' => 'http://localhost/socialite/callback.php',
],
];
$socialite = new SocialiteManager($config);
$app = $socialite->create('foo');
```
## 平台
不同的平台有不同的配置方法,为了确保工具的正常运行,所以请确保你所使用的平台的配置都是如期设置的。
### [支付宝](https://opendocs.alipay.com/open/200/105310#s2)
请按如下方式配置
```php
$config = [
'alipay' => [
// 这个键名还能像官方文档那样叫做 'app_id'
'client_id' => 'your-app-id',
// 请根据官方文档,在官方管理后台配置 RSA2
// 注意: 这是你自己的私钥
// 注意: 不允许私钥内容有其他字符
// 建议: 为了保证安全,你可以将文本信息从磁盘文件中读取,而不是在这里明文
'rsa_private_key' => 'your-rsa-private-key',
// 确保这里的值与你在服务后台绑定的地址值一致
// 这个键名还能像官方文档那样叫做 'redirect_url'
'redirect' => 'http://localhost/socialite/callback.php',
// 沙箱模式接入地址见 https://opendocs.alipay.com/open/220/105337#%E5%85%B3%E4%BA%8E%E6%B2%99%E7%AE%B1
'sandbox' => false,
]
...
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('alipay')->userFromCode('here is auth code');
// 详见文档后面 "User interface"
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
...
```
本工具暂时只支持 RSA2 个人私钥认证方式。
### [钉钉](https://ding-doc.dingtalk.com/doc#/serverapi3/mrugr3)
如文档所示
> 注意:该工具仅支持 QR code 连接到第三方网站用来获取用户信息opeid unionid 和 nickname
```php
$config = [
'dingtalk' => [
// or 'app_id'
'client_id' => 'your app id',
// or 'app_secret'
'client_secret' => 'your app secret',
// or 'redirect_url'
'redirect' => 'redirect URL'
]
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('dingtalk')->userFromCode('here is auth code');
// 详见文档后面 "User interface"
$user->getId(); // 1472352
$user->getNickname(); // "overtrue"
$user->getUsername(); // "overtrue"
$user->getName(); // "安正超"
...
```
### [抖音](https://open.douyin.com/platform/doc/OpenAPI-oauth2)
> 注意: 使用抖音服务提供的时候,如果你想直接使用 access_token 获取用户信息时,请先设置 openid。 先调用 `withOpenId()` 再调用 `userFromToken()`
```php
$config = [
'douyin' => [
'client_id' => 'your app id',
'client_secret' => 'your app secret',
'redirect' => 'redirect URL'
]
];
$socialite = new SocialiteManager($config);
$user = $socialite->create('douyin')->userFromCode('here is auth code');
$user = $socialite->create('douyin')->withOpenId('openId')->userFromToken('here is the access token');
```
### [百度](https://developer.baidu.com/wiki/index.php?title=docs/oauth)
其他配置没啥区别,在用法上,可以很轻易的选择重定向登录页面的模式,通过 `withDisplay()`
- **page**全屏形式的授权页面 (默认),适用于 web 应用。
- **popup:** 弹框形式的授权页面,适用于桌面软件应用和 web 应用。
- **dialog:** 浮层形式的授权页面,只能用于站内 web 应用。
- **mobile:** Iphone/Android 等智能移动终端上用的授权页面,适用于 Iphone/Android 等智能移动终端上的应用。
- **tv:** 电视等超大显示屏使用的授权页面。
- **pad:** IPad/Android 等智能平板电脑使用的授权页面。
```php
$authUrl = $socialite->create('baidu')->withDisplay('mobile')->redirect();
```
`popup` 模式是工具内默认的使用模式。`basic` 是默认使用的 scopes 值。
### [飞书](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)
通过一些简单的方法配置 app_ticket 就能使用内部应用模式
```php
$config = [
'feishu' => [
// or 'app_id'
'client_id' => 'your app id',
// or 'app_secret'
'client_secret' => 'your app secret',
// or 'redirect_url'
'redirect' => 'redirect URL',
// 如果你想使用使用内部应用的方式获取 app_access_token
// 对这个键设置了 'internal' 值那么你已经开启了内部应用模式
'app_mode' => 'internal'
]
];
$socialite = new SocialiteManager($config);
$feishuDriver = $socialite->create('feishu');
$feishuDriver->withInternalAppMode()->userFromCode('here is code');
$feishuDriver->withDefaultMode()->withAppTicket('app_ticket')->userFromCode('here is code');
```
### [淘宝](https://open.taobao.com/doc.htm?docId=102635&docType=1&source=search)
其他配置与其他平台的一样,你能选择你想要展示的重定向页面类型通过使用 `withView()`
```php
$authUrl = $socialite->create('taobao')->withView('wap')->redirect();
```
`web` 模式是工具默认使用的展示方式, `user_info` 是默认使用的 scopes 范围值。
### [微信](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/Official_Accounts/official_account_website_authorization.html)
我们支持开放平台代表公众号进行第三方平台网页授权。
你只需要像下面这样输入你的配置。官方账号不需要授权。
```php
...
[
'wechat' =>
[
'client_id' => 'client_id',
'client_secret' => 'client_secret',
'redirect' => 'redirect-url',
// 开放平台 - 第三方平台所需
'component' => [
// or 'app_id', 'component_app_id' as key
'id' => 'component-app-id',
// or 'app_token', 'access_token', 'component_access_token' as key
'token' => 'component-access-token',
]
]
],
...
```
## 其他一些技巧
### Scopes
在重定向用户之前,您还可以使用 `scopes()` 方法在请求上设置 “范围”。此方法将覆盖所有现有的作用域:
```php
$response = $socialite->create('github')
->scopes(['scope1', 'scope2'])->redirect();
```
### Redirect URL
你也可以动态设置' redirect_uri ',你可以使用以下方法来改变 `redirect_uri` URL:
```php
$url = 'your callback url.';
$socialite->redirect($url);
// or
$socialite->withRedirectUrl($url)->redirect();
```
### State
你的应用程序可以使用一个状态参数来确保响应属于同一个用户发起的请求,从而防止跨站请求伪造 (CSFR) 攻击。当恶意攻击者欺骗用户执行不需要的操作 (只有用户有权在受信任的 web 应用程序上执行) 时,就会发生 CSFR 攻击,所有操作都将在不涉及或警告用户的情况下完成。
这里有一个最简单的例子,说明了如何提供状态可以让你的应用程序更安全。在本例中,我们使用会话 ID 作为状态参数,但是您可以使用您想要为状态创建值的任何逻辑。
### 带着 `state` 参数的重定向
```php
<?php
session_start();
$config = [
//...
];
// Assign to state the hashing of the session ID
$state = hash('sha256', session_id());
$socialite = new SocialiteManager($config);
$url = $socialite->create('github')->withState($state)->redirect();
return redirect($url);
```
### 检验回调的 `state`
一旦用户授权你的应用程序,用户将被重定向回你的应用程序的 redirect_uri。OAuth 服务器将不加修改地返回状态参数。检查 redirect_uri 中提供的状态是否与应用程序生成的状态相匹配:
```php
<?php
session_start();
$state = request()->query('state');
$code = request()->query('code');
// Check the state received with current session id
if ($state != hash('sha256', session_id())) {
exit('State does not match!');
}
$user = $socialite->create('github')->userFromCode($code);
// authorized
```
[查看更多关于 `state` 参数的文档](https://auth0.com/docs/protocols/oauth2/oauth-state)
### 其他的一些参数
要在请求中包含任何可选参数,调用 `with()` 方法传入一个你想要设置的关联数组:
```php
$response = $socialite->create('google')
->with(['hd' => 'example.com'])->redirect();
```
## User interface
### 标准的 user api
```php
$user = $socialite->create('github')->userFromCode($code);
```
```json
{
"id": 1472352,
"nickname": "overtrue",
"name": "安正超",
"email": "anzhengchao@gmail.com",
"avatar": "https://avatars.githubusercontent.com/u/1472352?v=3",
"raw": {
"login": "overtrue",
"id": 1472352,
"avatar_url": "https://avatars.githubusercontent.com/u/1472352?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/overtrue",
"html_url": "https://github.com/overtrue",
...
},
"token_response": {
"access_token": "5b1dc56d64fffbd052359f032716cc4e0a1cb9a0",
"token_type": "bearer",
"scope": "user:email"
}
}
```
你可以像这样以数组键的形式获取 user 属性:
```php
$user['id']; // 1472352
$user['nickname']; // "overtrue"
$user['name']; // "安正超"
$user['email']; // "anzhengchao@gmail.com"
...
```
或者使用该 `User` 对象的方法:
```php
mixed $user->getId();
?string $user->getNickname();
?string $user->getName();
?string $user->getEmail();
?string $user->getAvatar();
?string $user->getRaw();
?string $user->getAccessToken();
?string $user->getRefreshToken();
?int $user->getExpiresIn();
?array $user->getTokenResponse();
```
### 从 OAuth API 响应中取得原始数据
`$user->getRaw()` 方法会返回一个 **array**
### 当你使用 userFromCode() 想要获取 token 响应的原始数据
`$user->getTokenResponse()` 方法会返回一个 **array** 里面是响应从获取 token 时候 API 返回的响应。
> 注意:当你使用 `userFromCode()` 时,这个方法只返回一个 **有效的数组**,否则将返回 **null**,因为 `userFromToken() ` 没有 token 的 HTTP 响应。
### 通过 access token 获取用户信息
```php
$accessToken = 'xxxxxxxxxxx';
$user = $socialite->userFromToken($accessToken);
```
# Enjoy it! :heart:
# 参照
- [Alipay - 用户信息授权](https://opendocs.alipay.com/open/289/105656)
- [DingTalk - 扫码登录第三方网站](https://ding-doc.dingtalk.com/doc#/serverapi3/mrugr3)
- [Google - OpenID Connect](https://developers.google.com/identity/protocols/OpenIDConnect)
- [Github - Authorizing OAuth Apps](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/)
- [Facebook - Graph API](https://developers.facebook.com/docs/graph-api)
- [Linkedin - Authenticating with OAuth 2.0](https://developer.linkedin.com/docs/oauth2)
- [微博 - OAuth 2.0 授权机制说明](http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E)
- [QQ - OAuth 2.0 登录 QQ](http://wiki.connect.qq.com/oauth2-0%E7%AE%80%E4%BB%8B)
- [腾讯云 - OAuth2.0](https://cloud.tencent.com/document/product/306/37730#.E6.8E.A5.E5.85.A5.E8.85.BE.E8.AE.AF.E4.BA.91-oauth)
- [微信公众平台 - OAuth 文档](http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html)
- [微信开放平台 - 网站应用微信登录开发指南](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN)
- [微信开放平台 - 代公众号发起网页授权](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318590&token=&lang=zh_CN)
- [企业微信 - OAuth 文档](https://open.work.weixin.qq.com/api/doc/90000/90135/91020)
- [企业微信第三方应用 - OAuth 文档](https://open.work.weixin.qq.com/api/doc/90001/90143/91118)
- [豆瓣 - OAuth 2.0 授权机制说明](http://developers.douban.com/wiki/?title=oauth2)
- [抖音 - 网站应用开发指南](http://open.douyin.com/platform/doc)
- [飞书 - 授权说明](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)
- [Tapd - 用户授权说明](https://www.tapd.cn/help/show#1120003271001000093)
- [Line - OAuth 2.0](https://developers.line.biz/en/docs/line-login/integrate-line-login/)
- [Gitee - OAuth文档](https://gitee.com/api/v5/oauth_doc#/)
# PHP 扩展包开发
> 想知道如何从零开始构建 PHP 扩展包?
>
> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package)
# License
MIT

@ -0,0 +1,44 @@
{
"name": "ifornew/socialite",
"description": "A collection of OAuth 2 packages.",
"keywords": [
"oauth",
"social",
"login",
"weibo",
"wechat",
"qq",
"feishu",
"qcloud"
],
"autoload": {
"psr-4": {
"Ifornew\\Socialite\\": "src/"
}
},
"require": {
"php": ">=7.4",
"symfony/http-foundation": "^4.0 || ^5.0",
"guzzlehttp/guzzle": "~6.0|~7.0",
"ext-json": "*",
"symfony/psr-http-message-bridge": "^2.0",
"ext-openssl": "*"
},
"require-dev": {
"mockery/mockery": "~1.2",
"phpunit/phpunit": "~9.0",
"friendsofphp/php-cs-fixer": "^3.0"
},
"license": "MIT",
"authors": [
{
"name": "overtrue",
"email": "anzhengchao@gmail.com"
}
],
"scripts": {
"check-style": "php-cs-fixer fix --using-cache=no --diff --dry-run --ansi",
"fix-style": "php-cs-fixer fix --using-cache=no --ansi",
"test": "vendor/bin/phpunit --colors=always"
}
}

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

@ -0,0 +1,122 @@
<?php
namespace Ifornew\Socialite;
use ArrayAccess;
use InvalidArgumentException;
class Config implements ArrayAccess, \JsonSerializable
{
/**
* @var array
*/
protected array $config;
/**
* @param array $config
*/
public function __construct(array $config)
{
$this->config = $config;
}
/**
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get(string $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;
}
/**
* @param string $key
* @param mixed $value
*
* @return array
*/
public function set(string $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;
}
/**
* @param string $key
*
* @return bool
*/
public function has(string $key): bool
{
return (bool) $this->get($key);
}
public function offsetExists($offset): bool
{
return array_key_exists($offset, $this->config);
}
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->get($offset);
}
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->set($offset, null);
}
#[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->config;
}
public function __toString()
{
return \json_encode($this, \JSON_UNESCAPED_UNICODE);
}
}

@ -0,0 +1,13 @@
<?php
namespace Ifornew\Socialite\Contracts;
interface FactoryInterface
{
/**
* @param string $driver
*
* @return \Ifornew\Socialite\Contracts\ProviderInterface
*/
public function create(string $driver): ProviderInterface;
}

@ -0,0 +1,14 @@
<?php
namespace Ifornew\Socialite\Contracts;
use Ifornew\Socialite\User;
interface ProviderInterface
{
public function redirect(?string $redirectUrl = null): string;
public function userFromCode(string $code): User;
public function userFromToken(string $token): User;
}

@ -0,0 +1,22 @@
<?php
namespace Ifornew\Socialite\Contracts;
interface UserInterface
{
public function getId();
public function getNickname(): ?string;
public function getName(): ?string;
public function getEmail(): ?string;
public function getAvatar(): ?string;
public function getAccessToken(): ?string;
public function getRefreshToken(): ?string;
public function getExpiresIn(): ?int;
}

@ -0,0 +1,19 @@
<?php
namespace Ifornew\Socialite\Exceptions;
class AuthorizeFailedException extends Exception
{
public array $body;
/**
* @param string $message
* @param array $body
*/
public function __construct(string $message, $body)
{
parent::__construct($message, -1);
$this->body = (array) $body;
}
}

@ -0,0 +1,10 @@
<?php
namespace Ifornew\Socialite\Exceptions;
/**
* Class BadRequestException.
*/
class BadRequestException extends Exception
{
}

@ -0,0 +1,8 @@
<?php
namespace Ifornew\Socialite\Exceptions;
class Exception extends \Exception
{
//
}

@ -0,0 +1,12 @@
<?php
namespace Ifornew\Socialite\Exceptions\Feishu;
use Ifornew\Socialite\Exceptions\Exception;
/**
* Class BadRequestException.
*/
class InvalidTicketException extends Exception
{
}

@ -0,0 +1,8 @@
<?php
namespace Ifornew\Socialite\Exceptions;
class InvalidArgumentException extends Exception
{
//
}

@ -0,0 +1,19 @@
<?php
namespace Ifornew\Socialite\Exceptions;
class InvalidTokenException extends Exception
{
public string $token;
/**
* @param string $message
* @param string $token
*/
public function __construct(string $message, string $token)
{
parent::__construct($message, -1);
$this->token = $token;
}
}

@ -0,0 +1,8 @@
<?php
namespace Ifornew\Socialite\Exceptions;
class MethodDoesNotSupportException extends Exception
{
//
}

@ -0,0 +1,240 @@
<?php
namespace Ifornew\Socialite\Providers;
use Ifornew\Socialite\Exceptions\InvalidArgumentException;
use Ifornew\Socialite\User;
/**
* @see https://opendocs.alipay.com/open/289/105656
*/
class Alipay extends Base
{
public const NAME = 'alipay';
protected string $baseUrl = 'https://openapi.alipay.com/gateway.do';
protected string $authUrl = 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm';
protected array $scopes = ['auth_user'];
protected string $apiVersion = '1.0';
protected string $signType = 'RSA2';
protected string $postCharset = 'UTF-8';
protected string $format = 'json';
protected bool $sandbox = false;
public function __construct(array $config)
{
parent::__construct($config);
$this->sandbox = $this->config->get('sandbox', false);
if ($this->sandbox) {
$this->baseUrl = 'https://openapi.alipaydev.com/gateway.do';
$this->authUrl = 'https://openauth.alipaydev.com/oauth2/publicAppAuthorize.htm';
}
}
protected function getAuthUrl(): string
{
return $this->buildAuthUrlFromBase($this->authUrl);
}
protected function getTokenUrl(): string
{
return $this->baseUrl;
}
/**
* @param string $token
*
* @return array
* @throws \Ifornew\Socialite\Exceptions\InvalidArgumentException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function getUserByToken(string $token): array
{
$params = $this->getPublicFields('alipay.user.info.share');
$params += ['auth_token' => $token];
$params['sign'] = $this->generateSign($params);
$response = $this->getHttpClient()->post(
$this->baseUrl,
[
'form_params' => $params,
'headers' => [
"content-type" => "application/x-www-form-urlencoded;charset=utf-8",
],
]
);
$response = json_decode($response->getBody()->getContents(), true);
if (!empty($response['error_response']) || empty($response['alipay_user_info_share_response'])) {
throw new \InvalidArgumentException('You have error! ' . \json_encode($response, JSON_UNESCAPED_UNICODE));
}
return $response['alipay_user_info_share_response'];
}
/**
* @param array $user
*
* @return \Ifornew\Socialite\User
*/
protected function mapUserToObject(array $user): User
{
return new User(
[
'id' => $user['user_id'] ?? null,
'name' => $user['nick_name'] ?? null,
'avatar' => $user['avatar'] ?? null,
'email' => $user['email'] ?? null,
]
);
}
/**
* @param string $code
*
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Ifornew\Socialite\Exceptions\AuthorizeFailedException
* @throws \Ifornew\Socialite\Exceptions\InvalidArgumentException
*/
public function tokenFromCode(string $code): array
{
$response = $this->getHttpClient()->post(
$this->getTokenUrl(),
[
'form_params' => $this->getTokenFields($code),
'headers' => [
"content-type" => "application/x-www-form-urlencoded;charset=utf-8",
],
]
);
$response = json_decode($response->getBody()->getContents(), true);
if (!empty($response['error_response'])) {
throw new \InvalidArgumentException('You have error! ' . json_encode($response, JSON_UNESCAPED_UNICODE));
}
return $this->normalizeAccessTokenResponse($response['alipay_system_oauth_token_response']);
}
/**
* @return array
* @throws \Ifornew\Socialite\Exceptions\InvalidArgumentException
*/
protected function getCodeFields(): array
{
if (empty($this->redirectUrl)) {
throw new InvalidArgumentException('Please set same redirect URL like your Alipay Official Admin');
}
$fields = array_merge(
[
'app_id' => $this->getConfig()->get('client_id') ?? $this->getConfig()->get('app_id'),
'scope' => implode(',', $this->scopes),
'redirect_uri' => $this->redirectUrl,
],
$this->parameters
);
return $fields;
}
/**
* @param string $code
*
* @return array|string[]
* @throws \Ifornew\Socialite\Exceptions\InvalidArgumentException
*/
protected function getTokenFields(string $code): array
{
$params = $this->getPublicFields('alipay.system.oauth.token');
$params += [
'code' => $code,
'grant_type' => 'authorization_code',
];
$params['sign'] = $this->generateSign($params);
return $params;
}
/**
* @param string $method
*