|
8 | 8 |
|
9 | 9 | class CodeGrantService |
10 | 10 | { |
| 11 | + function generateCodeVerifier() { |
| 12 | + return bin2hex(random_bytes(32)); |
| 13 | + } |
| 14 | + |
| 15 | + function generateCodeChallenge($code_verifier) { |
| 16 | + return rtrim(strtr(base64_encode(hash('sha256', $code_verifier, true)), '+/', '-_'), '='); |
| 17 | + } |
| 18 | + |
11 | 19 | /** |
12 | 20 | * Checker for the CSRF token |
13 | 21 | */ |
@@ -35,20 +43,28 @@ public function flash(string $msg): void |
35 | 43 | array_push($_SESSION['flash'], $msg); |
36 | 44 | } |
37 | 45 |
|
38 | | - /** |
39 | | - * DocuSign login handler |
40 | | - */ |
41 | 46 | public function login(): void |
42 | 47 | { |
43 | | - $provider = $this->getOauthProvider(); |
44 | | - $authorizationUrl = $provider->getAuthorizationUrl(); |
45 | | - // Get the state generated for you and store it to the session. |
| 48 | + if (isset($_SESSION['pkce_failed']) && $_SESSION['pkce_failed'] === true) { |
| 49 | + $provider = $this->getOauthProvider(); |
| 50 | + $authorizationUrl = $provider->getAuthorizationUrl(); |
| 51 | + } else { |
| 52 | + $codeVerifier = $this->generateCodeVerifier(); |
| 53 | + $codeChallenge = $this->generateCodeChallenge($codeVerifier); |
| 54 | + $_SESSION['code_verifier'] = $codeVerifier; |
| 55 | + |
| 56 | + $provider = $this->getOauthProvider(); |
| 57 | + $authorizationUrl = $provider->getAuthorizationUrl([ |
| 58 | + 'code_challenge' => $codeChallenge, |
| 59 | + 'code_challenge_method' => 'S256' |
| 60 | + ]); |
| 61 | + } |
46 | 62 | $_SESSION['oauth2state'] = $provider->getState(); |
47 | 63 | // Redirect the user to the authorization URL. |
48 | 64 | header('Location: ' . $authorizationUrl); |
49 | 65 | exit; |
50 | 66 | } |
51 | | - |
| 67 | + |
52 | 68 | /** |
53 | 69 | * Get OAUTH provider |
54 | 70 | * @return DocuSign $provider |
@@ -80,15 +96,19 @@ public function authCallback($redirectUrl): void |
80 | 96 | } |
81 | 97 | exit('Invalid OAuth state'); |
82 | 98 | } else { |
| 99 | + $tokenRequestOptions = [ |
| 100 | + 'code' => $_GET['code'] |
| 101 | + ]; |
| 102 | + |
| 103 | + // Add the code_verifier only for PKCE authorization |
| 104 | + if (!isset($_SESSION['pkce_failed']) || $_SESSION['pkce_failed'] === false) { |
| 105 | + $tokenRequestOptions['code_verifier'] = $_SESSION['code_verifier']; |
| 106 | + unset($_SESSION['pkce_failed']); |
| 107 | + } |
| 108 | + |
83 | 109 | try { |
84 | 110 | // Try to get an access token using the authorization code grant. |
85 | | - $accessToken = $provider->getAccessToken( |
86 | | - 'authorization_code', |
87 | | - [ |
88 | | - 'code' => $_GET['code'] |
89 | | - ] |
90 | | - ); |
91 | | - |
| 111 | + $accessToken = $provider->getAccessToken('authorization_code', $tokenRequestOptions); |
92 | 112 | $this->flash('You have authenticated with DocuSign.'); |
93 | 113 | // We have an access token, which we may use in authenticated |
94 | 114 | // requests against the service provider's API. |
@@ -117,7 +137,9 @@ public function authCallback($redirectUrl): void |
117 | 137 | $_SESSION['ds_base_path'] = $account_info["base_uri"] . $base_uri_suffix; |
118 | 138 | } catch (IdentityProviderException $e) { |
119 | 139 | // Failed to get the access token or user details. |
120 | | - exit($e->getMessage()); |
| 140 | + $_SESSION['pkce_failed'] = true; |
| 141 | + $this->login(); |
| 142 | + exit; |
121 | 143 | } |
122 | 144 | if (!$redirectUrl) { |
123 | 145 | $redirectUrl = $GLOBALS['app_url']; |
|
0 commit comments