diff options
author | yehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664> | 2019-11-25 08:22:35 +0000 |
---|---|---|
committer | yehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664> | 2019-11-25 08:22:35 +0000 |
commit | c61784411988d36d9bbd93cd3a97e773990af342 (patch) | |
tree | 924e6e9dea2ba7b1eedb14d0c4b03a38aefdf179 /Postman/Postman-Auth | |
parent | 907ce8c044159ca8da6ccce3ec5362ac61e7c142 (diff) | |
download | Post-SMTP-c61784411988d36d9bbd93cd3a97e773990af342.zip |
Adding a folder
Diffstat (limited to 'Postman/Postman-Auth')
8 files changed, 666 insertions, 0 deletions
diff --git a/Postman/Postman-Auth/PostmanAbstractAuthenticationManager.php b/Postman/Postman-Auth/PostmanAbstractAuthenticationManager.php new file mode 100644 index 0000000..7402ba7 --- /dev/null +++ b/Postman/Postman-Auth/PostmanAbstractAuthenticationManager.php @@ -0,0 +1,171 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanAbstractAuthenticationManager" )) { + + require_once 'PostmanAuthenticationManager.php'; + + /** + */ + abstract class PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager { + + // constants + const APPROVAL_PROMPT = 'force'; + const ACCESS_TYPE = 'offline'; + const ACCESS_TOKEN = 'access_token'; + const REFRESH_TOKEN = 'refresh_token'; + const EXPIRES = 'expires_in'; + + // the oauth authorization options + private $clientId; + private $clientSecret; + private $authorizationToken; + private $callbackUri; + private $logger; + + /** + * Constructor + */ + public function __construct(PostmanLogger $logger, $clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri) { + assert ( ! empty ( $clientId ) ); + assert ( ! empty ( $clientSecret ) ); + assert ( ! empty ( $authorizationToken ) ); + assert ( ! empty ( $callbackUri ) ); + $this->logger = $logger; + $this->clientId = $clientId; + $this->clientSecret = $clientSecret; + $this->authorizationToken = $authorizationToken; + $this->callbackUri = $callbackUri; + } + protected function getLogger() { + return $this->logger; + } + protected function getClientId() { + return $this->clientId; + } + protected function getClientSecret() { + return $this->clientSecret; + } + protected function getAuthorizationToken() { + return $this->authorizationToken; + } + + /** + * Create a state token to prevent request forgery. + * Store it in the session for later validation. + */ + public function generateRequestTransactionId() { + return $state = md5 ( rand () ); + } + + /** + */ + public function isAccessTokenExpired() { + $expireTime = ($this->authorizationToken->getExpiryTime () - self::FORCE_REFRESH_X_SECONDS_BEFORE_EXPIRE); + $tokenHasExpired = time () > $expireTime; + $this->logger->debug ( 'Access Token Expiry Time is ' . $expireTime . ', expires_in=' . ($expireTime - time ()) . ', expired=' . ($tokenHasExpired ? 'yes' : 'no') ); + return $tokenHasExpired; + } + + /** + * Decoded the received token + * This code is identical for Google and Hotmail + * + * @param mixed $response + * @throws Exception + */ + protected function processResponse($response) { + $authToken = json_decode ( stripslashes ( $response ) ); + if ($authToken === NULL) { + $this->getLogger ()->error ( $response ); + throw new Exception ( $response ); + } else if (isset ( $authToken->{'error'} )) { + if (isset ( $authToken->{'error_description'} )) { + $this->getLogger ()->error ( $authToken->{'error'} . ' processing response: ' . $authToken->{'error_description'} ); + throw new Exception ( $authToken->{'error_description'} . '(' . $authToken->{'error'} . ')' ); + } else { + // Yahoo doesn't give descriptions + $this->getLogger ()->error ( $authToken->{'error'} . ' processing response' ); + throw new Exception ( $authToken->{'error'} ); + } + } else { + $this->getLogger ()->trace ( 'Processing response:' ); + $this->getLogger ()->trace ( $response ); + $this->decodeReceivedAuthorizationToken ( $authToken ); + } + } + + /** + * Parses the authorization token and extracts the expiry time, accessToken, + * and if this is a first-time authorization, a refresh token. + * + * This code is identical for Google and Hotmail + * + * @param mixed $client + */ + protected function decodeReceivedAuthorizationToken($newtoken) { + assert ( ! empty ( $newtoken ) ); + assert ( ! empty ( $newtoken->{self::EXPIRES} ) ); + assert ( ! empty ( $newtoken->{self::ACCESS_TOKEN} ) ); + + // update expiry time + if (empty ( $newtoken->{self::EXPIRES} )) { + throw new Exception ( '[expires_in] value is missing from the authentication token' ); + } + $newExpiryTime = time () + $newtoken->{self::EXPIRES}; + $this->getAuthorizationToken ()->setExpiryTime ( $newExpiryTime ); + $this->getLogger ()->debug ( 'Updating Access Token Expiry Time ' ); + + // update acccess token + if (empty ( $newtoken->{self::ACCESS_TOKEN} )) { + throw new Exception ( '[access_token] value is missing from the authentication token' ); + } + $newAccessToken = $newtoken->{self::ACCESS_TOKEN}; + $this->getAuthorizationToken ()->setAccessToken ( $newAccessToken ); + $this->getLogger ()->debug ( 'Updating Access Token' ); + + // update refresh token, if there is one + if (isset ( $newtoken->{self::REFRESH_TOKEN} )) { + $newRefreshToken = $newtoken->{self::REFRESH_TOKEN}; + $this->getAuthorizationToken ()->setRefreshToken ( $newRefreshToken ); + $this->getLogger ()->debug ( 'Updating Refresh Token ' ); + } + } + + /** + * Given an OAuth provider-specific URL and redirectUri, + * issue an HttpRequest to refresh the access token + * + * This code is identical for Google and Hotmail + */ + public function refreshToken() { + $this->getLogger ()->debug ( 'Refreshing Token' ); + $refreshUrl = $this->getTokenUrl (); + $callbackUrl = $this->getCallbackUri (); + assert ( ! empty ( $refreshUrl ) ); + assert ( ! empty ( $callbackUrl ) ); + // the format of the URL is + // client_id=CLIENT_ID&client_secret=CLIENT_SECRET&redirect_uri=REDIRECT_URI&grant_type=refresh_token&refresh_token=REFRESH_TOKEN + $postvals = array ( + 'client_id' => $this->getClientId (), + 'client_secret' => $this->getClientSecret (), + 'redirect_uri' => $callbackUrl, + 'grant_type' => 'refresh_token', + 'refresh_token' => $this->getAuthorizationToken ()->getRefreshToken () + ); + // example request string + // client_id=0000000603DB0F&redirect_uri=http%3A%2F%2Fwww.contoso.com%2Fcallback.php&client_secret=LWILlT555GicSrIATma5qgyBXebRI&refresh_token=*LA9...//refresh token string shortened for example//...xRoX&grant_type=refresh_token + $response = PostmanUtils::remotePostGetBodyOnly ( $refreshUrl, $postvals ); + $this->processResponse ( $response ); + } + /** + * (non-PHPdoc) + * + * @see PostmanAuthenticationManager::getCallbackUri() + */ + public function getCallbackUri() { + return $this->callbackUri; + } + } +} diff --git a/Postman/Postman-Auth/PostmanAuthenticationManager.php b/Postman/Postman-Auth/PostmanAuthenticationManager.php new file mode 100644 index 0000000..c405459 --- /dev/null +++ b/Postman/Postman-Auth/PostmanAuthenticationManager.php @@ -0,0 +1,18 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! interface_exists ( "PostmanAuthenticationManager" )) { + interface PostmanAuthenticationManager { + const POSTMAN_AUTHORIZATION_IN_PROGRESS = 'request_oauth_permission'; + const FORCE_REFRESH_X_SECONDS_BEFORE_EXPIRE = 60; + public function isAccessTokenExpired(); + public function refreshToken(); + public function generateRequestTransactionId(); + public function requestVerificationCode($transactionId); + public function processAuthorizationGrantCode($transactionId); + public function getAuthorizationUrl(); + public function getTokenUrl(); + public function getCallbackUri(); + } +} diff --git a/Postman/Postman-Auth/PostmanAuthenticationManagerFactory.php b/Postman/Postman-Auth/PostmanAuthenticationManagerFactory.php new file mode 100644 index 0000000..799b999 --- /dev/null +++ b/Postman/Postman-Auth/PostmanAuthenticationManagerFactory.php @@ -0,0 +1,58 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanAuthenticationManagerFactory" )) { + + require_once 'PostmanGoogleAuthenticationManager.php'; + require_once 'PostmanMicrosoftAuthenticationManager.php'; + require_once 'PostmanNonOAuthAuthenticationManager.php'; + require_once 'PostmanYahooAuthenticationManager.php'; + + // + class PostmanAuthenticationManagerFactory { + private $logger; + + // singleton instance + public static function getInstance() { + static $inst = null; + if ($inst === null) { + $inst = new PostmanAuthenticationManagerFactory (); + } + return $inst; + } + private function __construct() { + $this->logger = new PostmanLogger ( get_class ( $this ) ); + } + public function createAuthenticationManager() { + $transport = PostmanTransportRegistry::getInstance ()->getSelectedTransport (); + return $this->createManager ( $transport ); + } + private function createManager(PostmanZendModuleTransport $transport) { + $options = PostmanOptions::getInstance (); + $authorizationToken = PostmanOAuthToken::getInstance (); + $authenticationType = $options->getAuthenticationType (); + $hostname = $options->getHostname (); + $clientId = $options->getClientId (); + $clientSecret = $options->getClientSecret (); + $senderEmail = $options->getMessageSenderEmail (); + $scribe = $transport->getScribe (); + $redirectUrl = $scribe->getCallbackUrl (); + if ($transport->isOAuthUsed ( $options->getAuthenticationType () )) { + if ($transport->isServiceProviderGoogle ( $hostname )) { + $authenticationManager = new PostmanGoogleAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl, $senderEmail ); + } else if ($transport->isServiceProviderMicrosoft ( $hostname )) { + $authenticationManager = new PostmanMicrosoftAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl ); + } else if ($transport->isServiceProviderYahoo ( $hostname )) { + $authenticationManager = new PostmanYahooAuthenticationManager ( $clientId, $clientSecret, $authorizationToken, $redirectUrl ); + } else { + assert ( false ); + } + } else { + $authenticationManager = new PostmanNonOAuthAuthenticationManager (); + } + $this->logger->debug ( 'Created ' . get_class ( $authenticationManager ) ); + return $authenticationManager; + } + } +}
\ No newline at end of file diff --git a/Postman/Postman-Auth/PostmanGoogleAuthenticationManager.php b/Postman/Postman-Auth/PostmanGoogleAuthenticationManager.php new file mode 100644 index 0000000..c00afba --- /dev/null +++ b/Postman/Postman-Auth/PostmanGoogleAuthenticationManager.php @@ -0,0 +1,122 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanGoogleAuthenticationManager" )) { + + require_once 'PostmanAbstractAuthenticationManager.php'; + require_once 'PostmanStateIdMissingException.php'; + + /** + * https://developers.google.com/accounts/docs/OAuth2WebServer + * https://developers.google.com/gmail/xoauth2_protocol + * https://developers.google.com/gmail/api/auth/scopes + */ + class PostmanGoogleAuthenticationManager extends PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager { + + // This endpoint is the target of the initial request. It handles active session lookup, authenticating the user, and user consent. + const GOOGLE_ENDPOINT = 'https://accounts.google.com/o/oauth2/auth'; + const GOOGLE_REFRESH = 'https://www.googleapis.com/oauth2/v3/token'; + + // this scope doesn't work + // Create, read, update, and delete drafts. Send messages and drafts. + const SCOPE_COMPOSE = 'https://www.googleapis.com/auth/gmail.compose'; + + // this scope doesn't work + // All read/write operations except immediate, permanent deletion of threads and messages, bypassing Trash. + const SCOPE_MODIFY = 'https://www.googleapis.com/auth/gmail.modify'; + + // Full access to the account, including permanent deletion of threads and messages. This scope should only be requested if your application needs to immediately and permanently delete threads and messages, bypassing Trash; all other actions can be performed with less permissive scopes. + const SCOPE_FULL_ACCESS = 'https://mail.google.com/'; + const AUTH_TEMP_ID = 'GOOGLE_OAUTH_TEMP_ID'; + const VENDOR_NAME = 'google'; + // the sender email address + private $senderEmail; + + /** + * Constructor + * + * Get a Client ID from https://account.live.com/developers/applications/index + */ + public function __construct($clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri, $senderEmail) { + assert ( ! empty ( $clientId ) ); + assert ( ! empty ( $clientSecret ) ); + assert ( ! empty ( $authorizationToken ) ); + assert ( ! empty ( $senderEmail ) ); + $logger = new PostmanLogger ( get_class ( $this ) ); + $this->senderEmail = $senderEmail; + parent::__construct ( $logger, $clientId, $clientSecret, $authorizationToken, $callbackUri ); + } + + /** + * The authorization sequence begins when your application redirects a browser to a Google URL; + * the URL includes query parameters that indicate the type of access being requested. + * + * As in other scenarios, Google handles user authentication, session selection, and user consent. + * The result is an authorization code, which Google returns to your application in a query string. + * + * (non-PHPdoc) + * + * @see PostmanAuthenticationManager::requestVerificationCode() + */ + public function requestVerificationCode($transactionId) { + $params = array ( + 'response_type' => 'code', + 'redirect_uri' => urlencode ( $this->getCallbackUri () ), + 'client_id' => $this->getClientId (), + 'scope' => urlencode ( self::SCOPE_FULL_ACCESS ), + 'access_type' => 'offline', + 'approval_prompt' => 'force', + 'state' => $transactionId, + 'login_hint' => $this->senderEmail + ); + + $authUrl = $this->getAuthorizationUrl () . '?' . build_query ( $params ); + + $this->getLogger ()->debug ( 'Requesting verification code from Google' ); + PostmanUtils::redirect ( $authUrl ); + } + + /** + * After receiving the authorization code, your application can exchange the code + * (along with a client ID and client secret) for an access token and, in some cases, + * a refresh token. + * + * This code is identical for Google and Hotmail + * + * @see PostmanAuthenticationManager::processAuthorizationGrantCode() + */ + public function processAuthorizationGrantCode($transactionId) { + if (isset ( $_GET ['code'] )) { + $this->getLogger ()->debug ( 'Found authorization code in request header' ); + $code = filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); + if (isset ( $_GET ['state'] ) && $_GET ['state'] == $transactionId) { + $this->getLogger ()->debug ( 'Found valid state in request header' ); + } else { + $this->getLogger ()->error ( 'The grant code from Google had no accompanying state and may be a forgery' ); + throw new PostmanStateIdMissingException (); + } + $postvals = array ( + 'client_id' => $this->getClientId (), + 'client_secret' => $this->getClientSecret (), + 'grant_type' => 'authorization_code', + 'redirect_uri' => $this->getCallbackUri (), + 'code' => $code + ); + $response = PostmanUtils::remotePostGetBodyOnly ( $this->getTokenUrl (), $postvals ); + $this->processResponse ( $response ); + $this->getAuthorizationToken ()->setVendorName ( self::VENDOR_NAME ); + return true; + } else { + $this->getLogger ()->debug ( 'Expected code in the request header but found none - user probably denied request' ); + return false; + } + } + public function getAuthorizationUrl() { + return self::GOOGLE_ENDPOINT; + } + public function getTokenUrl() { + return self::GOOGLE_REFRESH; + } + } +} diff --git a/Postman/Postman-Auth/PostmanMicrosoftAuthenticationManager.php b/Postman/Postman-Auth/PostmanMicrosoftAuthenticationManager.php new file mode 100644 index 0000000..96fb529 --- /dev/null +++ b/Postman/Postman-Auth/PostmanMicrosoftAuthenticationManager.php @@ -0,0 +1,107 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanMicrosoftAuthenticationManager" )) { + + require_once 'PostmanAbstractAuthenticationManager.php'; + + /** + * https://msdn.microsoft.com/en-us/library/hh243647.aspx (Seems to be the most up-to-date doc on OAuth 2.0 + * https://msdn.microsoft.com/en-us/library/hh243649.aspx (Seems to be the most up-to-date examples on using the API) + * https://msdn.microsoft.com/en-us/library/ff750690.aspx OAuth WRAP (Messenger Connect) + * https://msdn.microsoft.com/en-us/library/ff749624.aspx Working with OAuth WRAP (Messenger Connect) + * https://gist.github.com/kayalshri/5262641 Working example from Giriraj Namachivayam (kayalshri) + */ + class PostmanMicrosoftAuthenticationManager extends PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager { + + // constants + const WINDOWS_LIVE_ENDPOINT = 'https://login.live.com/oauth20_authorize.srf'; + const WINDOWS_LIVE_REFRESH = 'https://login.live.com/oauth20_token.srf'; + + // http://stackoverflow.com/questions/7163786/messenger-connect-oauth-wrap-api-to-get-user-emails + // http://quabr.com/26329398/outlook-oauth-send-emails-with-wl-imap-scope-in-php + const SCOPE = 'wl.imap,wl.offline_access'; + const VENDOR_NAME = 'microsoft'; + + /** + * Constructor + * + * Get a Client ID from https://account.live.com/developers/applications/index + */ + public function __construct($clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri) { + assert ( ! empty ( $clientId ) ); + assert ( ! empty ( $clientSecret ) ); + assert ( ! empty ( $authorizationToken ) ); + assert ( ! empty ( $callbackUri ) ); + $logger = new PostmanLogger ( get_class ( $this ) ); + parent::__construct ( $logger, $clientId, $clientSecret, $authorizationToken, $callbackUri ); + } + + /** + * ********************************************** + * Request Verification Code + * https://msdn.microsoft.com/en-us/library/ff749592.aspx + * + * The following example shows a URL that enables + * a user to provide consent to an application by + * using a Windows Live ID. + * + * When successful, this URL returns the user to + * your application, along with a verification + * code. + * ********************************************** + */ + public function requestVerificationCode($transactionId) { + $params = array ( + 'response_type' => 'code', + 'redirect_uri' => urlencode ( $this->getCallbackUri () ), + 'client_id' => $this->getClientId (), + 'client_secret' => $this->getClientSecret (), + 'scope' => urlencode ( self::SCOPE ), + 'access_type' => 'offline', + 'approval_prompt' => 'force' + ); + + $authUrl = $this->getAuthorizationUrl () . '?' . build_query ( $params ); + + $this->getLogger ()->debug ( 'Requesting verification code from Microsoft' ); + PostmanUtils::redirect ( $authUrl ); + } + + /** + * ********************************************** + * If we have a code back from the OAuth 2.0 flow, + * we need to exchange that for an access token. + * We store the resultant access token + * bundle in the session, and redirect to ourself. + * ********************************************** + */ + public function processAuthorizationGrantCode($transactionId) { + if (isset ( $_GET ['code'] )) { + $code = filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); + $this->getLogger ()->debug ( 'Found authorization code in request header' ); + $postvals = array ( + 'client_id' => $this->getClientId (), + 'client_secret' => $this->getClientSecret (), + 'grant_type' => 'authorization_code', + 'redirect_uri' => $this->getCallbackUri (), + 'code' => $code + ); + $response = PostmanUtils::remotePostGetBodyOnly ( $this->getTokenUrl (), $postvals ); + $this->processResponse ( $response ); + $this->getAuthorizationToken ()->setVendorName ( self::VENDOR_NAME ); + return true; + } else { + $this->getLogger ()->debug ( 'Expected code in the request header but found none - user probably denied request' ); + return false; + } + } + public function getAuthorizationUrl() { + return self::WINDOWS_LIVE_ENDPOINT; + } + public function getTokenUrl() { + return self::WINDOWS_LIVE_REFRESH; + } + } +} diff --git a/Postman/Postman-Auth/PostmanNonOAuthAuthenticationManager.php b/Postman/Postman-Auth/PostmanNonOAuthAuthenticationManager.php new file mode 100644 index 0000000..ed4f0c3 --- /dev/null +++ b/Postman/Postman-Auth/PostmanNonOAuthAuthenticationManager.php @@ -0,0 +1,46 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanNonOAuthAuthenticationManager" )) { + + require_once 'PostmanAuthenticationManager.php'; + class PostmanNonOAuthAuthenticationManager implements PostmanAuthenticationManager { + + /** + */ + public function isAccessTokenExpired() { + return false; + } + + /** + * (non-PHPdoc) + * + * @see PostmanAuthenticationManager::requestVerificationCode() + */ + public function requestVerificationCode($transactionId) { + // otherwise known as IllegaStateException + assert ( false ); + } + public function processAuthorizationGrantCode($transactionId) { + // otherwise known as IllegaStateException + assert ( false ); + } + public function refreshToken() { + // no-op + } + public function getAuthorizationUrl() { + return null; + } + public function getTokenUrl() { + return null; + } + public function getCallbackUri() { + return null; + } + public function generateRequestTransactionId() { + // otherwise known as IllegaStateException + assert ( false ); + } + } +} diff --git a/Postman/Postman-Auth/PostmanStateIdMissingException.php b/Postman/Postman-Auth/PostmanStateIdMissingException.php new file mode 100644 index 0000000..afa16c2 --- /dev/null +++ b/Postman/Postman-Auth/PostmanStateIdMissingException.php @@ -0,0 +1,8 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( 'PostmanStateIdMissingException' )) { + class PostmanStateIdMissingException extends Exception { + } +}
\ No newline at end of file diff --git a/Postman/Postman-Auth/PostmanYahooAuthenticationManager.php b/Postman/Postman-Auth/PostmanYahooAuthenticationManager.php new file mode 100644 index 0000000..86c35d9 --- /dev/null +++ b/Postman/Postman-Auth/PostmanYahooAuthenticationManager.php @@ -0,0 +1,136 @@ +<?php +if ( ! defined( 'ABSPATH' ) ) { + exit; // Exit if accessed directly +} +if (! class_exists ( "PostmanYahooAuthenticationManager" )) { + + require_once 'PostmanAbstractAuthenticationManager.php'; + require_once 'PostmanStateIdMissingException.php'; + + /** + * Super-simple. + * I should have started with Yahoo. + * + * https://developer.yahoo.com/oauth2/guide/ + * Get a Client ID at https://developer.apps.yahoo.com/projects + * + * @author jasonhendriks + */ + class PostmanYahooAuthenticationManager extends PostmanAbstractAuthenticationManager implements PostmanAuthenticationManager { + + // This endpoint is the target of the initial request. It handles active session lookup, authenticating the user, and user consent. + const AUTHORIZATION_URL = 'https://api.login.yahoo.com/oauth2/request_auth'; + const GET_TOKEN_URL = 'https://api.login.yahoo.com/oauth2/get_token'; + + // The SESSION key for the OAuth Transaction Id + const AUTH_TEMP_ID = 'OAUTH_TEMP_ID'; + const VENDOR_NAME = 'yahoo'; + + /** + * Constructor + * + * Get a Client ID from https://account.live.com/developers/applications/index + */ + public function __construct($clientId, $clientSecret, PostmanOAuthToken $authorizationToken, $callbackUri) { + assert ( ! empty ( $clientId ) ); + assert ( ! empty ( $clientSecret ) ); + assert ( ! empty ( $authorizationToken ) ); + assert ( ! empty ( $callbackUri ) ); + $logger = new PostmanLogger ( get_class ( $this ) ); + parent::__construct ( $logger, $clientId, $clientSecret, $authorizationToken, $callbackUri ); + } + + /** + * The authorization sequence begins when your application redirects a browser to a Google URL; + * the URL includes query parameters that indicate the type of access being requested. + * + * As in other scenarios, Google handles user authentication, session selection, and user consent. + * The result is an authorization code, which Google returns to your application in a query string. + * + * (non-PHPdoc) + * + * @see PostmanAuthenticationManager::requestVerificationCode() + */ + public function requestVerificationCode($transactionId) { + $params = array ( + 'response_type' => 'code', + 'redirect_uri' => urlencode ( $this->getCallbackUri () ), + 'client_id' => $this->getClientId (), + 'state' => $transactionId, + 'language' => get_locale () + ); + + $authUrl = $this->getAuthorizationUrl () . '?' . build_query ( $params ); + + $this->getLogger ()->debug ( 'Requesting verification code from Yahoo' ); + PostmanUtils::redirect ( $authUrl ); + } + + /** + * After receiving the authorization code, your application can exchange the code + * (along with a client ID and client secret) for an access token and, in some cases, + * a refresh token. + * + * (non-PHPdoc) + * + * @see PostmanAuthenticationManager::processAuthorizationGrantCode() + */ + public function processAuthorizationGrantCode($transactionId) { + if (isset ( $_GET ['code'] )) { + $code = filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); + $this->getLogger ()->debug ( sprintf ( 'Found authorization code %s in request header', $code ) ); + if (isset ( $_GET ['state'] ) && $_GET ['state'] == $transactionId) { + $this->getLogger ()->debug ( 'Found valid state in request header' ); + } else { + $this->getLogger ()->error ( 'The grant code from Yahoo had no accompanying state and may be a forgery' ); + throw new PostmanStateIdMissingException (); + } + // Note: The Authorization: Basic authorization header is generated through a Base64 encoding of client_id:client_secret per RFC 2617. + // header("Authorization: Basic " . base64_encode($username . ":" . $password); + $headers = array ( + 'Authorization' => sprintf ( "Basic %s", base64_encode ( $this->getClientId () . ':' . $this->getClientSecret () ) ) + ); + $postvals = array ( + 'code' => $code, + 'grant_type' => 'authorization_code', + 'redirect_uri' => $this->getCallbackUri () + ); + $response = PostmanUtils::remotePostGetBodyOnly ( $this->getTokenUrl (), $postvals, $headers ); + $this->processResponse ( $response ); + $this->getAuthorizationToken ()->setVendorName ( self::VENDOR_NAME ); + return true; + } else { + $this->getLogger ()->debug ( 'Expected code in the request header but found none - user probably denied request' ); + return false; + } + } + + /** + * Step 5: Exchange refresh token for new access token + * After the access token expires, you can use the refresh token, which has a long lifetime, to get a new access token. + */ + public function refreshToken() { + $this->getLogger ()->debug ( 'Refreshing Token' ); + $refreshUrl = $this->getTokenUrl (); + $callbackUrl = $this->getCallbackUri (); + assert ( ! empty ( $refreshUrl ) ); + assert ( ! empty ( $callbackUrl ) ); + $headers = array ( + 'Authorization' => sprintf ( "Basic %s", base64_encode ( $this->getClientId () . ':' . $this->getClientSecret () ) ) + ); + $postvals = array ( + 'redirect_uri' => $callbackUrl, + 'grant_type' => 'refresh_token', + 'refresh_token' => $this->getAuthorizationToken ()->getRefreshToken () + ); + $response = PostmanUtils::remotePostGetBodyOnly ( $this->getTokenUrl (), $postvals, $headers ); + $this->processResponse ( $response ); + } + public function getAuthorizationUrl() { + return self::AUTHORIZATION_URL; + } + public function getTokenUrl() { + return self::GET_TOKEN_URL; + } + } +} |