WeWork.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. namespace Overtrue\Socialite\Providers;
  3. use Overtrue\Socialite\Exceptions\AuthorizeFailedException;
  4. use Overtrue\Socialite\Exceptions\InvalidArgumentException;
  5. use Overtrue\Socialite\Exceptions\MethodDoesNotSupportException;
  6. use Overtrue\Socialite\User;
  7. class WeWork extends Base
  8. {
  9. public const NAME = 'wework';
  10. protected bool $detailed = false;
  11. protected ?int $agentId;
  12. protected ?string $apiAccessToken;
  13. /**
  14. * @param int $agentId
  15. *
  16. * @return $this
  17. */
  18. public function setAgentId(int $agentId)
  19. {
  20. $this->agentId = $agentId;
  21. return $this;
  22. }
  23. public function userFromCode(string $code): User
  24. {
  25. $token = $this->getApiAccessToken();
  26. $user = $this->getUserId($token, $code);
  27. if ($this->detailed) {
  28. $user = $this->getUserById($user['UserId']);
  29. }
  30. return $this->mapUserToObject($user)->setProvider($this)->setRaw($user);
  31. }
  32. public function detailed(): self
  33. {
  34. $this->detailed = true;
  35. return $this;
  36. }
  37. /**
  38. * @param string $apiAccessToken
  39. *
  40. * @return $this
  41. */
  42. public function withApiAccessToken(string $apiAccessToken)
  43. {
  44. $this->apiAccessToken = $apiAccessToken;
  45. return $this;
  46. }
  47. /**
  48. * @return string
  49. * @throws \Overtrue\Socialite\Exceptions\InvalidArgumentException
  50. */
  51. public function getAuthUrl(): string
  52. {
  53. // 网页授权登录
  54. if (!empty($this->scopes)) {
  55. return $this->getOAuthUrl();
  56. }
  57. // 第三方网页应用登录(扫码登录)
  58. return $this->getQrConnectUrl();
  59. }
  60. protected function getOAuthUrl(): string
  61. {
  62. $queries = [
  63. 'appid' => $this->getClientId(),
  64. 'redirect_uri' => $this->redirectUrl,
  65. 'response_type' => 'code',
  66. 'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator),
  67. 'state' => $this->state,
  68. ];
  69. return sprintf('https://open.weixin.qq.com/connect/oauth2/authorize?%s#wechat_redirect', http_build_query($queries));
  70. }
  71. public function getQrConnectUrl()
  72. {
  73. $queries = [
  74. 'appid' => $this->getClientId(),
  75. 'agentid' => $this->agentId ?? $this->config->get('agentid'),
  76. 'redirect_uri' => $this->redirectUrl,
  77. 'state' => $this->state,
  78. ];
  79. if (empty($queries['agentid'])) {
  80. throw new InvalidArgumentException('You must config the `agentid` in configuration or using `setAgentid($agentId)`.');
  81. }
  82. return sprintf('https://open.work.weixin.qq.com/wwopen/sso/qrConnect?%s#wechat_redirect', http_build_query($queries));
  83. }
  84. /**
  85. * @param string $token
  86. *
  87. * @return array
  88. * @throws \Overtrue\Socialite\Exceptions\MethodDoesNotSupportException
  89. */
  90. protected function getUserByToken(string $token): array
  91. {
  92. throw new MethodDoesNotSupportException('WeWork doesn\'t support access_token mode');
  93. }
  94. protected function getApiAccessToken()
  95. {
  96. return $this->apiAccessToken ?? $this->apiAccessToken = $this->createApiAccessToken();
  97. }
  98. /**
  99. * @param string $token
  100. * @param string $code
  101. *
  102. * @return array
  103. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  104. * @throws \GuzzleHttp\Exception\GuzzleException
  105. */
  106. protected function getUserId(string $token, string $code): array
  107. {
  108. $response = $this->getHttpClient()->get(
  109. 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo',
  110. [
  111. 'query' => array_filter(
  112. [
  113. 'access_token' => $token,
  114. 'code' => $code,
  115. ]
  116. ),
  117. ]
  118. );
  119. $response = \json_decode($response->getBody(), true) ?? [];
  120. if (($response['errcode'] ?? 1) > 0 || (empty($response['UserId']) && empty($response['OpenId']))) {
  121. throw new AuthorizeFailedException('Failed to get user openid:' . $response['errmsg'] ?? 'Unknown.', $response);
  122. } else if (empty($response['UserId'])) {
  123. $this->detailed = false;
  124. }
  125. return $response;
  126. }
  127. /**
  128. * @param string $userId
  129. *
  130. * @return array
  131. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  132. * @throws \GuzzleHttp\Exception\GuzzleException
  133. */
  134. protected function getUserById(string $userId): array
  135. {
  136. $response = $this->getHttpClient()->post(
  137. 'https://qyapi.weixin.qq.com/cgi-bin/user/get',
  138. [
  139. 'query' => [
  140. 'access_token' => $this->getApiAccessToken(),
  141. 'userid' => $userId,
  142. ],
  143. ]
  144. );
  145. $response = \json_decode($response->getBody(), true) ?? [];
  146. if (($response['errcode'] ?? 1) > 0 || empty($response['userid'])) {
  147. throw new AuthorizeFailedException('Failed to get user:' . $response['errmsg'] ?? 'Unknown.', $response);
  148. }
  149. return $response;
  150. }
  151. /**
  152. * @param array $user
  153. *
  154. * @return \Overtrue\Socialite\User
  155. */
  156. protected function mapUserToObject(array $user): User
  157. {
  158. if ($this->detailed) {
  159. return new User(
  160. [
  161. 'id' => $user['userid'] ?? null,
  162. 'name' => $user['name'] ?? null,
  163. 'avatar' => $user['avatar'] ?? null,
  164. 'email' => $user['email'] ?? null,
  165. ]
  166. );
  167. }
  168. return new User(
  169. [
  170. 'id' => $user['UserId'] ?? null ?: $user['OpenId'] ?? null,
  171. ]
  172. );
  173. }
  174. /**
  175. * @return string
  176. * @throws \Overtrue\Socialite\Exceptions\AuthorizeFailedException
  177. * @throws \GuzzleHttp\Exception\GuzzleException
  178. */
  179. protected function createApiAccessToken(): string
  180. {
  181. $response = $this->getHttpClient()->get(
  182. 'https://qyapi.weixin.qq.com/cgi-bin/gettoken',
  183. [
  184. 'query' => array_filter(
  185. [
  186. 'corpid' => $this->config->get('corp_id') ?? $this->config->get('corpid'),
  187. 'corpsecret' => $this->config->get('corp_secret') ?? $this->config->get('corpsecret'),
  188. ]
  189. ),
  190. ]
  191. );
  192. $response = \json_decode($response->getBody(), true) ?? [];
  193. if (($response['errcode'] ?? 1) > 0) {
  194. throw new AuthorizeFailedException('Failed to get api access_token:' . $response['errmsg'] ?? 'Unknown.', $response);
  195. }
  196. return $response['access_token'];
  197. }
  198. protected function getTokenUrl(): string
  199. {
  200. return '';
  201. }
  202. }