Skip to content

Commit

Permalink
Support WeWork login (#26)
Browse files Browse the repository at this point in the history
* Support WeWork login

* Update README
  • Loading branch information
sy-records committed Oct 17, 2023
1 parent ff448ab commit e026602
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ API 文档:[https://apidoc.gitee.com/yurunsoft/YurunOAuthLogin](https://apidoc
- GitLab
- 飞书
- 钉钉
- 企业微信

> 后续将不断添加新的平台支持,也欢迎你来提交PR,一起完善!
Expand Down
14 changes: 12 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
{
"name": "yurunsoft/yurun-oauth-login",
"description": "YurunOAuthLogin是一个PHP 第三方登录授权 SDK,集成了QQ、微信、微博、Github等常用接口",
"description": "YurunOAuthLogin 是一个 PHP 第三方登录授权 SDK,集成了 QQ、微信、微博、GitHub 等常用接口",
"type": "library",
"license": "MIT",
"autoload": {
"psr-4": {
"Yurun\\OAuthLogin\\": "src/"
}
},
"authors": [
{
"name": "Yurun",
"email": "admin@yurunsoft.com"
},
{
"name": "Lu Fei",
"email": "lufei@simps.io"
}
],
"require": {
"php": ">=5.5",
"yurunsoft/yurun-http" : "~4.0|~5.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.18.3"
}
}
}
257 changes: 257 additions & 0 deletions src/WeWork/OAuth2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
<?php

namespace Yurun\OAuthLogin\WeWork;

use Yurun\OAuthLogin\ApiException;
use Yurun\OAuthLogin\Base;

class OAuth2 extends Base
{
/**
* api接口域名.
*/
const API_DOMAIN = 'https://qyapi.weixin.qq.com/';

/**
* 开放域名.
*/
const OPEN_DOMAIN = 'https://open.weixin.qq.com/';

/**
* Web登录域名.
*/
const LOGIN_DOMAIN = 'https://login.work.weixin.qq.com/';

/**
* agentid
*
* @var string
*/
public $agentid;

/**
* 构造方法.
*
* @param string $appid 应用的唯一标识
* @param string $appSecret appid对应的密钥
* @param string $callbackUrl 登录回调地址
* @param string $agentid 应用agentid
*/
public function __construct($appid = null, $appSecret = null, $callbackUrl = null, $agentid = null)
{
parent::__construct($appid, $appSecret, $callbackUrl);
$this->agentid = $agentid;
}

/**
* 获取登录授权url地址
*
* @param string $name 跟在域名后的文本
* @param array $params GET参数
*
* @return string
*/
public function getAuthLoginUrl($name, $params = [])
{
return static::OPEN_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params)));
}

/**
* 获取Web登录授权url地址
*
* @param string $name 跟在域名后的文本
* @param array $params GET参数
*
* @return string
*/
public function getWebLoginUrl($name, $params = [])
{
return static::LOGIN_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params)));
}

/**
* 获取url地址
*
* @param string $name 跟在域名后的文本
* @param array $params GET参数
*
* @return string
*/
public function getUrl($name, $params = [])
{
return static::API_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params)));
}

/**
* 第一步:获取登录页面跳转url.
*
* @param string $callbackUrl 登录回调地址
* @param string $state 状态值,不传则自动生成,随后可以通过->state获取。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。一般为每个用户登录时随机生成state存在session中,登录回调中判断state是否和session中相同
* @param array $scope 请求用户授权时向用户显示的可进行授权的列表。可空
* @link https://developer.work.weixin.qq.com/document/path/91022
*
* @return string
*/
public function getAuthUrl($callbackUrl = null, $state = null, $scope = null)
{
$option = [
'appid' => $this->appid,
'agentid' => $this->agentid,
'redirect_uri' => null === $callbackUrl ? $this->callbackUrl : $callbackUrl,
'scope' => null === $scope ? $this->scope : $scope,
'state' => $this->getState($state),
'response_type' => 'code',
];
if (null === $this->loginAgentUrl)
{
return $this->getAuthLoginUrl('connect/oauth2/authorize', $option);
}
else
{
return $this->loginAgentUrl . '?' . $this->http_build_query($option);
}
}

/**
* 第一步:生成企业微信扫码登录页面,获取登录页面跳转url.
*
* @param string $callbackUrl
* @param string $state
* @param string $loginType
* @link https://developer.work.weixin.qq.com/document/path/98152
*
* @return string
*/
public function getWebAuthUrl($callbackUrl = null, $state = null, $loginType = 'CorpApp')
{
$option = [
'appid' => $this->appid,
'agentid' => $this->agentid,
'login_type' => $loginType,
'redirect_uri' => null === $callbackUrl ? $this->callbackUrl : $callbackUrl,
'state' => $this->getState($state)
];
if (null === $this->loginAgentUrl)
{
return $this->getWebLoginUrl('wwlogin/sso/login', $option);
}
else
{
return $this->loginAgentUrl . '?' . $this->http_build_query($option);
}
}

/**
* 第二步:处理回调并获取access_token。与getAccessToken不同的是会验证state值是否匹配,防止csrf攻击。
*
* @param string $storeState 存储的正确的state
* @param string $code 第一步里$redirectUri地址中传过来的code,为null则通过get参数获取
* @param string $state 回调接收到的state,为null则通过get参数获取
*
* @return string
*/
protected function __getAccessToken($storeState, $code = null, $state = null)
{
$this->result = $this->http->get($this->getUrl('cgi-bin/gettoken'), [
'corpid' => $this->appid,
'corpsecret' => $this->appSecret
])->json(true);

if (!empty($this->result['errcode']))
{
throw new ApiException($this->result['errmsg'], $this->result['errcode']);
}
else
{
return $this->accessToken = $this->result['access_token'];
}
}

/**
* 获取用户资料.
*
* @param string $accessToken
*
* @return array
*/
public function getUserInfo($accessToken = null)
{
$token = null === $accessToken ? $this->accessToken : $accessToken;
$this->result = $this->http->get($this->getUrl('cgi-bin/auth/getuserinfo', [
'access_token' => $token,
'code' => isset($_GET['code']) ? $_GET['code'] : '',
]))
->json(true);

if (!empty($this->result['errcode']))
{
throw new ApiException($this->result['errmsg'], $this->result['errcode']);
}
else
{
$this->openid = isset($this->result['userid']) ? $this->result['userid'] : $this->result['openid'];

return $this->result;
}
}

/**
* Get user detail info.
*
* @param string $userTicket
* @param string|null $accessToken
* @return array
*/
public function getUserDetail($userTicket, $accessToken = null)
{
$token = null === $accessToken ? $this->accessToken : $accessToken;
$this->result = $this->http->post($this->getUrl('cgi-bin/auth/getuserdetail', ['access_token' => $token]), [
'user_ticket' => $userTicket,
], 'json')
->json(true);

if (!empty($this->result['errcode']))
{
throw new ApiException($this->result['errmsg'], $this->result['errcode']);
}
else
{
$this->openid = $this->result['userid'];

return $this->result;
}
}

/**
* 刷新AccessToken续期
*
* @param string $refreshToken
*
* @return bool
*/
public function refreshToken($refreshToken)
{
return false;
}

/**
* 检验授权凭证AccessToken是否有效.
*
* @param string $accessToken
*
* @return bool
*/
public function validateAccessToken($accessToken = null)
{
try
{
$this->getUserInfo($accessToken);

return true;
}
catch (ApiException $e)
{
return false;
}
}
}
101 changes: 101 additions & 0 deletions src/WeWork/loginAgent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title>企业微信登录</title>
</head>

<body>
<script>
var GWC = {
version: '1.0.0',
urlParams: {},
appendParams: function(url, params) {
if (params) {
var baseWithSearch = url.split('#')[0];
var hash = url.split('#')[1];
for (var key in params) {
var attrValue = params[key];
if (attrValue !== undefined) {
var newParam = key + "=" + attrValue;
if (baseWithSearch.indexOf('?') > 0) {
var oldParamReg = new RegExp('^' + key + '=[-%.!~*\'\(\)\\w]*', 'g');
if (oldParamReg.test(baseWithSearch)) {
baseWithSearch = baseWithSearch.replace(oldParamReg, newParam);
} else {
baseWithSearch += "&" + newParam;
}
} else {
baseWithSearch += "?" + newParam;
}
}
}

if (hash) {
url = baseWithSearch + '#' + hash;
} else {
url = baseWithSearch;
}
}
return url;
},
getUrlParams: function() {
var pairs = location.search.substring(1).split('&');
for (var i = 0; i < pairs.length; i++) {
var pos = pairs[i].indexOf('=');
if (pos === -1) {
continue;
}
GWC.urlParams[pairs[i].substring(0, pos)] = decodeURIComponent(pairs[i].substring(pos + 1));
}
},
doRedirect: function() {
var code = GWC.urlParams['code'];
var appId = GWC.urlParams['appid'];
var agentId = GWC.urlParams['agentid'];
var state = GWC.urlParams['state'];
var scope = GWC.urlParams['scope'];
var loginType = GWC.urlParams['login_type'];
var redirectUri;

if (!code) {
//第一步,没有拿到code,跳转至授权页面获取code
if (typeof loginType === 'undefined') {
redirectUri = GWC.appendParams('https://open.weixin.qq.com/connect/oauth2/authorize', {
'appid': appId,
'agentid': agentId,
'redirect_uri': encodeURIComponent(GWC.appendParams(location.href.split('?')[0], {
'redirect_uri': encodeURIComponent(GWC.urlParams['redirect_uri']),
})),
'state': state,
'scope': scope,
});
} else {
redirectUri = GWC.appendParams('https://login.work.weixin.qq.com/wwlogin/sso/login', {
'appid': appId,
'agentid': agentId,
'redirect_uri': encodeURIComponent(GWC.appendParams(location.href.split('?')[0], {
'redirect_uri': encodeURIComponent(GWC.urlParams['redirect_uri']),
})),
'state': state,
});
}
} else {
//第二步,从授权页面跳转回来,已经获取到了code,再次跳转到实际所需页面
redirectUri = GWC.appendParams(GWC.urlParams['redirect_uri'], {
'code': code,
'state': state,
});
}

location.href = redirectUri;
}
};

GWC.getUrlParams();
GWC.doRedirect();
</script>
</body>

</html>

0 comments on commit e026602

Please sign in to comment.