Skip to content

Commit

Permalink
auth v2
Browse files Browse the repository at this point in the history
  • Loading branch information
panguobin committed Feb 15, 2022
1 parent d60671d commit ae984c1
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 9 deletions.
41 changes: 37 additions & 4 deletions src/API/Authentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Upbond\Auth\SDK\API\Helpers\ApiClient;
use Upbond\Auth\SDK\Exception\ApiException;
use GuzzleHttp\Psr7;
use Upbond\Auth\SDK\Utility\PKCE;

/**
* Class Authentication
Expand Down Expand Up @@ -66,6 +67,11 @@ class Authentication
*/
private $guzzleOptions;

private $code_verifier;
private $code_challenge;
private $code_challenge_method;


/**
* ApiClient instance.
*
Expand All @@ -92,7 +98,11 @@ public function __construct(
?string $client_secret = null,
?string $audience = null,
?string $scope = null,
array $guzzleOptions = []
array $guzzleOptions = [],
?string $code_verifier = null,
?string $code_challenge = null,
?string $code_challenge_method = null

)
{
$this->domain = $domain;
Expand All @@ -102,6 +112,10 @@ public function __construct(
$this->scope = $scope;
$this->guzzleOptions = $guzzleOptions;

$this->code_verifier = $code_verifier;
$this->code_challenge = $code_challenge;
$this->code_challenge_method = $code_challenge_method;

$this->apiClient = new ApiClient( [
'domain' => 'https://'.$this->domain,
'basePath' => '/',
Expand Down Expand Up @@ -138,8 +152,22 @@ public function get_authorize_link(
$params['state'] = $state ?? $params['state'] ?? null;
$params['audience'] = $params['audience'] ?? $this->audience ?? null;
$params['scope'] = $params['scope'] ?? $this->scope ?? null;
$params['code_challenge'] = $params['code_challenge'] ?? $this->code_challenge ?? null;
$params['code_challenge_method'] = $params['code_challenge_method'] ?? $this->code_challenge_method ?? 'S256';
$params['code_verifier'] = $params['code_verifier'] ?? $this->code_verifier ?? null;


// code_challenge: JfTJyQnjqRG78O1H1NakYBG6c3OHrQuLoE_QYKzfUYk
// code_challenge_method: S256
$params = array_filter($params);

// v2 authorize
return sprintf(
'https://%s/authorize?%s',
$this->domain,
Psr7\build_query($params)
);


return sprintf(
'https://%s/authenticate/oauth/authorize?%s',
Expand Down Expand Up @@ -341,7 +369,8 @@ public function sms_passwordless_start(string $phone_number, ?string $forwarded_
public function userinfo(string $access_token) : array
{
return $this->apiClient->method('get')
->addPath('authenticate', 'user')
// ->addPath('authenticate', 'user')
->addPath( 'userinfo')
->withHeader(new AuthorizationBearer($access_token))
->call();
}
Expand Down Expand Up @@ -373,9 +402,10 @@ public function oauth_token(array $options = []) : array
if (! isset($options['grant_type'])) {
throw new ApiException('grant_type is mandatory');
}

// dd($options);
$request = $this->apiClient->method('post')
->addPath( 'authenticate', 'oauth', 'token' )
// ->addPath( 'authenticate', 'oauth', 'token' )
->addPath( 'oauth', 'token' )
->withBody(json_encode($options));

if (isset($options['auth0_forwarded_for'])) {
Expand Down Expand Up @@ -407,8 +437,11 @@ public function code_exchange(string $code, string $redirect_uri) : array
'redirect_uri' => $redirect_uri,
'code' => $code,
'grant_type' => 'authorization_code',
'code_verifier'=> $this->code_verifier
];

// dd($options);

return $this->oauth_token($options);
}

Expand Down
28 changes: 23 additions & 5 deletions src/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

use GuzzleHttp\Exception\RequestException;
use Psr\SimpleCache\CacheInterface;
use Upbond\Auth\SDK\Utility\PKCE;


/**
* Class Auth
Expand Down Expand Up @@ -213,6 +215,12 @@ class Auth
*/
protected $cacheHandler;

protected $code_verifier;

protected $code_challenge;

protected $code_challenge_method;

/**
* BaseAuth Constructor.
*
Expand Down Expand Up @@ -248,7 +256,6 @@ class Auth
*/
public function __construct(array $config)
{

$this->domain = $config['domain'] ?? $_ENV['UPBOND_AUTH_DOMAIN'] ?? $config['api_uri'];
if (empty($this->domain)) {
throw new CoreException('Invalid domain');
Expand All @@ -258,7 +265,7 @@ public function __construct(array $config)
// if (empty($this->clientId)) {
// throw new CoreException('Invalid client_id');
// }

$this->redirectUri = $config['redirect_uri'] ?? $_ENV['UPBOND_AUTH_REDIRECT_URI'] ?? null;
if (empty($this->redirectUri)) {
throw new CoreException('Invalid redirect_uri');
Expand All @@ -279,6 +286,7 @@ public function __construct(array $config)
$this->idTokenLeeway = $config['id_token_leeway'] ?? null;
$this->jwksUri = $config['jwks_uri'] ?? 'https://'.$this->domain.'/.well-known/jwks.json';


$this->idTokenAlg = $config['id_token_alg'] ?? 'RS256';
if (! in_array( $this->idTokenAlg, ['HS256', 'RS256'] )) {
throw new CoreException('Invalid id_token_alg; must be "HS256" or "RS256"');
Expand Down Expand Up @@ -313,6 +321,14 @@ public function __construct(array $config)
$this->store = new SessionStore();
}

$this->code_verifier = $this->store->get('code_verifier') ?? PKCE::generateCodeVerifier(128);
$this->code_challenge = PKCE::generateCodeChallenge($this->code_verifier);
$this->code_challenge_method = 'S256';

if(!$this->store->get('code_verifier')){
$this->store->set('code_verifier',$this->code_verifier);
}

$transientStore = $config['transient_store'] ?? null;
if (! $transientStore instanceof StoreInterface) {
$transientStore = new CookieStore([
Expand All @@ -335,9 +351,12 @@ public function __construct(array $config)
$this->clientSecret,
$this->audience,
$this->scope,
$this->guzzleOptions
$this->guzzleOptions,
$this->code_verifier,
$this->code_challenge,
$this->code_challenge_method
);

// $this->store->set('code_verifier',$this->code_verifier);
$this->user = $this->store->get('user');
$this->accessToken = $this->store->get('access_token');
$this->idToken = $this->store->get('id_token');
Expand Down Expand Up @@ -733,7 +752,6 @@ protected function getState()
} else if ($this->responseMode === 'form_post' && isset($_POST[self::TRANSIENT_STATE_KEY])) {
$state = $_POST[self::TRANSIENT_STATE_KEY];
}

return $state;
}

Expand Down
42 changes: 42 additions & 0 deletions src/Exception/ArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Auth0\SDK\Exception;

/**
* @codeCoverageIgnore
*/
final class ArgumentException extends \Exception implements Auth0Exception
{
public const MSG_VALUE_CANNOT_BE_EMPTY = 'A value for `%s` must be provided';
public const MSG_PKCE_CODE_VERIFIER_LENGTH = 'Code verifier must be created with a minimum length of 43 characters and a maximum length of 128 characters.';
public const MSG_BAD_PERMISSIONS_ARRAY = 'Invalid or empty permissions array passed. All permissions must include both permission_name and resource_server_identifier keys.';
public const MSG_UNKNOWN_METHOD = 'Unknown method %s.';

public static function missing(
string $parameterName,
?\Throwable $previous = null
): self {
return new self(sprintf(self::MSG_VALUE_CANNOT_BE_EMPTY, $parameterName), 0, $previous);
}

public static function codeVerifierLength(
?\Throwable $previous = null
): self {
return new self(self::MSG_PKCE_CODE_VERIFIER_LENGTH, 0, $previous);
}

public static function badPermissionsArray(
?\Throwable $previous = null
): self {
return new self(self::MSG_BAD_PERMISSIONS_ARRAY, 0, $previous);
}

public static function unknownMethod(
string $methodName,
?\Throwable $previous = null
): self {
return new self(sprintf(self::MSG_UNKNOWN_METHOD, $methodName), 0, $previous);
}
}
65 changes: 65 additions & 0 deletions src/Utility/PKCE.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace Upbond\Auth\SDK\Utility;

/**
* Class PKCE.
*/
final class PKCE
{
/**
* Generate a random string of between 43 and 128 characters containing
* letters, numbers and "-", ".", "_", "~", as defined in the RFC 7636
* specification.
*
* @param int $length Code verifier length
*
* @link https://tools.ietf.org/html/rfc7636
*/
public static function generateCodeVerifier(
int $length = 43
): string {
if ($length < 43 || $length > 128) {
throw \Auth0\SDK\Exception\ArgumentException::codeVerifierLength();
}

$string = '';

while (($len = mb_strlen($string)) < $length) {
$size = $length - $len;

// @codeCoverageIgnoreStart
try {
$bytes = random_bytes($size);
} catch (\Exception $exception) {
$bytes = (string) openssl_random_pseudo_bytes($size);
}
// @codeCoverageIgnoreEnd

$string .= mb_substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
}

return $string;
}

/**
* Returns the generated code challenge from the given code_verifier. The
* code_challenge should be a Base64 encoded string with URL and
* filename-safe characters. The trailing '=' characters should be removed
* and no line breaks, whitespace, or other additional characters should be
* present.
*
* @param string $codeVerifier String to generate code challenge from.
*
* @link https://auth0.com/docs/flows/concepts/auth-code-pkce
*/
public static function generateCodeChallenge(
string $codeVerifier
): string {
$encoded = base64_encode(hash('sha256', $codeVerifier, true));

return strtr(rtrim($encoded, '='), '+/', '-_');
}
}

0 comments on commit ae984c1

Please sign in to comment.