1515
1616use Guanguans \Notify \Foundation \Authenticators \NullAuthenticator ;
1717use Guanguans \Notify \Foundation \Client ;
18- use Guanguans \Notify \Foundation \Concerns \AsFormParams ;
1918use Guanguans \Notify \Foundation \Exceptions \RequestException ;
20- use Guanguans \Notify \Foundation \ Message ;
19+ use Guanguans \Notify \ZohoCliq \ Messages \ AccessTokenMessage ;
2120use GuzzleHttp \Middleware ;
2221use Psr \Http \Message \RequestInterface ;
2322use Psr \Http \Message \ResponseInterface ;
2423
2524/**
25+ * @see https://github.com/w7corp/easywechat/blob/6.x/src/OpenPlatform/ComponentAccessToken.php
26+ *
2627 * ```
2728 * curl --location 'https://accounts.zoho.com/oauth/v2/token' \
2829 * --form 'client_id="1000.TTFROV098VVFG8NB686LR98TCDR"' \
3839 *
3940 * curl --location 'https://cliq.zoho.com/api/v2/users?limit=5' \
4041 * --header 'Authorization: Zoho-oauthtoken 1000.80e9143983dcc190427cad7e8f029e25.cdc1c1043e5e7f0555fc14fc7faf3' \
41- * ```.
42+ * ```
4243 */
4344class Authenticator extends NullAuthenticator
4445{
4546 private static ?string $ accessToken ;
47+ private Client $ client ;
4648
4749 public function __construct (
4850 private string $ clientId ,
51+ #[\SensitiveParameter]
4952 private string $ clientSecret ,
50- ) {}
53+ ?Client $ client = null ,
54+ ) {
55+ $ this ->client = $ client ?? new Client ;
56+ }
5157
52- public function __invoke (callable $ handler ): callable
58+ public function applyToMiddleware (callable $ handler ): callable
5359 {
54- return Middleware::mapRequest (
55- fn (RequestInterface $ request ): RequestInterface => $ request ->withHeader ('Authorization ' , "Bearer {$ this ->getAccessToken ()}" ),
56- )($ handler );
60+ return array_reduce (
61+ [
62+ [$ this , 'dataCenter ' ],
63+ [$ this , 'retry ' ],
64+ [$ this , 'authenticate ' ],
65+ ],
66+ static fn (callable $ handler , callable $ middleware ): callable => $ middleware ($ handler ),
67+ $ handler ,
68+ );
5769 }
5870
59- public function retry ( callable $ handler ): callable
71+ public static function flushAccessToken ( ): void
6072 {
61- return Middleware::retry (function (int $ retries , RequestInterface &$ request , ?ResponseInterface $ response = null ): bool {
62- if (1 <= $ retries ) {
63- return false ;
64- }
65-
66- /** @var \Guanguans\Notify\Foundation\Response $response */
67- if ($ response ?->unauthorized()) {
68- $ request = $ request ->withHeader ('Authorization ' , "Bearer {$ this ->refreshAccessToken ()}" );
73+ self ::$ accessToken = null ;
74+ }
6975
70- return true ;
71- }
76+ /**
77+ * @todo
78+ */
79+ private function dataCenter (callable $ handler ): callable
80+ {
81+ return $ handler ;
82+ }
7283
73- return false ;
74- })($ handler );
84+ private function authenticate (callable $ handler ): callable
85+ {
86+ return Middleware::mapRequest (
87+ fn (RequestInterface $ request ): RequestInterface => $ request ->withHeader (
88+ 'Authorization ' ,
89+ // "Bearer xxx",
90+ "Bearer {$ this ->getAccessToken ()}" ,
91+ ),
92+ )($ handler );
7593 }
7694
77- public static function flushAccessToken ( ): void
95+ private function retry ( callable $ handler ): callable
7896 {
79- self ::$ accessToken = null ;
97+ return Middleware::retry (
98+ function (int $ retries , RequestInterface &$ request , ?ResponseInterface $ response = null ): bool {
99+ if (1 <= $ retries ) {
100+ return false ;
101+ }
102+
103+ if ($ response ?->getStatusCode() === 401 ) {
104+ $ request = $ request ->withHeader ('Authorization ' , "Bearer {$ this ->refreshAccessToken ()}" );
105+
106+ return true ;
107+ }
108+
109+ return false ;
110+ }
111+ )($ handler );
80112 }
81113
82114 private function refreshAccessToken (): string
@@ -90,8 +122,6 @@ private function refreshAccessToken(): string
90122 * Temporary memory cache.
91123 *
92124 * @todo psr cache
93- * @todo retry on authentication failure
94- * @todo data center
95125 */
96126 private function getAccessToken (): string
97127 {
@@ -101,31 +131,14 @@ private function getAccessToken(): string
101131 /**
102132 * @throws \GuzzleHttp\Exception\GuzzleException
103133 * @throws \JsonException
134+ * @throws \ReflectionException
104135 */
105136 private function fetchAccessToken (): string
106137 {
107- $ response = (new Client )
108- ->send (
109- new class ([
110- 'client_id ' => $ this ->clientId ,
111- 'client_secret ' => $ this ->clientSecret ,
112- 'grant_type ' => 'client_credentials ' ,
113- 'scope ' => 'ZohoCliq.Webhooks.CREATE ' ,
114- ]) extends Message {
115- use AsFormParams;
116- protected array $ defined = [
117- 'client_id ' ,
118- 'client_secret ' ,
119- 'grant_type ' ,
120- 'scope ' ,
121- ];
122-
123- public function toHttpUri (): string
124- {
125- return 'https://accounts.zoho.com/oauth/v2/token ' ;
126- }
127- }
128- );
138+ $ response = $ this ->client ->send (AccessTokenMessage::make ([
139+ 'client_id ' => $ this ->clientId ,
140+ 'client_secret ' => $ this ->clientSecret ,
141+ ]));
129142
130143 if ($ response ->json ('error ' )) {
131144 throw RequestException::create ($ response ->request (), $ response );
0 commit comments