├── .coveralls.yml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── LICENSE
├── README.md
├── README_CN.md
├── composer.json
├── docs
└── .gitignore
├── examples
├── .gitignore
├── access.php
├── config.php
├── css
│ ├── main.css
│ └── normalize.css
├── index.php
├── request.php
└── service.php
├── makefile
├── phpdoc.xml
├── phpunit.xml.dist
├── src
└── EvaOAuth
│ ├── AdapterTrait.php
│ ├── AuthorizedHttpClient.php
│ ├── Events
│ ├── BeforeAuthorize.php
│ ├── BeforeGetAccessToken.php
│ ├── BeforeGetRequestToken.php
│ ├── EventsManager.php
│ ├── Formatter.php
│ └── LogSubscriber.php
│ ├── Exception
│ ├── BadMethodCallException.php
│ ├── ExceptionInterface.php
│ ├── InvalidArgumentException.php
│ ├── RequestException.php
│ └── VerifyException.php
│ ├── HttpClient.php
│ ├── OAuth1
│ ├── Consumer.php
│ ├── Providers
│ │ ├── AbstractProvider.php
│ │ ├── Flickr.php
│ │ └── Twitter.php
│ ├── ServiceProviderInterface.php
│ ├── Signature
│ │ ├── Hmac.php
│ │ ├── PlainText.php
│ │ └── SignatureInterface.php
│ └── Token
│ │ ├── AccessToken.php
│ │ ├── AccessTokenInterface.php
│ │ ├── RequestToken.php
│ │ └── RequestTokenInterface.php
│ ├── OAuth2
│ ├── AuthorizationServerInterface.php
│ ├── Client.php
│ ├── GrantStrategy
│ │ ├── AuthorizationCode.php
│ │ └── GrantStrategyInterface.php
│ ├── Providers
│ │ ├── AbstractProvider.php
│ │ ├── Douban.php
│ │ ├── Facebook.php
│ │ ├── Hundsun.php
│ │ ├── Tencent.php
│ │ └── Weibo.php
│ ├── ResourceServerInterface.php
│ └── Token
│ │ ├── AccessToken.php
│ │ └── AccessTokenInterface.php
│ ├── Service.php
│ ├── Token
│ ├── AccessTokenInterface.php
│ └── TokenTrait.php
│ ├── User
│ ├── StandardUser.php
│ ├── UserInterface.php
│ └── UserProviderInterface.php
│ └── Utils
│ ├── ResponseParser.php
│ └── Text.php
├── tests
├── Bootstrap.php
├── EvaOAuthTest
│ ├── AuthorizedHttpClientTest.php
│ ├── Events
│ │ ├── FormatterTest.php
│ │ └── LogSubscriberTest.php
│ ├── OAuth1
│ │ ├── ConsumerTest.php
│ │ ├── Signature
│ │ │ ├── HmacTest.php
│ │ │ └── PlainTextTest.php
│ │ └── Token
│ │ │ ├── AccessTokenTest.php
│ │ │ └── RequestTokenTest.php
│ ├── OAuth2
│ │ ├── ClientTest.php
│ │ ├── GrantStrategy
│ │ │ └── AuthorizationCodeTest.php
│ │ └── Token
│ │ │ └── AccessTokenTest.php
│ ├── ServiceTest.php
│ ├── User
│ │ └── UserTest.php
│ └── Utils
│ │ ├── ResponseParserTest.php
│ │ └── TextTest.php
└── report
│ └── .gitignore
└── tmp
└── .gitignore
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | src_dir: .
2 | coverage_clover: ./tmp/clover.xml
3 | json_path: ./tmp/coveralls.json
4 | exclude_no_stmt: true
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor/*
3 | config.local.php
4 | config/access_token*
5 | tests/report/*
6 | .idea
7 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | checks:
2 | php: true
3 |
4 | filter:
5 | excluded_paths:
6 | - tests/*
7 | - examples/*
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - 7.0
8 |
9 | matrix:
10 | fast_finish: true
11 | allow_failures:
12 | - php: 7.0
13 |
14 | before_script:
15 | - composer self-update
16 | - composer install --dev --prefer-source
17 |
18 | script:
19 | phpunit -v
20 |
21 | after_script:
22 | - ./vendor/bin/coveralls -v
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, AlloVince
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | * Neither the name of the {organization} nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | EvaOAuth
2 | =========
3 |
4 | [](https://packagist.org/packages/evaengine/eva-oauth)
5 | [](https://packagist.org/packages/evaengine/eva-oauth)
6 | [](https://travis-ci.org/AlloVince/EvaOAuth)
7 | [](https://coveralls.io/r/AlloVince/EvaOAuth?branch=master)
8 | [](https://scrutinizer-ci.com/g/AlloVince/EvaOAuth/?branch=master)
9 |
10 | EvaOAuth provides a standard interface for OAuth1.0 / OAuth2.0 client authorization, it is easy to integrate with any PHP project by very few lines code.
11 |
12 | [中文文档](http://avnpc.com/pages/evaoauth)
13 |
14 | ## Features
15 |
16 | - **Standard interface**, same code for both OAuth1.0 and OAuth2.0 different workflow, receiving token and user info as same format either.
17 | - **Fully tested**
18 | - **Easy to debug**, enable debug mode will record every request and response, help you find out problems quickly.
19 | - **Out-of-the-box**, already supported most popular websites including Facebook. Twitter, etc.
20 | - **Scalable**, integrate a new oauth website just need 3 lines code.
21 |
22 | ## Quick Start
23 |
24 | EvaOAuth can be found on [Packagist](https://packagist.org/packages/evaengine/eva-oauth). The recommended way to install this is through composer.
25 |
26 | Edit your composer.json and add:
27 |
28 | ``` json
29 | {
30 | "require": {
31 | "evaengine/eva-oauth": "~1.0"
32 | }
33 | }
34 | ```
35 |
36 | And install dependencies:
37 |
38 | ``` shell
39 | curl -sS https://getcomposer.org/installer | php
40 | php composer.phar install
41 | ```
42 |
43 | Let's start a example of Facebook Login, if you have already have a Facebook developer account and created an app, prepare a request.php as below:
44 |
45 | ``` php
46 | $service = new Eva\EvaOAuth\Service('Facebook', [
47 | 'key' => 'You Facebook App ID',
48 | 'secret' => 'You Facebook App Secret',
49 | 'callback' => 'http://localhost/EvaOAuth/example/access.php'
50 | ]);
51 | $service->requestAuthorize();
52 | ```
53 |
54 | Run request.php in browser, will be redirected to Facebook authorization page. After user confirm authorization, prepare the access.php for callback:
55 |
56 | ``` php
57 | $token = $service->getAccessToken();
58 | ```
59 |
60 | Once access token received, we could use access token to visit any protected resources.
61 |
62 | ``` php
63 | $httpClient = new Eva\EvaOAuth\AuthorizedHttpClient($token);
64 | $response = $httpClient->get('https://graph.facebook.com/me');
65 | ```
66 |
67 | That's it, more usages please check [examples](https://github.com/AlloVince/EvaOAuth/tree/master/examples) and [wiki](https://github.com/AlloVince/EvaOAuth/wiki).
68 |
69 | ## Providers
70 |
71 | EvaOAuth supports most popular OAuth services as below:
72 |
73 | - OAuth2.0
74 | - Douban
75 | - Facebook
76 | - Tencent
77 | - Weibo
78 | - OAuth1.0
79 | - Twitter
80 |
81 | Creating a custom provider require only few lines code, for OAuth2 sites:
82 |
83 |
84 | ``` php
85 | namespace YourNamespace;
86 |
87 | class Foursquare extends \Eva\EvaOAuth\OAuth2\Providers\AbstractProvider
88 | {
89 | protected $authorizeUrl = 'https://foursquare.com/oauth2/authorize';
90 | protected $accessTokenUrl = 'https://foursquare.com/oauth2/access_token';
91 | }
92 | ```
93 |
94 | Then register to service and create instance:
95 |
96 | ``` php
97 | use Eva\EvaOAuth\Service;
98 | Service::registerProvider('foursquare', 'YourNamespace\Foursquare');
99 | $service = new Service('foursquare', [
100 | 'key' => 'Foursquare App ID',
101 | 'secret' => 'Foursquare App Secret',
102 | 'callback' => 'http://somecallback/'
103 | ]);
104 | ```
105 |
106 | ## Storage
107 |
108 | In OAuth1.0 workflow, we need to store request token somewhere, and use request token exchange for access token.
109 |
110 | EvaOAuth use [Doctrine\Cache](https://github.com/doctrine/cache) as storage layer. If no configuration, default storage layer use file system to save data, default path is EvaOAuth/tmp.
111 |
112 | Feel free to change file storage path before `Service` start:
113 |
114 | ``` php
115 | Service::setStorage(new Doctrine\Common\Cache\FilesystemCache('/tmp'));
116 | ```
117 |
118 | Or use other storage such as Memcache:
119 |
120 | ``` php
121 | $storage = new \Doctrine\Common\Cache\MemcacheCache();
122 | $storage->setMemcache(new \Memcache());
123 | Service::setStorage($storage);
124 | ```
125 |
126 | ## Events Support
127 |
128 | EvaOAuth defined some events for easier injection which are:
129 |
130 | - BeforeGetRequestToken: Triggered before get request token.
131 | - BeforeAuthorize: Triggered before redirect to authorize page.
132 | - BeforeGetAccessToken: Triggered before get access token.
133 |
134 | For example, if we want to send an additional header before get access token:
135 |
136 | ``` php
137 | $service->getEmitter()->on('beforeGetAccessToken', function(\Eva\EvaOAuth\Events\BeforeGetAccessToken $event) {
138 | $event->getRequest()->addHeader('foo', 'bar');
139 | });
140 | ```
141 |
142 | ## Implementation Specification
143 |
144 | EvaOAuth based on amazing http client library [Guzzle](https://github.com/guzzle/guzzle), use fully OOP to describe OAuth specification.
145 |
146 | Refer wiki for details:
147 |
148 | - [OAuth1.0](https://github.com/AlloVince/EvaOAuth/wiki/OAuth1.0-Specification-Implementation)
149 | - [OAuth2.0](https://github.com/AlloVince/EvaOAuth/wiki/OAuth2.0-Specification-Implementation)
150 |
151 | ## Debug and Logging
152 |
153 | Enable debug mode will log all requests & responses.
154 |
155 | ``` php
156 | $service->debug('/tmp/access.log');
157 | ```
158 |
159 | Make sure PHP script have permission to write log path.
160 |
161 |
162 | ## API References
163 |
164 | Run `phpdoc` will generate API references under `docs/`.
165 |
166 | 
167 |
168 | [Join My Telegram Group](https://t.me/joinchat/HKvcQAw2kqASoYfxiSrIbA)
169 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | EvaOAuth
2 | =========
3 |
4 | [](https://packagist.org/packages/evaengine/eva-oauth)
5 | [](https://packagist.org/packages/evaengine/eva-oauth)
6 | [](https://travis-ci.org/AlloVince/EvaOAuth)
7 | [](https://coveralls.io/r/AlloVince/EvaOAuth?branch=master)
8 | [](https://scrutinizer-ci.com/g/AlloVince/EvaOAuth/?branch=master)
9 |
10 | EvaOAuth 是一个统一接口设计的[PHP OAuth](http://avnpc.com/pages/evaoauth) Client库,兼容OAuth1.0与OAuth2.0规范,可以通过10多行代码集成到任意项目中。
11 |
12 | 项目代码托管在 [https://github.com/AlloVince/EvaOAuth](https://github.com/AlloVince/EvaOAuth) ,欢迎Star及Fork贡献代码。
13 |
14 | ## 为什么选择EvaOAuth
15 |
16 | 经过若干项目考验, EvaOAuth1.0 根据实际需求进行了一次完全重构,主要的一些特性如下:
17 |
18 | - **标准接口**,无论OAuth1.0或OAuth2.0,同一套代码实现不同工作流,并且获取一致的数据格式,包括用户信息和Token。
19 | - **充分测试**,所有关键代码进行单元测试,同时通过CI保证多版本PHP下的可用性。
20 | - **容易调试**,开启Debug模式后,Log中会记录OAuth流程中所有的URL、Request、Response,帮助定位问题。
21 | - **开箱即用**,项目已经内置了主流的OAuth网址支持,如微博、QQ、Twitter、Facebook等。
22 | - **方便扩展**,可以通过最少3行代码集成新的OAuth服务,工作流程提供事件机制。
23 |
24 | ## 快速开始
25 |
26 | EvaOAuth可以通过[Packagist](https://packagist.org/packages/evaengine/eva-oauth)下载,推荐通过Composer安装。
27 |
28 | 编辑composer.json文件为:
29 |
30 | ``` json
31 | {
32 | "require": {
33 | "evaengine/eva-oauth": "~1.0"
34 | }
35 | }
36 | ```
37 |
38 | 然后通过Composer进行安装。
39 |
40 | ``` shell
41 | curl -sS https://getcomposer.org/installer | php
42 | php composer.phar install
43 | ```
44 |
45 | 下面通过一个实例演示如何集成豆瓣登录功能。假设已经在[豆瓣开发者](http://developers.douban.com)创建好一个应用。准备一个request.php如下:
46 |
47 | ``` php
48 | require_once './vendor/autoload.php'; //加载Composer自动生成的autoload
49 | $service = new Eva\EvaOAuth\Service('Douban', [
50 | 'key' => 'You Douban App ID', //对应豆瓣应用的API Key
51 | 'secret' => 'You Douban App Secret', //对应豆瓣应用的Secret
52 | 'callback' => 'http://localhost/EvaOAuth/example/access.php' //回调地址
53 | ]);
54 | $service->requestAuthorize();
55 | ```
56 |
57 | 在浏览器中运行request.php,如果参数正确则会被重定向到豆瓣授权页面,登录授权后会再次重定向回我们设置的`callback`。因此再准备好access.php文件:
58 |
59 | ``` php
60 | $token = $service->getAccessToken();
61 | ```
62 |
63 | 这样就拿到了豆瓣的Access Token,接下来可以使用Token去访问受保护的资源:
64 |
65 | ``` php
66 | $httpClient = new Eva\EvaOAuth\AuthorizedHttpClient($token);
67 | $response = $httpClient->get('https://api.douban.com/v2/user/~me');
68 | ```
69 |
70 | 这样就完成了OAuth的登录功能。更多细节可以参考代码的[示例](https://github.com/AlloVince/EvaOAuth/tree/master/examples)以及[Wiki](https://github.com/AlloVince/EvaOAuth/wiki)页面。
71 |
72 | ## OAuth网站支持
73 |
74 | EvaOAuth将一个OAuth网站称为一个Provider。目前支持的Provider有:
75 |
76 | - OAuth2.0
77 | - 豆瓣(Douban)
78 | - Facebook
79 | - QQ (Tencent)
80 | - 微博 (Weibo)
81 | - OAuth1.0
82 | - Twitter
83 |
84 | 新增一个Provider仅需数行代码,下面演示如何集成Foursquare网站:
85 |
86 |
87 | ``` php
88 | namespace YourNamespace;
89 |
90 | class Foursquare extends \Eva\EvaOAuth\OAuth2\Providers\AbstractProvider
91 | {
92 | protected $authorizeUrl = 'https://foursquare.com/oauth2/authorize';
93 | protected $accessTokenUrl = 'https://foursquare.com/oauth2/access_token';
94 | }
95 | ```
96 |
97 | 然后将Provider注册到EvaOAuth就可以使用了。
98 |
99 | ``` php
100 | use Eva\EvaOAuth\Service;
101 | Service::registerProvider('foursquare', 'YourNamespace\Foursquare');
102 | $service = new Service('foursquare', [
103 | 'key' => 'Foursquare App ID',
104 | 'secret' => 'Foursquare App Secret',
105 | 'callback' => 'http://somecallback/'
106 | ]);
107 | ```
108 |
109 | ## 数据存储
110 |
111 | 在OAuth1.0的流程中,需要将Request Token保存起来,然后在授权成功后使用Request Token换取Access Token。因此需要数据存储功能。
112 |
113 | EvaOAuth的数据存储通过[Doctrine\Cache](https://github.com/doctrine/cache)实现。默认情况下EvaOAuth会将数据保存为本地文件,保存路径为`EvaOAuth/tmp`。
114 |
115 | 可以在EvaOAuth初始化前任意更改存储方式及存储位置,例如将文件保存位置更改为`/tmp`:
116 |
117 | ``` php
118 | Service::setStorage(new Doctrine\Common\Cache\FilesystemCache('/tmp'));
119 | ```
120 |
121 | 或者使用Memcache保存:
122 |
123 | ``` php
124 | $storage = new \Doctrine\Common\Cache\MemcacheCache();
125 | $storage->setMemcache(new \Memcache());
126 | Service::setStorage($storage);
127 | ```
128 |
129 | ## 事件支持
130 |
131 | EvaOAuth 定义了若干事件方面更容易的注入逻辑
132 |
133 | - `BeforeGetRequestToken`: 获取Request Token前触发。
134 | - `BeforeAuthorize`: 重定向到授权页面前触发。
135 | - `BeforeGetAccessToken`: 获取Access Token前触发。
136 |
137 | 比如我们希望在获取Access Token前向HTTP请求中加一个自定义Header,可以通过以下方式实现:
138 |
139 | ``` php
140 | $service->getEmitter()->on('beforeGetAccessToken', function(\Eva\EvaOAuth\Events\BeforeGetAccessToken $event) {
141 | $event->getRequest()->addHeader('foo', 'bar');
142 | });
143 | ```
144 |
145 | ## 技术实现
146 |
147 | EvaOAuth 基于强大的HTTP客户端库[Guzzle](https://github.com/guzzle/guzzle),并通过OOP方式对OAuth规范进行了完整的描述。
148 |
149 | 为了避免对规范的诠释上出现误差,底层代码优先选择规范描述中的角色与名词,规范间差异则在上层代码中统一。
150 |
151 | 因此如果没有同时支持两套规范的需求,可以直接使用OAuth1.0、OAuth2.0分别对应的工作流。
152 |
153 | 详细用例可以参考Wiki:
154 |
155 | - [OAuth1.0](https://github.com/AlloVince/EvaOAuth/wiki/OAuth1.0-Specification-Implementation)
156 | - [OAuth2.0](https://github.com/AlloVince/EvaOAuth/wiki/OAuth2.0-Specification-Implementation)
157 |
158 | ## Debug与Log
159 |
160 | 开启Debug模式将在Log中记录所有的请求与响应。
161 |
162 | ``` php
163 | $service->debug('/tmp/access.log');
164 | ```
165 |
166 | 请确保PHP对log文件有写入权限。
167 |
168 | ## API文档
169 |
170 | 首先通过`pear install phpdoc/phpDocumentor`安装phpDocumentor,然后在项目根目录下运行`phpdoc`,会在`docs/`下生成API文档。
171 |
172 | ## 问题反馈及贡献代码
173 |
174 | 项目代码托管在 [https://github.com/AlloVince/EvaOAuth](https://github.com/AlloVince/EvaOAuth]) ,欢迎Star及Fork贡献代码。
175 |
176 | 有问题欢迎在[EvaOAuth Issue](https://github.com/AlloVince/EvaOAuth/issues)提出。
177 |
178 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "evaengine/eva-oauth",
3 | "description": "EvaOAuth is an abstract interface to setup OAuth client, both support OAuth1.0a / OAuth2.",
4 | "license": "BSD-3-Clause",
5 | "homepage": "http://avnpc.com/",
6 | "authors": [
7 | {
8 | "name": "AlloVince",
9 | "email": "allo.vince@gmail.com",
10 | "homepage": "http://avnpc.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.4",
15 | "guzzlehttp/guzzle": "~5.2",
16 | "doctrine/cache": "~1.4",
17 | "monolog/monolog": "~1.13"
18 | },
19 | "require-dev": {
20 | "mockery/mockery": "dev-master",
21 | "satooshi/php-coveralls": "dev-master"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Eva\\EvaOAuth\\": "src/EvaOAuth/"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | oauth1/*
2 | config.local.php
3 |
--------------------------------------------------------------------------------
/examples/access.php:
--------------------------------------------------------------------------------
1 | ';
5 | $token = $service->getAccessToken();
6 | var_dump($token);
7 |
8 | $httpClient = new \Eva\EvaOAuth\AuthorizedHttpClient($token);
9 | $response = $httpClient->get('https://graph.facebook.com/me');
10 | echo $response;
11 |
--------------------------------------------------------------------------------
/examples/config.php:
--------------------------------------------------------------------------------
1 | [
5 | 'key' => '',
6 | 'secret' => '',
7 | ],
8 | 'flickr' => [
9 | 'key' => '',
10 | 'secret' => '',
11 | ],
12 | 'facebook' => [
13 | 'key' => '',
14 | 'secret' => '',
15 | ],
16 | 'tencent' => [
17 | 'key' => '',
18 | 'secret' => '',
19 | ],
20 | 'weibo' => [
21 | 'key' => '',
22 | 'secret' => '',
23 | ],
24 | 'hundsun' => [
25 | 'key' => '',
26 | 'secret' => ''
27 | ],
28 | 'douban' => [
29 | 'key' => '',
30 | 'secret' => ''
31 | ]
32 | ];
33 |
--------------------------------------------------------------------------------
/examples/css/main.css:
--------------------------------------------------------------------------------
1 | /*! HTML5 Boilerplate v5.1.0 | MIT License | https://html5boilerplate.com/ */
2 |
3 | /*
4 | * What follows is the result of much research on cross-browser styling.
5 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
6 | * Kroc Camen, and the H5BP dev community and team.
7 | */
8 |
9 | /* ==========================================================================
10 | Base styles: opinionated defaults
11 | ========================================================================== */
12 |
13 | html {
14 | color: #222;
15 | font-size: 1em;
16 | line-height: 1.4;
17 | }
18 |
19 | /*
20 | * Remove text-shadow in selection highlight:
21 | * https://twitter.com/miketaylr/status/12228805301
22 | *
23 | * These selection rule sets have to be separate.
24 | * Customize the background color to match your design.
25 | */
26 |
27 | ::-moz-selection {
28 | background: #b3d4fc;
29 | text-shadow: none;
30 | }
31 |
32 | ::selection {
33 | background: #b3d4fc;
34 | text-shadow: none;
35 | }
36 |
37 | /*
38 | * A better looking default horizontal rule
39 | */
40 |
41 | hr {
42 | display: block;
43 | height: 1px;
44 | border: 0;
45 | border-top: 1px solid #ccc;
46 | margin: 1em 0;
47 | padding: 0;
48 | }
49 |
50 | /*
51 | * Remove the gap between audio, canvas, iframes,
52 | * images, videos and the bottom of their containers:
53 | * https://github.com/h5bp/html5-boilerplate/issues/440
54 | */
55 |
56 | audio,
57 | canvas,
58 | iframe,
59 | img,
60 | svg,
61 | video {
62 | vertical-align: middle;
63 | }
64 |
65 | /*
66 | * Remove default fieldset styles.
67 | */
68 |
69 | fieldset {
70 | border: 0;
71 | margin: 0;
72 | padding: 0;
73 | }
74 |
75 | /*
76 | * Allow only vertical resizing of textareas.
77 | */
78 |
79 | textarea {
80 | resize: vertical;
81 | }
82 |
83 | /* ==========================================================================
84 | Browser Upgrade Prompt
85 | ========================================================================== */
86 |
87 | .browserupgrade {
88 | margin: 0.2em 0;
89 | background: #ccc;
90 | color: #000;
91 | padding: 0.2em 0;
92 | }
93 |
94 | /* ==========================================================================
95 | Author's custom styles
96 | ========================================================================== */
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | /* ==========================================================================
115 | Helper classes
116 | ========================================================================== */
117 |
118 | /*
119 | * Hide visually and from screen readers:
120 | */
121 |
122 | .hidden {
123 | display: none !important;
124 | }
125 |
126 | /*
127 | * Hide only visually, but have it available for screen readers:
128 | * http://snook.ca/archives/html_and_css/hiding-content-for-accessibility
129 | */
130 |
131 | .visuallyhidden {
132 | border: 0;
133 | clip: rect(0 0 0 0);
134 | height: 1px;
135 | margin: -1px;
136 | overflow: hidden;
137 | padding: 0;
138 | position: absolute;
139 | width: 1px;
140 | }
141 |
142 | /*
143 | * Extends the .visuallyhidden class to allow the element
144 | * to be focusable when navigated to via the keyboard:
145 | * https://www.drupal.org/node/897638
146 | */
147 |
148 | .visuallyhidden.focusable:active,
149 | .visuallyhidden.focusable:focus {
150 | clip: auto;
151 | height: auto;
152 | margin: 0;
153 | overflow: visible;
154 | position: static;
155 | width: auto;
156 | }
157 |
158 | /*
159 | * Hide visually and from screen readers, but maintain layout
160 | */
161 |
162 | .invisible {
163 | visibility: hidden;
164 | }
165 |
166 | /*
167 | * Clearfix: contain floats
168 | *
169 | * For modern browsers
170 | * 1. The space content is one way to avoid an Opera bug when the
171 | * `contenteditable` attribute is included anywhere else in the document.
172 | * Otherwise it causes space to appear at the top and bottom of elements
173 | * that receive the `clearfix` class.
174 | * 2. The use of `table` rather than `block` is only necessary if using
175 | * `:before` to contain the top-margins of child elements.
176 | */
177 |
178 | .clearfix:before,
179 | .clearfix:after {
180 | content: " "; /* 1 */
181 | display: table; /* 2 */
182 | }
183 |
184 | .clearfix:after {
185 | clear: both;
186 | }
187 |
188 | /* ==========================================================================
189 | EXAMPLE Media Queries for Responsive Design.
190 | These examples override the primary ('mobile first') styles.
191 | Modify as content requires.
192 | ========================================================================== */
193 |
194 | @media only screen and (min-width: 35em) {
195 | /* Style adjustments for viewports that meet the condition */
196 | }
197 |
198 | @media print,
199 | (-webkit-min-device-pixel-ratio: 1.25),
200 | (min-resolution: 1.25dppx),
201 | (min-resolution: 120dpi) {
202 | /* Style adjustments for high resolution devices */
203 | }
204 |
205 | /* ==========================================================================
206 | Print styles.
207 | Inlined to avoid the additional HTTP request:
208 | http://www.phpied.com/delay-loading-your-print-css/
209 | ========================================================================== */
210 |
211 | @media print {
212 | *,
213 | *:before,
214 | *:after {
215 | background: transparent !important;
216 | color: #000 !important; /* Black prints faster:
217 | http://www.sanbeiji.com/archives/953 */
218 | box-shadow: none !important;
219 | text-shadow: none !important;
220 | }
221 |
222 | a,
223 | a:visited {
224 | text-decoration: underline;
225 | }
226 |
227 | a[href]:after {
228 | content: " (" attr(href) ")";
229 | }
230 |
231 | abbr[title]:after {
232 | content: " (" attr(title) ")";
233 | }
234 |
235 | /*
236 | * Don't show links that are fragment identifiers,
237 | * or use the `javascript:` pseudo protocol
238 | */
239 |
240 | a[href^="#"]:after,
241 | a[href^="javascript:"]:after {
242 | content: "";
243 | }
244 |
245 | pre,
246 | blockquote {
247 | border: 1px solid #999;
248 | page-break-inside: avoid;
249 | }
250 |
251 | /*
252 | * Printing Tables:
253 | * http://css-discuss.incutio.com/wiki/Printing_Tables
254 | */
255 |
256 | thead {
257 | display: table-header-group;
258 | }
259 |
260 | tr,
261 | img {
262 | page-break-inside: avoid;
263 | }
264 |
265 | img {
266 | max-width: 100% !important;
267 | }
268 |
269 | p,
270 | h2,
271 | h3 {
272 | orphans: 3;
273 | widows: 3;
274 | }
275 |
276 | h2,
277 | h3 {
278 | page-break-after: avoid;
279 | }
280 | }
281 |
--------------------------------------------------------------------------------
/examples/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /**
4 | * 1. Set default font family to sans-serif.
5 | * 2. Prevent iOS and IE text size adjust after device orientation change,
6 | * without disabling user zoom.
7 | */
8 |
9 | html {
10 | font-family: sans-serif; /* 1 */
11 | -ms-text-size-adjust: 100%; /* 2 */
12 | -webkit-text-size-adjust: 100%; /* 2 */
13 | }
14 |
15 | /**
16 | * Remove default margin.
17 | */
18 |
19 | body {
20 | margin: 0;
21 | }
22 |
23 | /* HTML5 display definitions
24 | ========================================================================== */
25 |
26 | /**
27 | * Correct `block` display not defined for any HTML5 element in IE 8/9.
28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11
29 | * and Firefox.
30 | * Correct `block` display not defined for `main` in IE 11.
31 | */
32 |
33 | article,
34 | aside,
35 | details,
36 | figcaption,
37 | figure,
38 | footer,
39 | header,
40 | hgroup,
41 | main,
42 | menu,
43 | nav,
44 | section,
45 | summary {
46 | display: block;
47 | }
48 |
49 | /**
50 | * 1. Correct `inline-block` display not defined in IE 8/9.
51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
52 | */
53 |
54 | audio,
55 | canvas,
56 | progress,
57 | video {
58 | display: inline-block; /* 1 */
59 | vertical-align: baseline; /* 2 */
60 | }
61 |
62 | /**
63 | * Prevent modern browsers from displaying `audio` without controls.
64 | * Remove excess height in iOS 5 devices.
65 | */
66 |
67 | audio:not([controls]) {
68 | display: none;
69 | height: 0;
70 | }
71 |
72 | /**
73 | * Address `[hidden]` styling not present in IE 8/9/10.
74 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
75 | */
76 |
77 | [hidden],
78 | template {
79 | display: none;
80 | }
81 |
82 | /* Links
83 | ========================================================================== */
84 |
85 | /**
86 | * Remove the gray background color from active links in IE 10.
87 | */
88 |
89 | a {
90 | background-color: transparent;
91 | }
92 |
93 | /**
94 | * Improve readability of focused elements when they are also in an
95 | * active/hover state.
96 | */
97 |
98 | a:active,
99 | a:hover {
100 | outline: 0;
101 | }
102 |
103 | /* Text-level semantics
104 | ========================================================================== */
105 |
106 | /**
107 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
108 | */
109 |
110 | abbr[title] {
111 | border-bottom: 1px dotted;
112 | }
113 |
114 | /**
115 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
116 | */
117 |
118 | b,
119 | strong {
120 | font-weight: bold;
121 | }
122 |
123 | /**
124 | * Address styling not present in Safari and Chrome.
125 | */
126 |
127 | dfn {
128 | font-style: italic;
129 | }
130 |
131 | /**
132 | * Address variable `h1` font-size and margin within `section` and `article`
133 | * contexts in Firefox 4+, Safari, and Chrome.
134 | */
135 |
136 | h1 {
137 | font-size: 2em;
138 | margin: 0.67em 0;
139 | }
140 |
141 | /**
142 | * Address styling not present in IE 8/9.
143 | */
144 |
145 | mark {
146 | background: #ff0;
147 | color: #000;
148 | }
149 |
150 | /**
151 | * Address inconsistent and variable font size in all browsers.
152 | */
153 |
154 | small {
155 | font-size: 80%;
156 | }
157 |
158 | /**
159 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
160 | */
161 |
162 | sub,
163 | sup {
164 | font-size: 75%;
165 | line-height: 0;
166 | position: relative;
167 | vertical-align: baseline;
168 | }
169 |
170 | sup {
171 | top: -0.5em;
172 | }
173 |
174 | sub {
175 | bottom: -0.25em;
176 | }
177 |
178 | /* Embedded content
179 | ========================================================================== */
180 |
181 | /**
182 | * Remove border when inside `a` element in IE 8/9/10.
183 | */
184 |
185 | img {
186 | border: 0;
187 | }
188 |
189 | /**
190 | * Correct overflow not hidden in IE 9/10/11.
191 | */
192 |
193 | svg:not(:root) {
194 | overflow: hidden;
195 | }
196 |
197 | /* Grouping content
198 | ========================================================================== */
199 |
200 | /**
201 | * Address margin not present in IE 8/9 and Safari.
202 | */
203 |
204 | figure {
205 | margin: 1em 40px;
206 | }
207 |
208 | /**
209 | * Address differences between Firefox and other browsers.
210 | */
211 |
212 | hr {
213 | box-sizing: content-box;
214 | height: 0;
215 | }
216 |
217 | /**
218 | * Contain overflow in all browsers.
219 | */
220 |
221 | pre {
222 | overflow: auto;
223 | }
224 |
225 | /**
226 | * Address odd `em`-unit font size rendering in all browsers.
227 | */
228 |
229 | code,
230 | kbd,
231 | pre,
232 | samp {
233 | font-family: monospace, monospace;
234 | font-size: 1em;
235 | }
236 |
237 | /* Forms
238 | ========================================================================== */
239 |
240 | /**
241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited
242 | * styling of `select`, unless a `border` property is set.
243 | */
244 |
245 | /**
246 | * 1. Correct color not being inherited.
247 | * Known issue: affects color of disabled elements.
248 | * 2. Correct font properties not being inherited.
249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
250 | */
251 |
252 | button,
253 | input,
254 | optgroup,
255 | select,
256 | textarea {
257 | color: inherit; /* 1 */
258 | font: inherit; /* 2 */
259 | margin: 0; /* 3 */
260 | }
261 |
262 | /**
263 | * Address `overflow` set to `hidden` in IE 8/9/10/11.
264 | */
265 |
266 | button {
267 | overflow: visible;
268 | }
269 |
270 | /**
271 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
272 | * All other form control elements do not inherit `text-transform` values.
273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
274 | * Correct `select` style inheritance in Firefox.
275 | */
276 |
277 | button,
278 | select {
279 | text-transform: none;
280 | }
281 |
282 | /**
283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
284 | * and `video` controls.
285 | * 2. Correct inability to style clickable `input` types in iOS.
286 | * 3. Improve usability and consistency of cursor style between image-type
287 | * `input` and others.
288 | */
289 |
290 | button,
291 | html input[type="button"], /* 1 */
292 | input[type="reset"],
293 | input[type="submit"] {
294 | -webkit-appearance: button; /* 2 */
295 | cursor: pointer; /* 3 */
296 | }
297 |
298 | /**
299 | * Re-set default cursor for disabled elements.
300 | */
301 |
302 | button[disabled],
303 | html input[disabled] {
304 | cursor: default;
305 | }
306 |
307 | /**
308 | * Remove inner padding and border in Firefox 4+.
309 | */
310 |
311 | button::-moz-focus-inner,
312 | input::-moz-focus-inner {
313 | border: 0;
314 | padding: 0;
315 | }
316 |
317 | /**
318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
319 | * the UA stylesheet.
320 | */
321 |
322 | input {
323 | line-height: normal;
324 | }
325 |
326 | /**
327 | * It's recommended that you don't attempt to style these elements.
328 | * Firefox's implementation doesn't respect box-sizing, padding, or width.
329 | *
330 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
331 | * 2. Remove excess padding in IE 8/9/10.
332 | */
333 |
334 | input[type="checkbox"],
335 | input[type="radio"] {
336 | box-sizing: border-box; /* 1 */
337 | padding: 0; /* 2 */
338 | }
339 |
340 | /**
341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain
342 | * `font-size` values of the `input`, it causes the cursor style of the
343 | * decrement button to change from `default` to `text`.
344 | */
345 |
346 | input[type="number"]::-webkit-inner-spin-button,
347 | input[type="number"]::-webkit-outer-spin-button {
348 | height: auto;
349 | }
350 |
351 | /**
352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
354 | */
355 |
356 | input[type="search"] {
357 | -webkit-appearance: textfield; /* 1 */
358 | box-sizing: content-box; /* 2 */
359 | }
360 |
361 | /**
362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X.
363 | * Safari (but not Chrome) clips the cancel button when the search input has
364 | * padding (and `textfield` appearance).
365 | */
366 |
367 | input[type="search"]::-webkit-search-cancel-button,
368 | input[type="search"]::-webkit-search-decoration {
369 | -webkit-appearance: none;
370 | }
371 |
372 | /**
373 | * Define consistent border, margin, and padding.
374 | */
375 |
376 | fieldset {
377 | border: 1px solid #c0c0c0;
378 | margin: 0 2px;
379 | padding: 0.35em 0.625em 0.75em;
380 | }
381 |
382 | /**
383 | * 1. Correct `color` not being inherited in IE 8/9/10/11.
384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
385 | */
386 |
387 | legend {
388 | border: 0; /* 1 */
389 | padding: 0; /* 2 */
390 | }
391 |
392 | /**
393 | * Remove default vertical scrollbar in IE 8/9/10/11.
394 | */
395 |
396 | textarea {
397 | overflow: auto;
398 | }
399 |
400 | /**
401 | * Don't inherit the `font-weight` (applied by a rule above).
402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
403 | */
404 |
405 | optgroup {
406 | font-weight: bold;
407 | }
408 |
409 | /* Tables
410 | ========================================================================== */
411 |
412 | /**
413 | * Remove most spacing between table cells.
414 | */
415 |
416 | table {
417 | border-collapse: collapse;
418 | border-spacing: 0;
419 | }
420 |
421 | td,
422 | th {
423 | padding: 0;
424 | }
425 |
--------------------------------------------------------------------------------
/examples/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EvaEngine OAuth Demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | 1. Copy config.php to config.local.php
19 | 2. Fill config keys and secrets
20 | 3. Click below links to start OAuth authorize
21 |
22 |
23 | $client) {
26 | printf("- %s
", $key, $key);
27 | }
28 | ?>
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/examples/request.php:
--------------------------------------------------------------------------------
1 | ';
5 | $service->requestAuthorize();
6 |
--------------------------------------------------------------------------------
/examples/service.php:
--------------------------------------------------------------------------------
1 | $config[$provider]['key'],
16 | 'secret' => $config[$provider]['secret'],
17 | 'callback' => dirname('http://' . $_SERVER['HTTP_HOST'] . $_SERVER["REQUEST_URI"]) . '/access.php?provider=' . $provider
18 | ]);
19 |
20 | $service->debug(__DIR__ . '/../tmp/access.log');
21 |
--------------------------------------------------------------------------------
/makefile:
--------------------------------------------------------------------------------
1 | list:
2 | @echo "fix"
3 | @echo "phpcs"
4 | @echo "test"
5 | @echo "test-report"
6 | @echo "health"
7 |
8 | phpcs:
9 | phpcs --standard=PSR2 --extensions=php --ignore=vendor/*,tests/*,cphalcon/* --warning-severity=0 ./lib
10 |
11 | fix:
12 | phpcbf --standard=PSR2 --extensions=php --ignore=vendor/*,tests/*,cphalcon/* --warning-severity=0 ./
13 |
14 | test-report:
15 | phpunit -v --coverage-html ./tests/report/ --colors
16 |
17 | test:
18 | phpunit -v --colors
19 |
20 | health:
21 | pdepend --overview-pyramid=tests/report/output.svg src/
--------------------------------------------------------------------------------
/phpdoc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | EvaEngine
4 |
5 | tmp
6 | utf-8
7 |
8 |
9 | docs
10 |
11 |
12 | warn
13 |
14 | docs/log/{DATE}.log
15 | docs/log/{DATE}.errors.log
16 |
17 |
18 |
19 |
20 |
21 |
22 | src
23 |
24 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ./tests
5 |
6 |
7 |
8 |
9 | src/
10 |
11 | vendor/
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/EvaOAuth/AdapterTrait.php:
--------------------------------------------------------------------------------
1 | options;
48 | }
49 |
50 | /**
51 | * @param array $options
52 | */
53 | public static function setHttpClientDefaultOptions(array $options)
54 | {
55 | self::$httpClientDefaultOptions = $options;
56 | }
57 |
58 |
59 | /**
60 | * @return HttpClient
61 | */
62 | public static function getHttpClient()
63 | {
64 | if (self::$httpClient) {
65 | return self::$httpClient;
66 | }
67 |
68 | return self::$httpClient = new HttpClient(self::$httpClientDefaultOptions);
69 | }
70 |
71 | /**
72 | * @param Client $httpClient
73 | */
74 | public static function setHttpClient(Client $httpClient)
75 | {
76 | self::$httpClient = $httpClient;
77 | }
78 |
79 | /**
80 | * @return Cache
81 | */
82 | public function getStorage()
83 | {
84 | return $this->storage;
85 | }
86 |
87 | /**
88 | * @param Cache $storage
89 | */
90 | public function setStorage(Cache $storage)
91 | {
92 | $this->storage = $storage;
93 | }
94 |
95 | /**
96 | * @return Emitter
97 | */
98 | public function getEmitter()
99 | {
100 | if ($this->emitter) {
101 | return $this->emitter;
102 | }
103 | //All adapters share same emitter
104 | return $this->emitter = EventsManager::getEmitter();
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/EvaOAuth/AuthorizedHttpClient.php:
--------------------------------------------------------------------------------
1 | EventsManager::getEmitter()
36 | ]);
37 | parent::__construct($options);
38 |
39 |
40 | if ($token instanceof OAuth2AccessTokenInterface) {
41 | $this->getEmitter()->on(
42 | 'before',
43 | function (BeforeEvent $event) use ($token) {
44 | /** @var \Eva\EvaOAuth\OAuth2\Token\AccessToken $token */
45 | $event->getRequest()->setHeader(
46 | 'Authorization',
47 | $token->getTokenType() . ' ' . $token->getTokenValue()
48 | );
49 | }
50 | );
51 | } else {
52 | $signatureMethod = isset($options['signature_method'])
53 | ? $options['signature_method']
54 | : SignatureInterface::METHOD_HMAC_SHA1;
55 | $signatureClasses = [
56 | SignatureInterface::METHOD_PLAINTEXT => 'Eva\EvaOAuth\OAuth1\Signature\PlainText',
57 | SignatureInterface::METHOD_HMAC_SHA1 => 'Eva\EvaOAuth\OAuth1\Signature\Hmac',
58 | SignatureInterface::METHOD_RSA_SHA1 => 'Eva\EvaOAuth\OAuth1\Signature\Rsa',
59 | ];
60 | if (false === isset($signatureClasses[$signatureMethod])) {
61 | throw new InvalidArgumentException(sprintf(
62 | 'Signature method %s not able to process',
63 | $signatureMethod
64 | ));
65 | }
66 | $signatureClass = $signatureClasses[$signatureMethod];
67 |
68 | $this->getEmitter()->on(
69 | 'before',
70 | function (BeforeEvent $event) use ($token, $signatureClass) {
71 | /** @var Request $request */
72 | $request = $event->getRequest();
73 | /** @var \Eva\EvaOAuth\OAuth1\Token\AccessToken $token */
74 |
75 | $httpMethod = strtoupper($request->getMethod());
76 | $url = Url::fromString($request->getUrl());
77 | $parameters = [
78 | 'oauth_consumer_key' => $token->getConsumerKey(),
79 | 'oauth_signature_method' => SignatureInterface::METHOD_HMAC_SHA1,
80 | 'oauth_timestamp' => (string)time(),
81 | 'oauth_nonce' => strtolower(Text::generateRandomString(32)),
82 | 'oauth_token' => $token->getTokenValue(),
83 | 'oauth_version' => '1.0',
84 | ];
85 |
86 | $signature = (string)new $signatureClass(
87 | $token->getConsumerSecret(),
88 | Text::buildBaseString($httpMethod, $url, $parameters),
89 | $token->getTokenSecret()
90 | );
91 | $parameters['oauth_signature'] = $signature;
92 | $event->getRequest()->setHeader(
93 | 'Authorization',
94 | Text::buildHeaderString($parameters)
95 | );
96 | }
97 | );
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Events/BeforeAuthorize.php:
--------------------------------------------------------------------------------
1 | adapter;
39 | }
40 |
41 | public function getUri()
42 | {
43 | return $this->uri;
44 | }
45 |
46 | public function getRequestToken()
47 | {
48 | return $this->requestToken;
49 | }
50 |
51 | /**
52 | * @param string $uri
53 | * @param Consumer|Client $adapter
54 | * @param RequestToken $requestToken
55 | */
56 | public function __construct($uri, $adapter = null, RequestToken $requestToken = null)
57 | {
58 | $this->adapter = $adapter;
59 | $this->uri = $uri;
60 | $this->requestToken = $requestToken;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Events/BeforeGetAccessToken.php:
--------------------------------------------------------------------------------
1 | request;
47 | }
48 |
49 | /**
50 | * @return Consumer|Client
51 | */
52 | public function getAdapter()
53 | {
54 | return $this->adapter;
55 | }
56 |
57 | /**
58 | * @return ResourceServerInterface|ServiceProviderInterface
59 | */
60 | public function getProvider()
61 | {
62 | return $this->provider;
63 | }
64 |
65 | /**
66 | * @param Request $request
67 | * @param $provider
68 | * @param $adapter
69 | */
70 | public function __construct(Request $request, $provider, $adapter = null)
71 | {
72 | $this->request = $request;
73 | $this->adapter = $adapter;
74 | $this->provider = $provider;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Events/BeforeGetRequestToken.php:
--------------------------------------------------------------------------------
1 | request;
47 | }
48 |
49 | /**
50 | * @return Consumer|Client
51 | */
52 | public function getAdapter()
53 | {
54 | return $this->adapter;
55 | }
56 |
57 | /**
58 | * @return ResourceServerInterface|ServiceProviderInterface
59 | */
60 | public function getProvider()
61 | {
62 | return $this->provider;
63 | }
64 |
65 | /**
66 | * @param Request $request
67 | * @param $provider
68 | * @param $adapter
69 | */
70 | public function __construct(Request $request, $provider, $adapter = null)
71 | {
72 | $this->request = $request;
73 | $this->adapter = $adapter;
74 | $this->provider = $provider;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Events/EventsManager.php:
--------------------------------------------------------------------------------
1 | >>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
46 | const SHORT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';
47 |
48 | /** @var string Template used to format log messages */
49 | private $template;
50 |
51 | /**
52 | * @param string $template Log message template
53 | */
54 | public function __construct($template = self::CLF)
55 | {
56 | $this->template = $template ?: self::CLF;
57 | }
58 |
59 | /**
60 | * Returns a formatted message
61 | *
62 | * @param RequestInterface $request Request that was sent
63 | * @param ResponseInterface $response Response that was received
64 | * @param \Exception $error Exception that was received
65 | * @param array $customData Associative array of custom template data
66 | *
67 | * @return string
68 | */
69 | public function format(
70 | RequestInterface $request,
71 | ResponseInterface $response = null,
72 | \Exception $error = null,
73 | array $customData = []
74 | ) {
75 | $cache = $customData;
76 |
77 | return preg_replace_callback(
78 | '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
79 | function (array $matches) use ($request, $response, $error, &$cache) {
80 |
81 | if (isset($cache[$matches[1]])) {
82 | return $cache[$matches[1]];
83 | }
84 |
85 | $result = '';
86 | switch ($matches[1]) {
87 | case 'request':
88 | $result = $request;
89 | break;
90 | case 'response':
91 | $result = $response;
92 | break;
93 | case 'req_headers':
94 | $result = trim($request->getMethod() . ' '
95 | . $request->getResource()) . ' HTTP/'
96 | . $request->getProtocolVersion() . "\r\n"
97 | . $this->headers($request);
98 | break;
99 | case 'res_headers':
100 | $result = $response ?
101 | sprintf(
102 | 'HTTP/%s %d %s',
103 | $response->getProtocolVersion(),
104 | $response->getStatusCode(),
105 | $response->getReasonPhrase()
106 | ) . "\r\n" . $this->headers($response)
107 | : 'NULL';
108 | break;
109 | case 'req_body':
110 | $result = $request->getBody();
111 | break;
112 | case 'res_body':
113 | $result = $response ? $response->getBody() : 'NULL';
114 | break;
115 | case 'ts':
116 | $result = gmdate('c');
117 | break;
118 | case 'method':
119 | $result = $request->getMethod();
120 | break;
121 | case 'url':
122 | $result = $request->getUrl();
123 | break;
124 | case 'resource':
125 | $result = $request->getResource();
126 | break;
127 | case 'req_version':
128 | $result = $request->getProtocolVersion();
129 | break;
130 | case 'res_version':
131 | $result = $response
132 | ? $response->getProtocolVersion()
133 | : 'NULL';
134 | break;
135 | case 'host':
136 | $result = $request->getHost();
137 | break;
138 | case 'hostname':
139 | $result = gethostname();
140 | break;
141 | case 'code':
142 | $result = $response
143 | ? $response->getStatusCode()
144 | : 'NULL';
145 | break;
146 | case 'phrase':
147 | $result = $response
148 | ? $response->getReasonPhrase()
149 | : 'NULL';
150 | break;
151 | case 'error':
152 | $result = $error ? $error->getMessage() : 'NULL';
153 | break;
154 | default:
155 | // handle prefixed dynamic headers
156 | if (strpos($matches[1], 'req_header_') === 0) {
157 | $result = $request->getHeader(substr($matches[1], 11));
158 | } elseif (strpos($matches[1], 'res_header_') === 0) {
159 | $result = $response
160 | ? $response->getHeader(substr($matches[1], 11))
161 | : 'NULL';
162 | }
163 | }
164 |
165 | $cache[$matches[1]] = $result;
166 | return $result;
167 | },
168 | $this->template
169 | );
170 | }
171 |
172 | private function headers(MessageInterface $message)
173 | {
174 | $result = '';
175 | foreach ($message->getHeaders() as $name => $values) {
176 | $result .= $name . ': ' . implode(', ', $values) . "\r\n";
177 | }
178 |
179 | return trim($result);
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Events/LogSubscriber.php:
--------------------------------------------------------------------------------
1 | ['methodName']]
48 | * - ['eventName' => ['methodName', $priority]]
49 | * - ['eventName' => [['methodName'], ['otherMethod']]
50 | * - ['eventName' => [['methodName'], ['otherMethod', $priority]]
51 | * - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]]
52 | *
53 | * @return array
54 | */
55 | public function getEvents()
56 | {
57 | return [
58 | //Guzzle default events
59 | 'complete' => ['onComplete', RequestEvents::VERIFY_RESPONSE - 10],
60 | 'error' => ['onError', RequestEvents::EARLY],
61 | 'beforeAuthorize' => ['beforeAuthorize'],
62 | //'beforeGetAccessToken' => ['beforeGetAccessToken'],
63 | ];
64 | }
65 |
66 | public function __construct($logger = null, $formatter = null)
67 | {
68 | if ($logger instanceof LoggerInterface) {
69 | $this->logger = $logger;
70 | } else {
71 | $this->logger = new Logger('EvaOAuth');
72 | $this->logger->pushHandler(new StreamHandler($logger, Logger::DEBUG));
73 | }
74 |
75 | $this->formatter = $formatter instanceof Formatter
76 | ? $formatter
77 | : new Formatter($formatter);
78 | }
79 |
80 | public function beforeAuthorize(BeforeAuthorize $beforeAuthorizeEvent)
81 | {
82 | $this->logger->log(LogLevel::INFO, $beforeAuthorizeEvent->getUri());
83 | }
84 |
85 | public function beforeGetRequestToken(BeforeGetRequestToken $beforeGetRequestTokenEvent)
86 | {
87 | }
88 |
89 | public function beforeGetAccessToken(BeforeGetAccessToken $beforeGetAccessTokenEvent)
90 | {
91 | }
92 |
93 | public function onComplete(CompleteEvent $event)
94 | {
95 | $this->logger->log(
96 | LogLevel::INFO,
97 | $this->formatter->format(
98 | $event->getRequest(),
99 | $event->getResponse()
100 | ),
101 | [
102 | 'request' => $event->getRequest(),
103 | 'response' => $event->getResponse()
104 | ]
105 | );
106 | }
107 |
108 | public function onError(ErrorEvent $event)
109 | {
110 | $ex = $event->getException();
111 | $this->logger->log(
112 | LogLevel::CRITICAL,
113 | $this->formatter->format(
114 | $event->getRequest(),
115 | $event->getResponse(),
116 | $ex
117 | ),
118 | [
119 | 'request' => $event->getRequest(),
120 | 'response' => $event->getResponse(),
121 | 'exception' => $ex
122 | ]
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Exception/BadMethodCallException.php:
--------------------------------------------------------------------------------
1 | EventsManager::getEmitter()
27 | ]);
28 | parent::__construct($options);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Consumer.php:
--------------------------------------------------------------------------------
1 | signatureMethod = $signatureMethod;
45 | return $this;
46 | }
47 |
48 | /**
49 | * @return string
50 | */
51 | public function getSignatureMethod()
52 | {
53 | return $this->signatureMethod;
54 | }
55 |
56 | /**
57 | * @param string $signatureMethod
58 | * @return string
59 | */
60 | protected function getSignatureClass($signatureMethod = null)
61 | {
62 | $signatureMethod = $signatureMethod ?: $this->signatureMethod;
63 | $signatureClasses = [
64 | SignatureInterface::METHOD_PLAINTEXT => 'Eva\EvaOAuth\OAuth1\Signature\PlainText',
65 | SignatureInterface::METHOD_HMAC_SHA1 => 'Eva\EvaOAuth\OAuth1\Signature\Hmac',
66 | SignatureInterface::METHOD_RSA_SHA1 => 'Eva\EvaOAuth\OAuth1\Signature\Rsa',
67 | ];
68 | if (false === isset($signatureClasses[$signatureMethod])) {
69 | throw new InvalidArgumentException(sprintf('Signature method %s not able to process', $signatureMethod));
70 | }
71 |
72 | return $signatureClasses[$signatureMethod];
73 | }
74 |
75 | /**
76 | * @param ServiceProviderInterface $serviceProvider
77 | * @return Token\RequestToken
78 | */
79 | public function getRequestToken(ServiceProviderInterface $serviceProvider)
80 | {
81 | $options = $this->options;
82 | $httpMethod = ServiceProviderInterface::METHOD_POST;
83 | $url = $serviceProvider->getRequestTokenUrl();
84 | $parameters = [
85 | 'oauth_consumer_key' => $options['consumer_key'],
86 | 'oauth_signature_method' => $this->signatureMethod,
87 | 'oauth_timestamp' => (string)time(),
88 | 'oauth_nonce' => Text::generateRandomString(32),
89 | 'oauth_version' => '1.0',
90 | 'oauth_callback' => $options['callback'],
91 | ];
92 | $baseString = Text::buildBaseString($httpMethod, $url, $parameters);
93 | $signatureClass = $this->getSignatureClass();
94 | $signature = (string)new $signatureClass(
95 | $baseString,
96 | $options['consumer_secret']
97 | );
98 | $parameters['oauth_signature'] = $signature;
99 |
100 | $httpClient = self::getHttpClient();
101 | $httpClientOptions = [
102 | 'headers' => [
103 | 'X-EvaOAuth-Debug-BaseString' => $baseString, //For debug
104 | 'Authorization' => Text::buildHeaderString($parameters)
105 | ]
106 | ];
107 | $request = $httpClient->createRequest(
108 | $httpMethod,
109 | $url,
110 | $httpClientOptions
111 | );
112 |
113 | try {
114 | $this->getEmitter()->emit(
115 | 'beforeGetRequestToken',
116 | new BeforeGetRequestToken($request, $serviceProvider, $this)
117 | );
118 | /** @var Response $response */
119 | $response = $httpClient->send($request);
120 | return RequestToken::factory($response, $serviceProvider);
121 | } catch (RequestException $e) {
122 | throw new \Eva\EvaOAuth\Exception\RequestException(
123 | 'Get request token failed',
124 | $e->getRequest(),
125 | $e->getResponse()
126 | );
127 | }
128 | }
129 |
130 | /**
131 | * @param ServiceProviderInterface $serviceProvider
132 | * @param RequestToken $requestToken
133 | * @return string
134 | */
135 | public function getAuthorizeUri(ServiceProviderInterface $serviceProvider, RequestToken $requestToken)
136 | {
137 | $authorizeUrl = $serviceProvider->getAuthorizeUrl();
138 | return $authorizeUrl . '?oauth_token=' . $requestToken->getTokenValue();
139 | }
140 |
141 | /**
142 | * @param ServiceProviderInterface $serviceProvider
143 | */
144 | public function requestAuthorize(ServiceProviderInterface $serviceProvider)
145 | {
146 | $requestToken = $this->getRequestToken($serviceProvider);
147 | $this->getStorage()->save(md5($requestToken->getTokenValue()), $requestToken);
148 | $url = $this->getAuthorizeUri($serviceProvider, $requestToken);
149 | $this->getEmitter()->emit('beforeAuthorize', new BeforeAuthorize($url, $this, $requestToken));
150 | header("Location:$url");
151 | }
152 |
153 | /**
154 | * @param ServiceProviderInterface $serviceProvider
155 | * @param array $urlQuery
156 | * @param RequestToken $requestToken
157 | * @return AccessToken
158 | */
159 | public function getAccessToken(
160 | ServiceProviderInterface $serviceProvider,
161 | array $urlQuery = [],
162 | RequestToken $requestToken = null
163 | ) {
164 | $urlQuery = $urlQuery ?: $_GET;
165 | $tokenValue = empty($urlQuery['oauth_token']) ? '' : $urlQuery['oauth_token'];
166 | $tokenVerify = empty($urlQuery['oauth_verifier']) ? '' : $urlQuery['oauth_verifier'];
167 |
168 | if (!$tokenValue || !$tokenVerify) {
169 | throw new InvalidArgumentException(sprintf('No oauth_token or oauth_verifier input'));
170 | }
171 |
172 | /** @var RequestToken $requestToken */
173 | $requestToken = $requestToken ?: $this->getStorage()->fetch(md5($tokenValue));
174 | if (!$requestToken) {
175 | throw new InvalidArgumentException(sprintf('No request token found in storage'));
176 | }
177 |
178 | if ($tokenValue != $requestToken->getTokenValue()) {
179 | throw new VerifyException(sprintf('Request token not match'));
180 | }
181 |
182 | $options = $this->options;
183 | $httpMethod = $serviceProvider->getAccessTokenMethod();
184 | $url = $serviceProvider->getAccessTokenUrl();
185 |
186 | $parameters = [
187 | 'oauth_consumer_key' => $options['consumer_key'],
188 | 'oauth_signature_method' => $this->signatureMethod,
189 | 'oauth_timestamp' => (string)time(),
190 | 'oauth_nonce' => Text::generateRandomString(32),
191 | 'oauth_token' => $tokenValue,
192 | 'oauth_version' => '1.0',
193 | 'oauth_verifier' => $tokenVerify,
194 | 'oauth_callback' => $options['callback'],
195 | ];
196 |
197 | $baseString = Text::buildBaseString($httpMethod, $url, $parameters);
198 | $signatureClass = $this->getSignatureClass();
199 | $signature = (string)new $signatureClass(
200 | $baseString,
201 | $options['consumer_secret'],
202 | $requestToken->getTokenSecret()
203 | );
204 | $parameters['oauth_signature'] = $signature;
205 |
206 | $httpClient = self::getHttpClient();
207 | $httpClientOptions = [
208 | 'headers' => [
209 | 'X-EvaOAuth-Debug-BaseString' => $baseString, //For debug
210 | 'Authorization' => Text::buildHeaderString($parameters)
211 | ],
212 | 'body' => [
213 | 'oauth_verifier' => $tokenVerify
214 | ]
215 | ];
216 |
217 | $request = $httpClient->createRequest(
218 | $httpMethod,
219 | $url,
220 | $httpClientOptions
221 | );
222 |
223 | try {
224 | $this->getEmitter()->emit(
225 | 'beforeGetAccessToken',
226 | new BeforeGetAccessToken($request, $serviceProvider, $this)
227 | );
228 | /** @var Response $response */
229 | $response = $httpClient->send($request);
230 | return AccessToken::factory($response, $serviceProvider, $options);
231 | } catch (RequestException $e) {
232 | throw new \Eva\EvaOAuth\Exception\RequestException(
233 | 'Get access token failed',
234 | $e->getRequest(),
235 | $e->getResponse()
236 | );
237 | }
238 | }
239 |
240 | /**
241 | * @param array $options
242 | * @param Cache $storage
243 | */
244 | public function __construct(array $options, Cache $storage)
245 | {
246 | $options = array_merge([
247 | 'consumer_key' => '',
248 | 'consumer_secret' => '',
249 | 'callback' => '',
250 | 'signature_method' => SignatureInterface::METHOD_HMAC_SHA1,
251 | ], $options);
252 |
253 | if (!$options['consumer_key'] || !$options['consumer_secret'] || !$options['callback']) {
254 | throw new InvalidArgumentException(sprintf("Empty consumer key or secret or callback"));
255 | }
256 | $this->options = $options;
257 | $this->storage = $storage;
258 | $this->signatureMethod = $options['signature_method'];
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Providers/AbstractProvider.php:
--------------------------------------------------------------------------------
1 | providerName) {
62 | return $this->providerName;
63 | }
64 | return $this->providerName = array_pop(explode('\\', __CLASS__));
65 | }
66 |
67 | /**
68 | * @return string
69 | */
70 | public function getRequestTokenUrl()
71 | {
72 | return $this->requestTokenUrl;
73 | }
74 |
75 | /**
76 | * @return string
77 | */
78 | public function getRequestTokenFormat()
79 | {
80 | return $this->requestTokenFormat;
81 | }
82 |
83 | /**
84 | * @return string
85 | */
86 | public function getAuthorizeUrl()
87 | {
88 | return $this->authorizeUrl;
89 | }
90 |
91 | /**
92 | * @return string
93 | */
94 | public function getAccessTokenUrl()
95 | {
96 | return $this->accessTokenUrl;
97 | }
98 |
99 | /**
100 | * @return string
101 | */
102 | public function getAccessTokenMethod()
103 | {
104 | return $this->accessTokenMethod;
105 | }
106 |
107 | /**
108 | * @return string
109 | */
110 | public function getAccessTokenFormat()
111 | {
112 | return $this->accessTokenFormat;
113 | }
114 |
115 | /**
116 | * @param AccessToken $token
117 | * @throws BadMethodCallException
118 | */
119 | public function getUser(AccessToken $token)
120 | {
121 | throw new BadMethodCallException(sprintf("Not supported feature"));
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Providers/Flickr.php:
--------------------------------------------------------------------------------
1 | get('https://api.twitter.com/1.1/account/verify_credentials.json');
48 | $rawUser = $response->json();
49 |
50 | $user = new StandardUser([
51 | 'version' => AccessTokenInterface::VERSION_OAUTH1,
52 | 'provider' => 'Twitter',
53 | 'id' => $rawUser['id'],
54 | 'name' => $rawUser['name'],
55 | 'avatar' => $rawUser['profile_image_url'],
56 | 'extra' => $rawUser,
57 | ]);
58 | return $user;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Providers/Twitter.php:
--------------------------------------------------------------------------------
1 | get('https://api.twitter.com/1.1/account/verify_credentials.json');
48 | $rawUser = $response->json();
49 |
50 | $user = new StandardUser([
51 | 'version' => AccessTokenInterface::VERSION_OAUTH1,
52 | 'provider' => 'Twitter',
53 | 'id' => $rawUser['id'],
54 | 'name' => $rawUser['name'],
55 | 'avatar' => $rawUser['profile_image_url'],
56 | 'extra' => $rawUser,
57 | ]);
58 | return $user;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/ServiceProviderInterface.php:
--------------------------------------------------------------------------------
1 | input, $this->secret . '&' . $this->tokenSecret, true));
44 | }
45 |
46 | /**
47 | * @param $input
48 | * @param $secret
49 | * @param string $tokenSecret
50 | */
51 | public function __construct($input, $secret, $tokenSecret = null)
52 | {
53 | $this->secret = (string) rawurlencode($secret);
54 | $this->input = (string) $input;
55 | $this->tokenSecret = $tokenSecret ? (string) rawurlencode($tokenSecret) : '';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Signature/PlainText.php:
--------------------------------------------------------------------------------
1 | secret . '&' . $this->tokenSecret;
44 | }
45 |
46 | /**
47 | * @param string $input
48 | * @param string $secret
49 | * @param string $tokenSecret
50 | */
51 | public function __construct($input, $secret, $tokenSecret = null)
52 | {
53 | $this->secret = (string) rawurlencode($secret);
54 | $this->tokenSecret = $tokenSecret ? (string) rawurlencode($tokenSecret) : '';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Signature/SignatureInterface.php:
--------------------------------------------------------------------------------
1 | getAccessTokenFormat());
65 | $tokenValue = empty($rawToken['oauth_token']) ? '' : $rawToken['oauth_token'];
66 | $tokenSecret = empty($rawToken['oauth_token_secret']) ? '' : $rawToken['oauth_token_secret'];
67 | $token = new static([
68 | 'consumer_key' => $options['consumer_key'],
69 | 'consumer_secret' => $options['consumer_secret'],
70 | 'token_value' => $tokenValue,
71 | 'token_secret' => $tokenSecret,
72 | ]);
73 | $token->setResponse($response);
74 | foreach ($rawToken as $key => $value) {
75 | $token->$key = $value;
76 | }
77 | return $token;
78 | }
79 |
80 | /**
81 | * @return string
82 | */
83 | public function getTokenSecret()
84 | {
85 | return $this->tokenSecret;
86 | }
87 |
88 | /**
89 | * @return string
90 | */
91 | public function getConsumerKey()
92 | {
93 | return $this->consumerKey;
94 | }
95 |
96 | /**
97 | * @return string
98 | */
99 | public function getConsumerSecret()
100 | {
101 | return $this->consumerSecret;
102 | }
103 |
104 | /**
105 | * @return array
106 | */
107 | public function getExtra()
108 | {
109 | return $this->extra;
110 | }
111 |
112 |
113 | /**
114 | * @param $name
115 | * @param $value
116 | * @return $this
117 | */
118 | public function __set($name, $value)
119 | {
120 | $fieldsMapping = [
121 | 'consumer_key' => 'consumerKey',
122 | 'consumer_secret' => 'consumerSecret',
123 | 'token_value' => 'tokenValue',
124 | 'token_secret' => 'tokenSecret',
125 | ];
126 |
127 | if (array_key_exists($name, $fieldsMapping)) {
128 | $field = $fieldsMapping[$name];
129 | $this->$field = $value;
130 | }
131 |
132 | return $this;
133 | }
134 |
135 |
136 | public function __construct(array $tokenParams)
137 | {
138 | $tokenParams = array_merge([
139 | 'consumer_key' => '',
140 | 'consumer_secret' => '',
141 | 'token_value' => '',
142 | 'token_secret' => '',
143 | ], $tokenParams);
144 |
145 | if (!$tokenParams['consumer_key'] || !$tokenParams['consumer_secret'] ||
146 | !$tokenParams['token_value'] || !$tokenParams['token_secret']
147 | ) {
148 | throw new InvalidArgumentException("No token value input");
149 | }
150 |
151 | foreach ($tokenParams as $key => $value) {
152 | $this->$key = $value;
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Token/AccessTokenInterface.php:
--------------------------------------------------------------------------------
1 | response = $response;
44 | return $this;
45 | }
46 |
47 | /**
48 | * @return Response
49 | */
50 | public function getResponse()
51 | {
52 | return $this->response;
53 | }
54 |
55 | /**
56 | * @param Response $response
57 | * @param ServiceProviderInterface $serviceProvider
58 | * @return RequestTokenInterface
59 | */
60 | public static function factory(Response $response, ServiceProviderInterface $serviceProvider)
61 | {
62 | $rawToken = ResponseParser::parse($response, $serviceProvider->getRequestTokenFormat());
63 | $tokenValue = empty($rawToken['oauth_token']) ? '' : $rawToken['oauth_token'];
64 | $tokenSecret = empty($rawToken['oauth_token_secret']) ? '' : $rawToken['oauth_token_secret'];
65 | //$callbackConfirmed = empty($rawToken['oauth_callback_confirmed']) ? false : true;
66 | //TODO callback confirm handle
67 |
68 | $token = new static($tokenValue, $tokenSecret);
69 | $token->setResponse($response);
70 | return $token;
71 | }
72 |
73 |
74 | /**
75 | * @return string
76 | */
77 | public function getTokenValue()
78 | {
79 | return $this->tokenValue;
80 | }
81 |
82 | /**
83 | * @return array
84 | */
85 | public function getTokenSecret()
86 | {
87 | return $this->tokenSecret;
88 | }
89 |
90 | /**
91 | * @param $tokenValue
92 | * @param $tokenSecret
93 | */
94 | public function __construct($tokenValue, $tokenSecret)
95 | {
96 | if (!$tokenValue || !$tokenSecret) {
97 | throw new InvalidArgumentException("No token value or secret input");
98 | }
99 |
100 | $this->tokenValue = (string)$tokenValue;
101 | $this->tokenSecret = (string)$tokenSecret;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth1/Token/RequestTokenInterface.php:
--------------------------------------------------------------------------------
1 | getUser($token);
74 | }
75 |
76 | /**
77 | * @return string
78 | */
79 | public function getGrantStrategyName()
80 | {
81 | return $this->grantStrategyName;
82 | }
83 |
84 | /**
85 | * @param $grantStrategyName
86 | * @return $this
87 | */
88 | public function changeGrantStrategy($grantStrategyName)
89 | {
90 | if (false === array_key_exists($grantStrategyName, self::$grantStrategyMapping)) {
91 | throw new InvalidArgumentException(sprintf("Input grant strategy %s not exist", $grantStrategyName));
92 | }
93 |
94 | $this->grantStrategyName = $grantStrategyName;
95 | return $this;
96 | }
97 |
98 | /**
99 | * @param $strategyName
100 | * @param $strategyClass
101 | */
102 | public static function registerGrantStrategy($strategyName, $strategyClass)
103 | {
104 | if (!class_exists($strategyClass) ||
105 | !in_array(
106 | 'Eva\EvaOAuth\OAuth2\GrantStrategy\GrantStrategyInterface',
107 | class_implements($strategyClass)
108 | )
109 | ) {
110 | throw new InvalidArgumentException('Register grant strategy failed by unrecognized interface');
111 | }
112 |
113 | self::$grantStrategyMapping[(string)$strategyName] = $strategyClass;
114 | }
115 |
116 | /**
117 | * @return array
118 | */
119 | public static function getGrantStrategyMapping()
120 | {
121 | if (self::$grantStrategyMapping) {
122 | return self::$grantStrategyMapping;
123 | }
124 |
125 | return self::$grantStrategyMapping = [
126 | self::GRANT_AUTHORIZATION_CODE => 'Eva\EvaOAuth\OAuth2\GrantStrategy\AuthorizationCode',
127 | self::GRANT_IMPLICIT => 'Eva\EvaOAuth\OAuth2\GrantStrategy\Implicit',
128 | self::GRANT_PASSWORD => 'Eva\EvaOAuth\OAuth2\GrantStrategy\Password',
129 | self::GRANT_CLIENT_CREDENTIALS => 'Eva\EvaOAuth\OAuth2\GrantStrategy\ClientCredentials',
130 | ];
131 | }
132 |
133 | /**
134 | * @return GrantStrategyInterface
135 | */
136 | public function getGrantStrategy()
137 | {
138 | if ($this->grantStrategy) {
139 | return $this->grantStrategy;
140 | }
141 |
142 | $grantStrategyClass = self::getGrantStrategyMapping()[$this->grantStrategyName];
143 |
144 | /** @var GrantStrategyInterface $grantStrategy */
145 | $grantStrategy = new $grantStrategyClass(self::getHttpClient(), $this->options);
146 |
147 | //Events Propagation
148 | $grantStrategy->getEmitter()->on('beforeAuthorize', function (BeforeAuthorize $event) {
149 | $this->getEmitter()->emit('beforeAuthorize', new BeforeAuthorize($event->getUri(), $this));
150 | });
151 | $grantStrategy->getEmitter()->on('beforeGetAccessToken', function (BeforeGetAccessToken $event) {
152 | $this->getEmitter()->emit(
153 | 'beforeGetAccessToken',
154 | new BeforeGetAccessToken($event->getRequest(), $event->getProvider(), $this)
155 | );
156 | });
157 |
158 | return $this->grantStrategy = $grantStrategy;
159 | }
160 |
161 | /**
162 | * @param AuthorizationServerInterface $authServer
163 | * @return string
164 | */
165 | public function getAuthorizeUri(AuthorizationServerInterface $authServer)
166 | {
167 | return $this->getGrantStrategy()->getAuthorizeUrl($authServer);
168 | }
169 |
170 | /**
171 | * @param AuthorizationServerInterface $authServer
172 | */
173 | public function requestAuthorize(AuthorizationServerInterface $authServer)
174 | {
175 | $uri = $this->getAuthorizeUri($authServer);
176 | $this->getGrantStrategy()->requestAuthorize($authServer, $uri);
177 | }
178 |
179 | /**
180 | * @param ResourceServerInterface $resourceServer
181 | * @return mixed
182 | */
183 | public function getAccessToken(ResourceServerInterface $resourceServer)
184 | {
185 | return $this->getGrantStrategy()->getAccessToken($resourceServer);
186 | }
187 |
188 | /**
189 | * @param array $options
190 | */
191 | public function __construct(array $options)
192 | {
193 | $options = array_merge([
194 | 'client_id' => '',
195 | 'client_secret' => '',
196 | 'redirect_uri' => '',
197 | 'scope' => '',
198 | ], $options);
199 |
200 | if (!$options['client_id'] || !$options['client_secret'] || !$options['redirect_uri']) {
201 | throw new InvalidArgumentException(sprintf("Empty client id or secret or redirect uri"));
202 | }
203 | $this->options = $options;
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth2/GrantStrategy/AuthorizationCode.php:
--------------------------------------------------------------------------------
1 | getAuthorizeUrl($authServer);
56 | $this->getEmitter()->emit('beforeAuthorize', new BeforeAuthorize($url));
57 | header("Location:$url");
58 | }
59 |
60 | /**
61 | * @param AuthorizationServerInterface $authServer
62 | * @return string
63 | */
64 | public function getAuthorizeUrl(AuthorizationServerInterface $authServer)
65 | {
66 | $options = $this->options;
67 | $authorizeQuery = [
68 | 'response_type' => 'code',
69 | 'client_id' => $options['client_id'],
70 | 'redirect_uri' => $options['redirect_uri'],
71 | 'state' => Text::generateRandomString()
72 | ];
73 | if ($options['scope']) {
74 | $authorizeQuery['scope'] = $options['scope'];
75 | }
76 | return $authServer->getAuthorizeUrl() . '?' . http_build_query($authorizeQuery);
77 | }
78 |
79 | /**
80 | * @param ResourceServerInterface $resourceServer
81 | * @param array $urlQuery
82 | * @return \Eva\EvaOAuth\OAuth2\Token\AccessTokenInterface
83 | */
84 | public function getAccessToken(ResourceServerInterface $resourceServer, array $urlQuery = [])
85 | {
86 | $urlQuery = $urlQuery ?: $_GET;
87 | $code = empty($urlQuery['code']) ? '' : $urlQuery['code'];
88 | $state = empty($urlQuery['state']) ? '' : $urlQuery['state'];
89 | $options = $this->options;
90 |
91 | if (!$code) {
92 | throw new InvalidArgumentException("No authorization code found");
93 | }
94 |
95 | //TODO: Valid state to void attach
96 |
97 | $parameters = [
98 | 'grant_type' => 'authorization_code',
99 | 'code' => $code,
100 | 'client_id' => $options['client_id'],
101 | 'client_secret' => $options['client_secret'],
102 | 'redirect_uri' => $options['redirect_uri'],
103 | ];
104 | if ($state) {
105 | $parameters['state'] = $state;
106 | }
107 |
108 | $httpClient = $this->httpClient;
109 |
110 | $method = $resourceServer->getAccessTokenMethod();
111 | $httpClientOptions = ($method == ResourceServerInterface::METHOD_GET) ?
112 | ['query' => $parameters] :
113 | ['body' => $parameters];
114 |
115 | $request = $httpClient->createRequest(
116 | $method,
117 | $resourceServer->getAccessTokenUrl(),
118 | $httpClientOptions
119 | );
120 |
121 | try {
122 | $this->getEmitter()->emit('beforeGetAccessToken', new BeforeGetAccessToken($request, $resourceServer));
123 | /** @var Response $response */
124 | $response = $httpClient->send($request);
125 | return AccessToken::factory($response, $resourceServer);
126 | } catch (RequestException $e) {
127 | throw new \Eva\EvaOAuth\Exception\RequestException(
128 | 'Get access token failed',
129 | $e->getRequest(),
130 | $e->getResponse()
131 | );
132 | }
133 | }
134 |
135 | /**
136 | * @param HttpClient $httpClient
137 | * @param array $options
138 | * @param Cache $storage
139 | */
140 | public function __construct(HttpClient $httpClient, array $options, Cache $storage = null)
141 | {
142 | $this->httpClient = $httpClient;
143 | $this->options = $options;
144 | $this->storage = $storage;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth2/GrantStrategy/GrantStrategyInterface.php:
--------------------------------------------------------------------------------
1 | providerName) {
52 | return $this->providerName;
53 | }
54 | return $this->providerName = array_pop(explode('\\', __CLASS__));
55 | }
56 |
57 | /**
58 | * @return string
59 | */
60 | public function getAuthorizeUrl()
61 | {
62 | return $this->authorizeUrl;
63 | }
64 |
65 | /**
66 | * @return string
67 | */
68 | public function getAccessTokenUrl()
69 | {
70 | return $this->accessTokenUrl;
71 | }
72 |
73 | /**
74 | * @return string
75 | */
76 | public function getAccessTokenMethod()
77 | {
78 | return $this->accessTokenMethod;
79 | }
80 |
81 | /**
82 | * @return string
83 | */
84 | public function getAccessTokenFormat()
85 | {
86 | return $this->accessTokenFormat;
87 | }
88 |
89 | /**
90 | * @param AccessToken $token
91 | * @return \Eva\EvaOAuth\User\UserInterface
92 | */
93 | public function getUser(AccessToken $token)
94 | {
95 | throw new BadMethodCallException(sprintf("Not supported feature"));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth2/Providers/Douban.php:
--------------------------------------------------------------------------------
1 | get('https://api.douban.com/v2/user/~me');
43 | $rawUser = $response->json();
44 |
45 | $user = new StandardUser([
46 | 'version' => AccessTokenInterface::VERSION_OAUTH2,
47 | 'provider' => 'Douban',
48 | 'id' => $rawUser['id'],
49 | 'name' => $rawUser['uid'],
50 | 'avatar' => $rawUser['avatar'],
51 | 'extra' => $rawUser,
52 | ]);
53 | return $user;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth2/Providers/Facebook.php:
--------------------------------------------------------------------------------
1 | getAccessTokenFormat());
64 | $tokenValue = empty($rawToken['access_token']) ? '' : $rawToken['access_token'];
65 | $token = new static($tokenValue);
66 | $token->setResponse($response);
67 | foreach ($rawToken as $key => $value) {
68 | $token->$key = $value;
69 | }
70 | return $token;
71 | }
72 |
73 | /**
74 | * @return int
75 | */
76 | public function getExpireTimestamp()
77 | {
78 | return $this->expireTimestamp;
79 | }
80 |
81 | /**
82 | * @return string
83 | */
84 | public function getRefreshToken()
85 | {
86 | return $this->refreshToken;
87 | }
88 |
89 | /**
90 | * @return string
91 | */
92 | public function getScope()
93 | {
94 | return $this->scope;
95 | }
96 |
97 | /**
98 | * @return array
99 | */
100 | public function getExtra()
101 | {
102 | return $this->extra;
103 | }
104 |
105 | /**
106 | * @param $name
107 | * @param $value
108 | * @return $this
109 | */
110 | public function __set($name, $value)
111 | {
112 | $fieldsMapping = [
113 | 'access_token' => 'tokenValue',
114 | 'refresh_token' => 'refreshToken',
115 | ];
116 |
117 | if (array_key_exists($name, $fieldsMapping)) {
118 | $field = $fieldsMapping[$name];
119 | $this->$field = $value;
120 | } else {
121 | if ($name === 'expires_in') {
122 | $this->expireTimestamp = $value + time();
123 | } else {
124 | $this->extra[$name] = $value;
125 | }
126 | }
127 | return $this;
128 | }
129 |
130 |
131 | /**
132 | * @param string $tokenValue
133 | * @param array $tokenArray
134 | */
135 | public function __construct($tokenValue, array $tokenArray = array())
136 | {
137 | $this->tokenValue = (string) $tokenValue;
138 | if (!$this->tokenValue) {
139 | throw new InvalidArgumentException("No token value input");
140 | }
141 |
142 | foreach ($tokenArray as $key => $value) {
143 | $this->$key = $value;
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/EvaOAuth/OAuth2/Token/AccessTokenInterface.php:
--------------------------------------------------------------------------------
1 | 'Eva\EvaOAuth\OAuth1\Providers\Twitter',
64 | 'flickr' => 'Eva\EvaOAuth\OAuth1\Providers\Flickr',
65 | 'facebook' => 'Eva\EvaOAuth\OAuth2\Providers\Facebook',
66 | 'douban' => 'Eva\EvaOAuth\OAuth2\Providers\Douban',
67 | 'tencent' => 'Eva\EvaOAuth\OAuth2\Providers\Tencent',
68 | 'weibo' => 'Eva\EvaOAuth\OAuth2\Providers\Weibo',
69 | 'hundsun' => 'Eva\EvaOAuth\OAuth2\Providers\Hundsun',
70 | ];
71 |
72 | /**
73 | * @var string
74 | */
75 | protected static $storageClass = '';
76 |
77 | /**
78 | * @var array
79 | */
80 | protected static $storageOptions = [
81 | ];
82 |
83 | /**
84 | * @var Cache
85 | */
86 | protected static $storage;
87 |
88 | /**
89 | * @param string $name
90 | * @param string $class
91 | */
92 | public static function registerProvider($name, $class)
93 | {
94 | self::$providers[$name] = $class;
95 | }
96 |
97 | /**
98 | * @param array $classes
99 | */
100 | public static function registerProviders(array $classes)
101 | {
102 | foreach ($classes as $name => $class) {
103 | self::$providers[$name] = $class;
104 | }
105 | }
106 |
107 | /**
108 | * @return array
109 | */
110 | public static function getProviders()
111 | {
112 | return self::$providers;
113 | }
114 |
115 | /**
116 | * @return Cache
117 | */
118 | public static function getStorage()
119 | {
120 | if (self::$storage) {
121 | return self::$storage;
122 | }
123 |
124 | return self::$storage = new FilesystemCache(__DIR__ . '/../../tmp/');
125 | }
126 |
127 | /**
128 | * @param Cache $storage
129 | */
130 | public static function setStorage(Cache $storage)
131 | {
132 | self::$storage = $storage;
133 | }
134 |
135 | /**
136 | * @return OAuth1Provider|OAuth2Provider
137 | */
138 | public function getProvider()
139 | {
140 | return $this->provider;
141 | }
142 |
143 | /**
144 | * @return Consumer|Client
145 | */
146 | public function getAdapter()
147 | {
148 | if ($this->version === self::OAUTH_VERSION_2) {
149 | return $this->client;
150 | }
151 | return $this->consumer;
152 | }
153 |
154 | /**
155 | * @return \GuzzleHttp\Client
156 | */
157 | public function getHttpClient()
158 | {
159 | $adapter = $this->getAdapter();
160 | return $adapter::getHttpClient();
161 | }
162 |
163 | /**
164 | * @return \GuzzleHttp\Event\Emitter
165 | */
166 | public function getEmitter()
167 | {
168 | return $this->getAdapter()->getEmitter();
169 | }
170 |
171 | /**
172 | * @return string
173 | */
174 | public function getAuthorizeUri()
175 | {
176 | $adapter = $this->getAdapter();
177 | if ($this->version === self::OAUTH_VERSION_2) {
178 | return $adapter->getAuthorizeUri($this->provider);
179 | }
180 | $requestToken = $adapter->getRequestToken($this->provider);
181 | return $adapter->getAuthorizeUri($this->provider, $requestToken);
182 | }
183 |
184 | /**
185 | * Redirect to authorize url
186 | */
187 | public function requestAuthorize()
188 | {
189 | $this->getAdapter()->requestAuthorize($this->provider);
190 | }
191 |
192 | /**
193 | * @return Token\AccessTokenInterface
194 | */
195 | public function getAccessToken()
196 | {
197 | return $this->getAdapter()->getAccessToken($this->provider);
198 | }
199 |
200 | /**
201 | * To compatible with old version
202 | * @return array
203 | */
204 | public function getTokenAndUser()
205 | {
206 | $adapter = $this->getAdapter();
207 |
208 | $isOAuth2 = $this->version === self::OAUTH_VERSION_2;
209 | $token = $adapter->getAccessToken($this->provider);
210 | $user = $this->provider->getUser($token);
211 |
212 | return [
213 | 'adapterKey' => $this->provider->getProviderName(),
214 | 'token' => $token->getTokenValue(),
215 | 'version' => $token->getTokenVersion(),
216 | 'scope' => $isOAuth2 ? $token->getScope() : null,
217 | 'refreshToken' => $isOAuth2 ? $token->getRefreshToken() : null,
218 | 'expireTime' => $isOAuth2 ? gmdate('Y-m-d H:i:s', $token->getExpireTimestamp()) : null,
219 | 'remoteUserId' => $user->getId(),
220 | 'remoteUserName' => $user->getName(),
221 | 'remoteEmail' => $user->getEmail(),
222 | 'remoteImageUrl' => $user->getAvatar(),
223 | 'remoteExtra' => json_encode($user->getExtra()),
224 | ];
225 | }
226 |
227 | /**
228 | * @param array $options
229 | * @param $version
230 | * @return array
231 | */
232 | public function convertOptions(array $options, $version)
233 | {
234 | if ($version === self::OAUTH_VERSION_2) {
235 | return array_merge([
236 | 'client_id' => $options['key'],
237 | 'client_secret' => $options['secret'],
238 | 'redirect_uri' => $options['callback'],
239 | ], $options);
240 | }
241 | return array_merge([
242 | 'consumer_key' => $options['key'],
243 | 'consumer_secret' => $options['secret'],
244 | 'callback' => $options['callback'],
245 | ], $options);
246 | }
247 |
248 | /**
249 | * Enable debug mode
250 | * Guzzle will print all request and response on screen
251 | * @param $logPath
252 | * @return $this
253 | */
254 | public function debug($logPath)
255 | {
256 | $adapter = $this->getAdapter();
257 | $adapter->getEmitter()->attach(new LogSubscriber($logPath, Formatter::DEBUG));
258 | return $this;
259 | }
260 |
261 | /**
262 | * @param string $providerName
263 | * @param array $options
264 | */
265 | public function __construct($providerName, array $options)
266 | {
267 | $providerName = strtolower($providerName);
268 | if (false === isset(self::$providers[$providerName])) {
269 | throw new BadMethodCallException(sprintf('Provider %s not found', $providerName));
270 | }
271 |
272 | $options = array_merge([
273 | 'key' => '',
274 | 'secret' => '',
275 | 'callback' => '',
276 | ], $options);
277 |
278 | $providerClass = self::$providers[$providerName];
279 | $implements = class_implements($providerClass);
280 | if (true === in_array('Eva\EvaOAuth\OAuth2\ResourceServerInterface', $implements)) {
281 | $options = $this->convertOptions($options, self::OAUTH_VERSION_2);
282 | $this->client = new Client($options);
283 | $this->version = self::OAUTH_VERSION_2;
284 | $this->provider = new $providerClass();
285 | } elseif (true === in_array('Eva\EvaOAuth\OAuth1\ServiceProviderInterface', $implements)) {
286 | $options = $this->convertOptions($options, self::OAUTH_VERSION_1);
287 | $this->version = self::OAUTH_VERSION_1;
288 | $this->provider = new $providerClass();
289 | $this->consumer = new Consumer($options, self::getStorage());
290 | } else {
291 | throw new InvalidArgumentException(sprintf("Class %s is not correct oauth adapter", $providerClass));
292 | }
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Token/AccessTokenInterface.php:
--------------------------------------------------------------------------------
1 | response = $response;
36 | return $this;
37 | }
38 |
39 | /**
40 | * @return Response
41 | */
42 | public function getResponse()
43 | {
44 | return $this->response;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function getTokenVersion()
51 | {
52 | return $this->tokenVersion;
53 | }
54 |
55 | /**
56 | * @return string
57 | */
58 | public function getTokenValue()
59 | {
60 | return $this->tokenValue;
61 | }
62 |
63 | /**
64 | * @return string
65 | */
66 | public function getTokenType()
67 | {
68 | return $this->tokenType;
69 | }
70 |
71 | /**
72 | * @param string $tokenType
73 | * @return $this
74 | */
75 | public function setTokenType($tokenType)
76 | {
77 | $this->tokenType = (string) $tokenType;
78 | return $this;
79 | }
80 |
81 | /**
82 | * @return array
83 | */
84 | public function toArray()
85 | {
86 | $res = [];
87 | foreach ($this as $key => $value) {
88 | if (true === is_scalar($value) || true === is_array($value)) {
89 | $res[$key] = $value;
90 | }
91 | }
92 | return $res;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/EvaOAuth/User/StandardUser.php:
--------------------------------------------------------------------------------
1 | version;
57 | }
58 |
59 | /**
60 | * @return string
61 | */
62 | public function getProvider()
63 | {
64 | return $this->provider;
65 | }
66 |
67 | /**
68 | * @return string
69 | */
70 | public function getId()
71 | {
72 | return $this->id;
73 | }
74 |
75 | /**
76 | * @return string
77 | */
78 | public function getName()
79 | {
80 | return $this->name;
81 | }
82 |
83 | /**
84 | * @return string
85 | */
86 | public function getAvatar()
87 | {
88 | return $this->avatar;
89 | }
90 |
91 | /**
92 | * @return string
93 | */
94 | public function getEmail()
95 | {
96 | return $this->email;
97 | }
98 |
99 | /**
100 | * @return array
101 | */
102 | public function getExtra()
103 | {
104 | return $this->extra;
105 | }
106 |
107 | /**
108 | * @return array
109 | */
110 | public function toArray()
111 | {
112 | $res = [];
113 | foreach ($this as $key => $value) {
114 | if (true === is_scalar($value) || true === is_array($value)) {
115 | $res[$key] = $value;
116 | }
117 | }
118 | return $res;
119 | }
120 |
121 | /**
122 | * @param array $info
123 | */
124 | public function __construct(array $info = [])
125 | {
126 | if ($info) {
127 | foreach ($info as $key => $value) {
128 | $this->$key = $value;
129 | }
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/EvaOAuth/User/UserInterface.php:
--------------------------------------------------------------------------------
1 | json();
28 | }
29 |
30 | /**
31 | * @param Response $response
32 | * @return array
33 | */
34 | public static function parseJSONP(Response $response)
35 | {
36 | $responseBody = $response->getBody();
37 |
38 | $lpos = strpos($responseBody, "(");
39 | $rpos = strrpos($responseBody, ")");
40 | $responseBody = substr($responseBody, $lpos + 1, $rpos - $lpos - 1);
41 | return json_decode($responseBody, true);
42 | }
43 |
44 | /**
45 | * @param Response $response
46 | * @return array
47 | */
48 | public static function parseQuery(Response $response)
49 | {
50 | $responseBody = $response->getBody();
51 | $params = [];
52 | parse_str($responseBody, $params);
53 | return $params;
54 | }
55 |
56 | /**
57 | * @param Response $response
58 | * @param string $format
59 | * @return array
60 | * @throws InvalidArgumentException
61 | */
62 | public static function parse(Response $response, $format = ResourceServerInterface::FORMAT_JSON)
63 | {
64 | switch ($format) {
65 | case ResourceServerInterface::FORMAT_JSON:
66 | return self::parseJSON($response);
67 | case ResourceServerInterface::FORMAT_JSONP:
68 | return self::parseJSONP($response);
69 | case ResourceServerInterface::FORMAT_QUERY:
70 | return self::parseQuery($response);
71 | default:
72 | throw new InvalidArgumentException(sprintf("Not able to parse format %s", $format));
73 |
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/EvaOAuth/Utils/Text.php:
--------------------------------------------------------------------------------
1 | $value) {
18 | $encodedHeader[] = $key . '="' . urlencode($value) . '"';
19 | }
20 | return 'OAuth ' . implode(', ', $encodedHeader);
21 | }
22 |
23 | public static function generateRandomString($length = 10)
24 | {
25 | $length = (int) $length;
26 | $length = $length > 0 ? $length : 10;
27 | return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
28 | }
29 |
30 | public static function buildBaseString($method, $url, array $params)
31 | {
32 | ksort($params);
33 |
34 | $encodedParams = [];
35 | foreach ($params as $key => $value) {
36 | $encodedParams[] = urlencode($key) . '=' . urlencode($value);
37 | }
38 | return implode('&', [
39 | strtoupper($method),
40 | urlencode($url),
41 | urlencode(implode('&', $encodedParams))
42 | ]);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Bootstrap.php:
--------------------------------------------------------------------------------
1 | addPsr4('Eva\EvaOAuthTest\\', __DIR__ . '/EvaOAuthTest');
6 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/AuthorizedHttpClientTest.php:
--------------------------------------------------------------------------------
1 | createRequest('GET', 'http://baidu.com');
25 | $client->getEmitter()->attach(
26 | new Mock([
27 | new Response(200, [], Stream::factory('some response')),
28 | ])
29 | );
30 | $client->send($request);
31 | $this->assertEquals('Bearer foo', $request->getHeader('Authorization'));
32 | }
33 |
34 | public function testOAuth1Header()
35 | {
36 | /** @var Client $client */
37 | $client = new AuthorizedHttpClient(new OAuth1AccessToken([
38 | 'consumer_key' => 'test_consumer_key',
39 | 'consumer_secret' => 'test_consumer_secret',
40 | 'token_value' => 'test_token_value',
41 | 'token_secret' => 'test_token_secret',
42 | ]));
43 | $request = $client->createRequest('GET', 'http://baidu.com');
44 | $client->getEmitter()->attach(
45 | new Mock([
46 | new Response(200, [], Stream::factory('some response')),
47 | ])
48 | );
49 | $client->send($request);
50 | $this->assertStringStartsWith('OAuth', $request->getHeader('Authorization'));
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/Events/FormatterTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(Formatter::CLF, $this->readAttribute($f, 'template'));
17 | $f = new Formatter(null);
18 | $this->assertEquals(Formatter::CLF, $this->readAttribute($f, 'template'));
19 | }
20 |
21 | public function testFormatsMessagesWithCustomData()
22 | {
23 | $f = new Formatter('{foo} - {method} - {code}');
24 | $request = new Request('GET', '/');
25 | $response = new Response(200);
26 | $result = $f->format($request, $response, null, ['foo' => 'bar']);
27 | $this->assertEquals('bar - GET - 200', $result);
28 | }
29 |
30 | public function testFormatsTimestamps()
31 | {
32 | $f = new Formatter('{ts}');
33 | $request = new Request('GET', '/');
34 | $result = $f->format($request);
35 | // Ensure it matches this format: '2014-03-02T00:18:41+00:00';
36 | $this->assertEquals(1, preg_match('/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/', $result));
37 | }
38 |
39 | public function formatProvider()
40 | {
41 | $request = new Request('PUT', '/', ['x-test' => 'abc'], Stream::factory('foo'));
42 | $response = new Response(200, ['X-Baz' => 'Bar'], Stream::factory('baz'));
43 | $err = new RequestException('Test', $request, $response);
44 |
45 | return [
46 | ['{request}', [$request], (string) $request],
47 | ['{response}', [$request, $response], (string) $response],
48 | ['{request} {response}', [$request, $response], $request . ' ' . $response],
49 | // Empty response yields no value
50 | ['{request} {response}', [$request], $request . ' '],
51 | ['{req_headers}', [$request], "PUT / HTTP/1.1\r\nx-test: abc"],
52 | ['{res_headers}', [$request, $response], "HTTP/1.1 200 OK\r\nX-Baz: Bar"],
53 | ['{res_headers}', [$request], 'NULL'],
54 | ['{req_body}', [$request], 'foo'],
55 | ['{res_body}', [$request, $response], 'baz'],
56 | ['{res_body}', [$request], 'NULL'],
57 | ['{method}', [$request], $request->getMethod()],
58 | ['{url}', [$request], $request->getUrl()],
59 | ['{resource}', [$request], $request->getResource()],
60 | ['{req_version}', [$request], $request->getProtocolVersion()],
61 | ['{res_version}', [$request, $response], $response->getProtocolVersion()],
62 | ['{res_version}', [$request], 'NULL'],
63 | ['{host}', [$request], $request->getHost()],
64 | ['{hostname}', [$request, $response], gethostname()],
65 | ['{hostname}{hostname}', [$request, $response], gethostname() . gethostname()],
66 | ['{code}', [$request, $response], $response->getStatusCode()],
67 | ['{code}', [$request], 'NULL'],
68 | ['{phrase}', [$request, $response], $response->getReasonPhrase()],
69 | ['{phrase}', [$request], 'NULL'],
70 | ['{error}', [$request, $response, $err], 'Test'],
71 | ['{error}', [$request], 'NULL'],
72 | ['{req_header_x-test}', [$request], 'abc'],
73 | ['{req_header_x-not}', [$request], ''],
74 | ['{res_header_X-Baz}', [$request, $response], 'Bar'],
75 | ['{res_header_x-not}', [$request, $response], ''],
76 | ['{res_header_X-Baz}', [$request], 'NULL'],
77 | ];
78 | }
79 |
80 | /**
81 | * @dataProvider formatProvider
82 | */
83 | public function testFormatsMessages($template, $args, $result)
84 | {
85 | $f = new Formatter($template);
86 | $this->assertEquals((string) $result, call_user_func_array(array($f, 'format'), $args));
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/Events/LogSubscriberTest.php:
--------------------------------------------------------------------------------
1 | getEmitter()->attach($logger);
19 | $client->getEmitter()->attach(new Mock([new Response(200)]));
20 | $client->get('http://httbin.org/get');
21 | rewind($resource);
22 | $this->assertNotFalse(strpos(stream_get_contents($resource), '"GET /get " 200'));
23 | fclose($resource);
24 | }
25 |
26 | public function testLogsAfterError()
27 | {
28 | $resource = fopen('php://temp', 'r+');
29 | $logger = new LogSubscriber($resource, Formatter::CLF);
30 | $client = new Client();
31 | $client->getEmitter()->attach($logger);
32 | $client->getEmitter()->attach(new Mock([new Response(500)]));
33 | try {
34 | $client->get('http://httbin.org/get');
35 | } catch (\Exception $e) {
36 | }
37 | rewind($resource);
38 | $this->assertNotFalse(strpos(stream_get_contents($resource), 'CRITICAL'));
39 | fclose($resource);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth1/ConsumerTest.php:
--------------------------------------------------------------------------------
1 | shouldReceive('fetch')
45 | ->andReturn(new RequestToken('testvalue', 'testsecret'))
46 | ->getMock();
47 | $consumer = new Consumer([
48 | 'consumer_key' => 'test_consumer_key',
49 | 'consumer_secret' => 'test_consumer_secret',
50 | 'callback' => 'http://test_callback/'
51 | ], $mockCache);
52 |
53 | $this->mockCache = $mockCache;
54 |
55 | $this->mock = new Mock();
56 |
57 | $consumer::setHttpClient(new Client());
58 | $httpClient = $consumer::getHttpClient();
59 | $this->consumer = $consumer;
60 | $httpClient->getEmitter()->attach($this->mock);
61 | }
62 |
63 | /**
64 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
65 | */
66 | public function testConstruct()
67 | {
68 | new Consumer([], $this->mockCache);
69 | }
70 |
71 | public function testGetRequestToken()
72 | {
73 | $this->mock->addResponse(
74 | new Response(200, [], Stream::factory(
75 | 'oauth_token=test_request_token&oauth_token_secret=test_request_token_secret&oauth_callback_confirmed=true'
76 | ))
77 | );
78 | $requestToken = $this->consumer->getRequestToken(new Twitter());
79 | $this->assertEquals('test_request_token', $requestToken->getTokenValue());
80 | $this->assertEquals('test_request_token_secret', $requestToken->getTokenSecret());
81 | }
82 |
83 | /**
84 | * @expectedException Eva\EvaOAuth\Exception\RequestException
85 | */
86 | public function testGetRequestTokenFailed()
87 | {
88 | $this->mock->addResponse(
89 | new Response(400, [], Stream::factory('error happened'))
90 | );
91 | $requestToken = $this->consumer->getRequestToken(new Twitter());
92 | }
93 |
94 | public function testGetAccessToken()
95 | {
96 | $this->mock->addResponse(
97 | new Response(200, [], Stream::factory('oauth_token=test_access_token&oauth_token_secret=test_token_secret'))
98 | );
99 | $accessToken = $this->consumer->getAccessToken(new Twitter(), [
100 | 'oauth_token' => 'test_request_token',
101 | 'oauth_verifier' => 'test_request_token_verifier',
102 | ], new RequestToken('test_request_token', 'test_request_token_secret'));
103 | $this->assertEquals('test_access_token', $accessToken->getTokenValue());
104 | $this->assertEquals('test_token_secret', $accessToken->getTokenSecret());
105 | }
106 |
107 | /**
108 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
109 | */
110 | public function testGetAccessTokenInput()
111 | {
112 | $this->consumer->getAccessToken(new Twitter(), []);
113 | }
114 |
115 | /**
116 | * @expectedException Eva\EvaOAuth\Exception\VerifyException
117 | */
118 | public function testGetAccessTokenNotMatch()
119 | {
120 | $this->consumer->getAccessToken(new Twitter(), [
121 | 'oauth_token' => 'test_request_token',
122 | 'oauth_verifier' => 'test_request_token_verifier',
123 | ]);
124 | }
125 |
126 | /**
127 | * @expectedException Eva\EvaOAuth\Exception\RequestException
128 | */
129 | public function testGetAccessTokenFailed()
130 | {
131 | $this->mock->addResponse(
132 | new Response(400, [], Stream::factory('error happened'))
133 | );
134 | $this->consumer->getAccessToken(new Twitter(), [
135 | 'oauth_token' => 'test_request_token',
136 | 'oauth_verifier' => 'test_request_token_verifier',
137 | ], new RequestToken('test_request_token', 'test_request_token_secret'));
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth1/Signature/HmacTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('1Gv6XVo5dKoJ5IyyZxusyQDxk1U=', (string) new Hmac(
23 | Text::buildBaseString('post', 'https://api.twitter.com/oauth/request_token', [
24 | 'oauth_consumer_key' => 'X6vZ7YDHiod0hUyTQj0Gw',
25 | 'oauth_signature_method' => 'HMAC-SHA1',
26 | 'oauth_timestamp' => '1428979350',
27 | 'oauth_nonce' => 'ddb73c89364451560652f53bcd8f14f7',
28 | 'oauth_version' => '1.0',
29 | ]),
30 | '8Ap6YGs9BchvEFAOn6iw43jsjMKE48y3SDfacPyFTuI'
31 | ));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth1/Signature/PlainTextTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('secret&', (string) new PlainText(
24 | 'input',
25 | 'secret'
26 | ));
27 |
28 | $this->assertEquals('secret&token_secret', (string) new PlainText(
29 | 'input',
30 | 'secret',
31 | 'token_secret'
32 | ));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth1/Token/AccessTokenTest.php:
--------------------------------------------------------------------------------
1 | 'test_consumer_key',
25 | 'consumer_secret' => 'test_consumer_secret',
26 | ]
27 | );
28 | $this->assertEquals(AccessTokenInterface::VERSION_OAUTH1, $token->getTokenVersion());
29 | $this->assertEquals('test_consumer_key', $token->getConsumerKey());
30 | $this->assertEquals('test_consumer_secret', $token->getConsumerSecret());
31 | $this->assertEquals('test_token_value', $token->getTokenValue());
32 | $this->assertEquals('test_token_secret', $token->getTokenSecret());
33 | $this->assertInstanceOf('GuzzleHttp\Message\Response', $token->getResponse());
34 | }
35 |
36 | /**
37 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
38 | */
39 | public function testConstruct()
40 | {
41 | new AccessToken([]);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth1/Token/RequestTokenTest.php:
--------------------------------------------------------------------------------
1 | client = new Client(array(
18 | 'client_id' => 'foo',
19 | 'client_secret' => 'bar',
20 | 'redirect_uri' => 'test',
21 | ));
22 | }
23 |
24 | /**
25 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
26 | */
27 | public function testConstruct()
28 | {
29 | new Client(array());
30 | }
31 |
32 | public function testClientTimeout()
33 | {
34 | $this->assertInstanceOf('GuzzleHttp\Client', Client::getHttpClient());
35 | }
36 |
37 | public function testDefaultGrantStrategy()
38 | {
39 | $this->assertEquals(Client::GRANT_AUTHORIZATION_CODE, $this->client->getGrantStrategyName());
40 | $this->assertInstanceOf('Eva\EvaOAuth\OAuth2\GrantStrategy\AuthorizationCode', $this->client->getGrantStrategy());
41 | $this->assertEquals(4, count(Client::getGrantStrategyMapping()));
42 | }
43 |
44 | /**
45 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
46 | */
47 | public function testChangeGrant()
48 | {
49 | $this->client->changeGrantStrategy('foo');
50 | }
51 |
52 | /**
53 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
54 | */
55 | public function testRegisterGrant()
56 | {
57 | $this->client->registerGrantStrategy('foo', 'bar');
58 | }
59 |
60 | public function testRegisterAndChangeGrant()
61 | {
62 | \Mockery::namedMock('FooStrategy', 'Eva\EvaOAuth\OAuth2\GrantStrategy\GrantStrategyInterface');
63 | $this->client->registerGrantStrategy('foo', 'FooStrategy');
64 | $this->client->changeGrantStrategy('foo');
65 | $this->assertEquals('foo', $this->client->getGrantStrategyName());
66 | //$this->assertInstanceOf('Eva\EvaOAuth\OAuth2\GrantStrategy\GrantStrategyInterface', $this->client->getGrantStrategy());
67 | }
68 |
69 | public function testDefaultHttpClient()
70 | {
71 | Client::setHttpClientDefaultOptions([
72 | 'timeout' => 3,
73 | ]);
74 | $this->assertInstanceOf('Eva\EvaOAuth\HttpClient', Client::getHttpClient());
75 | //$this->assertEquals(3, Client::getHttpClient()->getDefaultOption());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth2/GrantStrategy/AuthorizationCodeTest.php:
--------------------------------------------------------------------------------
1 | httpClient = new Client();
33 | $this->grant = new AuthorizationCode($this->httpClient, [
34 | 'client_id' => 'test_client_id',
35 | 'client_secret' => 'test_client_secret',
36 | 'redirect_uri' => 'test_redirect_uri',
37 | 'scope' => 'test_scope',
38 | ]);
39 | }
40 |
41 | public function testAuthorizeUrl()
42 | {
43 | $authServer = new Douban();
44 | $url = $this->grant->getAuthorizeUrl($authServer);
45 | $urlParts = parse_url($url);
46 | parse_str($urlParts['query'], $query);
47 |
48 | $this->assertEquals('www.douban.com', $urlParts['host']);
49 | $this->assertEquals('/service/auth2/auth', $urlParts['path']);
50 | $this->assertEquals('test_client_id', $query['client_id']);
51 | $this->assertEquals('test_redirect_uri', $query['redirect_uri']);
52 | $this->assertEquals('test_scope', $query['scope']);
53 | $this->assertEquals('code', $query['response_type']);
54 | $this->assertEquals(10, strlen($query['state']));
55 | }
56 |
57 | /**
58 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
59 | */
60 | public function testAccessTokenWithNoCode()
61 | {
62 | $this->grant->getAccessToken(new Douban());
63 | }
64 |
65 | /**
66 | * @expectedException Eva\EvaOAuth\Exception\RequestException
67 | */
68 | public function testGetAccessTokenFailed()
69 | {
70 | $this->httpClient->getEmitter()->attach(
71 | new Mock([
72 | new Response(400, ['Content-Type' => 'javascript'], Stream::factory('{"error":"some_error"}')),
73 | ])
74 | );
75 | $this->grant->getAccessToken(new Douban(), [
76 | 'code' => 'test_code'
77 | ]);
78 | }
79 |
80 | public function testGetAccessToken()
81 | {
82 | $this->httpClient->getEmitter()->attach(
83 | new Mock([
84 | new Response(200, ['Content-Type' => 'javascript'], Stream::factory('{"access_token":"test_access_token"}')),
85 | ])
86 | );
87 | $token = $this->grant->getAccessToken(new Douban(), [
88 | 'code' => 'test_code',
89 | 'state' => 'test_state',
90 | ]);
91 | $this->assertEquals('test_access_token', $token->getTokenValue());
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/OAuth2/Token/AccessTokenTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(AccessTokenInterface::VERSION_OAUTH2, $token->getTokenVersion());
25 | $this->assertEquals('test_token_value', $token->getTokenValue());
26 | $this->assertEquals('test_refresh_token', $token->getRefreshToken());
27 | $this->assertTrue(is_numeric($token->getExpireTimestamp()));
28 | $this->assertInstanceOf('GuzzleHttp\Message\Response', $token->getResponse());
29 | }
30 |
31 | public function testToArray()
32 | {
33 | $token = new AccessToken('test_value', [
34 | 'expires_in' => 1234,
35 | 'refresh_token' => 'test_refresh_token',
36 | 'scope' => 'test_scope',
37 | 'foo' => 'test_foo',
38 | 'bar' => 'test_bar',
39 | ]);
40 |
41 | $this->assertEquals('test_scope', $token->getScope());
42 | $this->assertEquals([
43 | 'foo' => 'test_foo',
44 | 'bar' => 'test_bar',
45 | ], $token->getExtra());
46 |
47 |
48 | $tokenArray = $token->toArray();
49 | $this->assertEquals('test_value', $tokenArray['tokenValue']);
50 | $this->assertEquals('test_refresh_token', $tokenArray['refreshToken']);
51 | $this->assertEquals('test_scope', $tokenArray['scope']);
52 | $this->assertEquals([
53 | 'foo' => 'test_foo',
54 | 'bar' => 'test_bar',
55 | ], $tokenArray['extra']);
56 | }
57 |
58 |
59 | /**
60 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
61 | */
62 | public function testConstruct()
63 | {
64 | new AccessToken('');
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/ServiceTest.php:
--------------------------------------------------------------------------------
1 | options = [
17 | 'key' => 'test_key',
18 | 'secret' => 'test_secret',
19 | 'callback' => 'test_callback',
20 | ];
21 | }
22 |
23 | /**
24 | * @expectedException Eva\EvaOAuth\Exception\BadMethodCallException
25 | */
26 | public function testNotExistProvider()
27 | {
28 | new Service('foo_service', []);
29 | }
30 |
31 | public function testConstruct()
32 | {
33 | $service = new Service('douban', $this->options);
34 | $this->assertInstanceOf('Eva\EvaOAuth\OAuth2\Client', $service->getAdapter());
35 |
36 | $service = new Service('twitter', $this->options);
37 | $this->assertInstanceOf('Eva\EvaOAuth\OAuth1\Consumer', $service->getAdapter());
38 | }
39 |
40 | /**
41 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
42 | */
43 | public function testProviderInterface()
44 | {
45 | \Mockery::namedMock('FooProvider');
46 | Service::registerProvider('foo', 'FooProvider');
47 | $service = new Service('foo', $this->options);
48 | }
49 |
50 | public function testRegisterProvider()
51 | {
52 | \Mockery::namedMock('FooOAuth2Provider', 'Eva\EvaOAuth\OAuth2\Providers\AbstractProvider');
53 | \Mockery::namedMock('BarOAuth1Provider', 'Eva\EvaOAuth\OAuth1\Providers\AbstractProvider');
54 | Service::registerProviders([
55 | 'foo' => 'FooOAuth2Provider',
56 | 'bar' => 'BarOAuth2Provider',
57 | ]);
58 | $this->assertArrayHasKey('foo', Service::getProviders());
59 | $this->assertArrayHasKey('bar', Service::getProviders());
60 |
61 | $service = new Service('foo', $this->options);
62 | $this->assertInstanceOf('FooOAuth2Provider', $service->getProvider());
63 | }
64 |
65 | public function testDebug()
66 | {
67 | $service = new Service('foo', $this->options);
68 | $service->debug('test');
69 | $this->assertTrue(is_array($service->getHttpClient()->getEmitter()->listeners()));
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/User/UserTest.php:
--------------------------------------------------------------------------------
1 | 'test_version',
13 | 'provider' => 'test_provider',
14 | 'id' => 'test_id',
15 | 'name' => 'test_name',
16 | 'avatar' => 'test_avatar',
17 | 'email' => 'test_email',
18 | 'extra' => [
19 | 'test_extra'
20 | ],
21 | ];
22 | $user = new StandardUser($userArray);
23 |
24 | $this->assertEquals('test_version', $user->getVersion());
25 | $this->assertEquals('test_provider', $user->getProvider());
26 | $this->assertEquals('test_id', $user->getId());
27 | $this->assertEquals('test_name', $user->getName());
28 | $this->assertEquals('test_avatar', $user->getAvatar());
29 | $this->assertEquals('test_email', $user->getEmail());
30 | $this->assertEquals(['test_extra'], $user->getExtra());
31 | $this->assertEquals($userArray, $user->toArray());
32 |
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/Utils/ResponseParserTest.php:
--------------------------------------------------------------------------------
1 | assertArrayHasKey('status', $res);
25 | }
26 |
27 | public function testParse()
28 | {
29 | $res = ResponseParser::parse(
30 | new Response(200, [], Stream::factory('jQuery21305313212884880004_1427952242748({"status":"success"})')),
31 | ResourceServerInterface::FORMAT_JSONP
32 | );
33 | $this->assertArrayHasKey('status', $res);
34 | }
35 |
36 | /**
37 | * @expectedException Eva\EvaOAuth\Exception\InvalidArgumentException
38 | */
39 | public function testUnknowFormat()
40 | {
41 | ResponseParser::parse(
42 | new Response(200, [], Stream::factory('jQuery21305313212884880004_1427952242748({"status":"success"})')),
43 | 'foo'
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/EvaOAuthTest/Utils/TextTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('OAuth foo="bar"', Text::buildHeaderString([
23 | 'foo' => 'bar'
24 | ]));
25 |
26 | $this->assertEquals('OAuth foo="bar", tfoo="tbar"', Text::buildHeaderString([
27 | 'tfoo' => 'tbar',
28 | 'foo' => 'bar'
29 | ]));
30 |
31 | }
32 |
33 | public function testRandomString()
34 | {
35 | $this->assertEquals(8, strlen(Text::generateRandomString(8)));
36 | $this->assertEquals(32, strlen(Text::generateRandomString(32)));
37 | }
38 |
39 | public function testBaseString()
40 | {
41 | $this->assertEquals(
42 | 'POST&http%3A%2F%2Ffoo&callback%3Dhttp%253A%252F%252Fbar',
43 | Text::buildBaseString('post', 'http://foo', ['callback' => 'http://bar'])
44 | );
45 |
46 | $this->assertEquals('POST&url&foo%3Dbar', Text::buildBaseString('post', 'url', ['foo' => 'bar']));
47 |
48 | $this->assertEquals(
49 | 'POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_consumer_key%3DX6vZ7YDHiod0hUyTQj0Gw%26oauth_nonce%3Dddb73c89364451560652f53bcd8f14f7%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1428979350%26oauth_version%3D1.0',
50 | Text::buildBaseString('post', 'https://api.twitter.com/oauth/request_token', [
51 | 'oauth_consumer_key' => 'X6vZ7YDHiod0hUyTQj0Gw',
52 | 'oauth_signature_method' => 'HMAC-SHA1',
53 | 'oauth_timestamp' => '1428979350',
54 | 'oauth_nonce' => 'ddb73c89364451560652f53bcd8f14f7',
55 | 'oauth_version' => '1.0',
56 | ])
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/report/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/tmp/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------