+++ /dev/null
-<?php
-
-/**
- * @mainpage
- * OAuth 2.0 server in PHP, originally written for
- * <a href="http://www.opendining.net/"> Open Dining</a>. Supports
- * <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-10">IETF draft v10</a>.
- *
- * Source repo has sample servers implementations for
- * <a href="http://php.net/manual/en/book.pdo.php"> PHP Data Objects</a> and
- * <a href="http://www.mongodb.org/">MongoDB</a>. Easily adaptable to other
- * storage engines.
- *
- * PHP Data Objects supports a variety of databases, including MySQL,
- * Microsoft SQL Server, SQLite, and Oracle, so you can try out the sample
- * to see how it all works.
- *
- * We're expanding the wiki to include more helpful documentation, but for
- * now, your best bet is to view the oauth.php source - it has lots of
- * comments.
- *
- * @author Tim Ridgely <tim.ridgely@gmail.com>
- * @author Aaron Parecki <aaron@parecki.com>
- * @author Edison Wong <hswong3i@pantarei-design.com>
- *
- * @see http://code.google.com/p/oauth2-php/
- */
-
-
-/**
- * The default duration in seconds of the access token lifetime.
- */
-define("OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME", 3600);
-
-/**
- * The default duration in seconds of the authorization code lifetime.
- */
-define("OAUTH2_DEFAULT_AUTH_CODE_LIFETIME", 30);
-
-/**
- * The default duration in seconds of the refresh token lifetime.
- */
-define("OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME", 1209600);
-
-
-/**
- * @defgroup oauth2_section_2 Client Credentials
- * @{
- *
- * When interacting with the authorization server, the client identifies
- * itself using a client identifier and authenticates using a set of
- * client credentials. This specification provides one mechanism for
- * authenticating the client using password credentials.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2
- */
-
-/**
- * Regex to filter out the client identifier (described in Section 2 of IETF draft).
- *
- * IETF draft does not prescribe a format for these, however I've arbitrarily
- * chosen alphanumeric strings with hyphens and underscores, 3-32 characters
- * long.
- *
- * Feel free to change.
- */
-define("OAUTH2_CLIENT_ID_REGEXP", "/^[a-z0-9-_]{3,32}$/i");
-
-/**
- * @}
- */
-
-
-/**
- * @defgroup oauth2_section_3 Obtaining End-User Authorization
- * @{
- *
- * When the client interacts with an end-user, the end-user MUST first
- * grant the client authorization to access its protected resources.
- * Once obtained, the end-user access grant is expressed as an
- * authorization code which the client uses to obtain an access token.
- * To obtain an end-user authorization, the client sends the end-user to
- * the end-user authorization endpoint.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
- */
-
-/**
- * Denotes "token" authorization response type.
- */
-define("OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN", "token");
-
-/**
- * Denotes "code" authorization response type.
- */
-define("OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE", "code");
-
-/**
- * Denotes "code-and-token" authorization response type.
- */
-define("OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN", "code-and-token");
-
-/**
- * Regex to filter out the authorization response type.
- */
-define("OAUTH2_AUTH_RESPONSE_TYPE_REGEXP", "/^(token|code|code-and-token)$/");
-
-/**
- * @}
- */
-
-
-/**
- * @defgroup oauth2_section_4 Obtaining an Access Token
- * @{
- *
- * The client obtains an access token by authenticating with the
- * authorization server and presenting its access grant (in the form of
- * an authorization code, resource owner credentials, an assertion, or a
- * refresh token).
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4
- */
-
-/**
- * Denotes "authorization_code" grant types (for token obtaining).
- */
-define("OAUTH2_GRANT_TYPE_AUTH_CODE", "authorization_code");
-
-/**
- * Denotes "password" grant types (for token obtaining).
- */
-define("OAUTH2_GRANT_TYPE_USER_CREDENTIALS", "password");
-
-/**
- * Denotes "assertion" grant types (for token obtaining).
- */
-define("OAUTH2_GRANT_TYPE_ASSERTION", "assertion");
-
-/**
- * Denotes "refresh_token" grant types (for token obtaining).
- */
-define("OAUTH2_GRANT_TYPE_REFRESH_TOKEN", "refresh_token");
-
-/**
- * Denotes "none" grant types (for token obtaining).
- */
-define("OAUTH2_GRANT_TYPE_NONE", "none");
-
-/**
- * Regex to filter out the grant type.
- */
-define("OAUTH2_GRANT_TYPE_REGEXP", "/^(authorization_code|password|assertion|refresh_token|none)$/");
-
-/**
- * @}
- */
-
-
-/**
- * @defgroup oauth2_section_5 Accessing a Protected Resource
- * @{
- *
- * Clients access protected resources by presenting an access token to
- * the resource server. Access tokens act as bearer tokens, where the
- * token string acts as a shared symmetric secret. This requires
- * treating the access token with the same care as other secrets (e.g.
- * end-user passwords). Access tokens SHOULD NOT be sent in the clear
- * over an insecure channel.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
- */
-
-/**
- * Used to define the name of the OAuth access token parameter (POST/GET/etc.).
- *
- * IETF Draft sections 5.1.2 and 5.1.3 specify that it should be called
- * "oauth_token" but other implementations use things like "access_token".
- *
- * I won't be heartbroken if you change it, but it might be better to adhere
- * to the spec.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1.2
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1.3
- */
-define("OAUTH2_TOKEN_PARAM_NAME", "oauth_token");
-
-/**
- * @}
- */
-
-
-/**
- * @defgroup oauth2_http_status HTTP status code
- * @{
- */
-
-/**
- * "Found" HTTP status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
- */
-define("OAUTH2_HTTP_FOUND", "302 Found");
-
-/**
- * "Bad Request" HTTP status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_HTTP_BAD_REQUEST", "400 Bad Request");
-
-/**
- * "Unauthorized" HTTP status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_HTTP_UNAUTHORIZED", "401 Unauthorized");
-
-/**
- * "Forbidden" HTTP status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_HTTP_FORBIDDEN", "403 Forbidden");
-
-/**
- * @}
- */
-
-
-/**
- * @defgroup oauth2_error Error handling
- * @{
- *
- * @todo Extend for i18n.
- */
-
-/**
- * The request is missing a required parameter, includes an unsupported
- * parameter or parameter value, or is otherwise malformed.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_ERROR_INVALID_REQUEST", "invalid_request");
-
-/**
- * The client identifier provided is invalid.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- */
-define("OAUTH2_ERROR_INVALID_CLIENT", "invalid_client");
-
-/**
- * The client is not authorized to use the requested response type.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- */
-define("OAUTH2_ERROR_UNAUTHORIZED_CLIENT", "unauthorized_client");
-
-/**
- * The redirection URI provided does not match a pre-registered value.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- */
-define("OAUTH2_ERROR_REDIRECT_URI_MISMATCH", "redirect_uri_mismatch");
-
-/**
- * The end-user or authorization server denied the request.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- */
-define("OAUTH2_ERROR_USER_DENIED", "access_denied");
-
-/**
- * The requested response type is not supported by the authorization server.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- */
-define("OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE", "unsupported_response_type");
-
-/**
- * The requested scope is invalid, unknown, or malformed.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- */
-define("OAUTH2_ERROR_INVALID_SCOPE", "invalid_scope");
-
-/**
- * The provided access grant is invalid, expired, or revoked (e.g. invalid
- * assertion, expired authorization token, bad end-user password credentials,
- * or mismatching authorization code and redirection URI).
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- */
-define("OAUTH2_ERROR_INVALID_GRANT", "invalid_grant");
-
-/**
- * The access grant included - its type or another attribute - is not
- * supported by the authorization server.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
- */
-define("OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE", "unsupported_grant_type");
-
-/**
- * The access token provided is invalid. Resource servers SHOULD use this
- * error code when receiving an expired token which cannot be refreshed to
- * indicate to the client that a new authorization is necessary. The resource
- * server MUST respond with the HTTP 401 (Unauthorized) status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_ERROR_INVALID_TOKEN", "invalid_token");
-
-/**
- * The access token provided has expired. Resource servers SHOULD only use
- * this error code when the client is expected to be able to handle the
- * response and request a new access token using the refresh token issued
- * with the expired access token. The resource server MUST respond with the
- * HTTP 401 (Unauthorized) status code.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_ERROR_EXPIRED_TOKEN", "expired_token");
-
-/**
- * The request requires higher privileges than provided by the access token.
- * The resource server SHOULD respond with the HTTP 403 (Forbidden) status
- * code and MAY include the "scope" attribute with the scope necessary to
- * access the protected resource.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
- */
-define("OAUTH2_ERROR_INSUFFICIENT_SCOPE", "insufficient_scope");
-
-/**
- * @}
- */
-
-/**
- * OAuth2.0 draft v10 server-side implementation.
- *
- * @author Originally written by Tim Ridgely <tim.ridgely@gmail.com>.
- * @author Updated to draft v10 by Aaron Parecki <aaron@parecki.com>.
- * @author Debug, coding style clean up and documented by Edison Wong <hswong3i@pantarei-design.com>.
- */
-abstract class OAuth2 {
-
- /**
- * Array of persistent variables stored.
- */
- protected $conf = array();
-
- /**
- * Returns a persistent variable.
- *
- * To avoid problems, always use lower case for persistent variable names.
- *
- * @param $name
- * The name of the variable to return.
- * @param $default
- * The default value to use if this variable has never been set.
- *
- * @return
- * The value of the variable.
- */
- public function getVariable($name, $default = NULL) {
- return isset($this->conf[$name]) ? $this->conf[$name] : $default;
- }
-
- /**
- * Sets a persistent variable.
- *
- * To avoid problems, always use lower case for persistent variable names.
- *
- * @param $name
- * The name of the variable to set.
- * @param $value
- * The value to set.
- */
- public function setVariable($name, $value) {
- $this->conf[$name] = $value;
- return $this;
- }
-
- // Subclasses must implement the following functions.
-
- /**
- * Make sure that the client credentials is valid.
- *
- * @param $client_id
- * Client identifier to be check with.
- * @param $client_secret
- * (optional) If a secret is required, check that they've given the right one.
- *
- * @return
- * TRUE if client credentials are valid, and MUST return FALSE if invalid.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2.1
- *
- * @ingroup oauth2_section_2
- */
- abstract protected function checkClientCredentials($client_id, $client_secret = NULL);
-
- /**
- * Get the registered redirect URI of corresponding client_id.
- *
- * OAuth says we should store request URIs for each registered client.
- * Implement this function to grab the stored URI for a given client id.
- *
- * @param $client_id
- * Client identifier to be check with.
- *
- * @return
- * Registered redirect URI of corresponding client identifier, and MUST
- * return FALSE if the given client does not exist or is invalid.
- *
- * @ingroup oauth2_section_3
- */
- abstract protected function getRedirectUri($client_id);
-
- /**
- * Look up the supplied oauth_token from storage.
- *
- * We need to retrieve access token data as we create and verify tokens.
- *
- * @param $oauth_token
- * oauth_token to be check with.
- *
- * @return
- * An associative array as below, and return NULL if the supplied oauth_token
- * is invalid:
- * - client_id: Stored client identifier.
- * - expires: Stored expiration in unix timestamp.
- * - scope: (optional) Stored scope values in space-separated string.
- *
- * @ingroup oauth2_section_5
- */
- abstract protected function getAccessToken($oauth_token);
-
- /**
- * Store the supplied access token values to storage.
- *
- * We need to store access token data as we create and verify tokens.
- *
- * @param $oauth_token
- * oauth_token to be stored.
- * @param $client_id
- * Client identifier to be stored.
- * @param $expires
- * Expiration to be stored.
- * @param $scope
- * (optional) Scopes to be stored in space-separated string.
- *
- * @ingroup oauth2_section_4
- */
- abstract protected function setAccessToken($oauth_token, $client_id, $expires, $scope = NULL);
-
- // Stuff that should get overridden by subclasses.
- //
- // I don't want to make these abstract, because then subclasses would have
- // to implement all of them, which is too much work.
- //
- // So they're just stubs. Override the ones you need.
-
- /**
- * Return supported grant types.
- *
- * You should override this function with something, or else your OAuth
- * provider won't support any grant types!
- *
- * @return
- * A list as below. If you support all grant types, then you'd do:
- * @code
- * return array(
- * OAUTH2_GRANT_TYPE_AUTH_CODE,
- * OAUTH2_GRANT_TYPE_USER_CREDENTIALS,
- * OAUTH2_GRANT_TYPE_ASSERTION,
- * OAUTH2_GRANT_TYPE_REFRESH_TOKEN,
- * OAUTH2_GRANT_TYPE_NONE,
- * );
- * @endcode
- *
- * @ingroup oauth2_section_4
- */
- protected function getSupportedGrantTypes() {
- return array();
- }
-
- /**
- * Return supported authorization response types.
- *
- * You should override this function with your supported response types.
- *
- * @return
- * A list as below. If you support all authorization response types,
- * then you'd do:
- * @code
- * return array(
- * OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE,
- * OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN,
- * OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN,
- * );
- * @endcode
- *
- * @ingroup oauth2_section_3
- */
- protected function getSupportedAuthResponseTypes() {
- return array(
- OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE,
- OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN,
- OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN
- );
- }
-
- /**
- * Return supported scopes.
- *
- * If you want to support scope use, then have this function return a list
- * of all acceptable scopes (used to throw the invalid-scope error).
- *
- * @return
- * A list as below, for example:
- * @code
- * return array(
- * 'my-friends',
- * 'photos',
- * 'whatever-else',
- * );
- * @endcode
- *
- * @ingroup oauth2_section_3
- */
- protected function getSupportedScopes() {
- return array();
- }
-
- /**
- * Check restricted authorization response types of corresponding Client
- * identifier.
- *
- * If you want to restrict clients to certain authorization response types,
- * override this function.
- *
- * @param $client_id
- * Client identifier to be check with.
- * @param $response_type
- * Authorization response type to be check with, would be one of the
- * values contained in OAUTH2_AUTH_RESPONSE_TYPE_REGEXP.
- *
- * @return
- * TRUE if the authorization response type is supported by this
- * client identifier, and FALSE if it isn't.
- *
- * @ingroup oauth2_section_3
- */
- protected function checkRestrictedAuthResponseType($client_id, $response_type) {
- return TRUE;
- }
-
- /**
- * Check restricted grant types of corresponding client identifier.
- *
- * If you want to restrict clients to certain grant types, override this
- * function.
- *
- * @param $client_id
- * Client identifier to be check with.
- * @param $grant_type
- * Grant type to be check with, would be one of the values contained in
- * OAUTH2_GRANT_TYPE_REGEXP.
- *
- * @return
- * TRUE if the grant type is supported by this client identifier, and
- * FALSE if it isn't.
- *
- * @ingroup oauth2_section_4
- */
- protected function checkRestrictedGrantType($client_id, $grant_type) {
- return TRUE;
- }
-
- // Functions that help grant access tokens for various grant types.
-
- /**
- * Fetch authorization code data (probably the most common grant type).
- *
- * Retrieve the stored data for the given authorization code.
- *
- * Required for OAUTH2_GRANT_TYPE_AUTH_CODE.
- *
- * @param $code
- * Authorization code to be check with.
- *
- * @return
- * An associative array as below, and NULL if the code is invalid:
- * - client_id: Stored client identifier.
- * - redirect_uri: Stored redirect URI.
- * - expires: Stored expiration in unix timestamp.
- * - scope: (optional) Stored scope values in space-separated string.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.1
- *
- * @ingroup oauth2_section_4
- */
- protected function getAuthCode($code) {
- return NULL;
- }
-
- /**
- * Take the provided authorization code values and store them somewhere.
- *
- * This function should be the storage counterpart to getAuthCode().
- *
- * If storage fails for some reason, we're not currently checking for
- * any sort of success/failure, so you should bail out of the script
- * and provide a descriptive fail message.
- *
- * Required for OAUTH2_GRANT_TYPE_AUTH_CODE.
- *
- * @param $code
- * Authorization code to be stored.
- * @param $client_id
- * Client identifier to be stored.
- * @param $redirect_uri
- * Redirect URI to be stored.
- * @param $expires
- * Expiration to be stored.
- * @param $scope
- * (optional) Scopes to be stored in space-separated string.
- *
- * @ingroup oauth2_section_4
- */
- protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) {
- }
-
- /**
- * Grant access tokens for basic user credentials.
- *
- * Check the supplied username and password for validity.
- *
- * You can also use the $client_id param to do any checks required based
- * on a client, if you need that.
- *
- * Required for OAUTH2_GRANT_TYPE_USER_CREDENTIALS.
- *
- * @param $client_id
- * Client identifier to be check with.
- * @param $username
- * Username to be check with.
- * @param $password
- * Password to be check with.
- *
- * @return
- * TRUE if the username and password are valid, and FALSE if it isn't.
- * Moreover, if the username and password are valid, and you want to
- * verify the scope of a user's access, return an associative array
- * with the scope values as below. We'll check the scope you provide
- * against the requested scope before providing an access token:
- * @code
- * return array(
- * 'scope' => <stored scope values (space-separated string)>,
- * );
- * @endcode
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.2
- *
- * @ingroup oauth2_section_4
- */
- protected function checkUserCredentials($client_id, $username, $password) {
- return FALSE;
- }
-
- /**
- * Grant access tokens for assertions.
- *
- * Check the supplied assertion for validity.
- *
- * You can also use the $client_id param to do any checks required based
- * on a client, if you need that.
- *
- * Required for OAUTH2_GRANT_TYPE_ASSERTION.
- *
- * @param $client_id
- * Client identifier to be check with.
- * @param $assertion_type
- * The format of the assertion as defined by the authorization server.
- * @param $assertion
- * The assertion.
- *
- * @return
- * TRUE if the assertion is valid, and FALSE if it isn't. Moreover, if
- * the assertion is valid, and you want to verify the scope of an access
- * request, return an associative array with the scope values as below.
- * We'll check the scope you provide against the requested scope before
- * providing an access token:
- * @code
- * return array(
- * 'scope' => <stored scope values (space-separated string)>,
- * );
- * @endcode
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
- *
- * @ingroup oauth2_section_4
- */
- protected function checkAssertion($client_id, $assertion_type, $assertion) {
- return FALSE;
- }
-
- /**
- * Grant refresh access tokens.
- *
- * Retrieve the stored data for the given refresh token.
- *
- * Required for OAUTH2_GRANT_TYPE_REFRESH_TOKEN.
- *
- * @param $refresh_token
- * Refresh token to be check with.
- *
- * @return
- * An associative array as below, and NULL if the refresh_token is
- * invalid:
- * - client_id: Stored client identifier.
- * - expires: Stored expiration unix timestamp.
- * - scope: (optional) Stored scope values in space-separated string.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.4
- *
- * @ingroup oauth2_section_4
- */
- protected function getRefreshToken($refresh_token) {
- return NULL;
- }
-
- /**
- * Take the provided refresh token values and store them somewhere.
- *
- * This function should be the storage counterpart to getRefreshToken().
- *
- * If storage fails for some reason, we're not currently checking for
- * any sort of success/failure, so you should bail out of the script
- * and provide a descriptive fail message.
- *
- * Required for OAUTH2_GRANT_TYPE_REFRESH_TOKEN.
- *
- * @param $refresh_token
- * Refresh token to be stored.
- * @param $client_id
- * Client identifier to be stored.
- * @param $expires
- * expires to be stored.
- * @param $scope
- * (optional) Scopes to be stored in space-separated string.
- *
- * @ingroup oauth2_section_4
- */
- protected function setRefreshToken($refresh_token, $client_id, $expires, $scope = NULL) {
- return;
- }
-
- /**
- * Expire a used refresh token.
- *
- * This is not explicitly required in the spec, but is almost implied.
- * After granting a new refresh token, the old one is no longer useful and
- * so should be forcibly expired in the data store so it can't be used again.
- *
- * If storage fails for some reason, we're not currently checking for
- * any sort of success/failure, so you should bail out of the script
- * and provide a descriptive fail message.
- *
- * @param $refresh_token
- * Refresh token to be expirse.
- *
- * @ingroup oauth2_section_4
- */
- protected function unsetRefreshToken($refresh_token) {
- return;
- }
-
- /**
- * Grant access tokens for the "none" grant type.
- *
- * Not really described in the IETF Draft, so I just left a method
- * stub... Do whatever you want!
- *
- * Required for OAUTH2_GRANT_TYPE_NONE.
- *
- * @ingroup oauth2_section_4
- */
- protected function checkNoneAccess($client_id) {
- return FALSE;
- }
-
- /**
- * Get default authentication realm for WWW-Authenticate header.
- *
- * Change this to whatever authentication realm you want to send in a
- * WWW-Authenticate header.
- *
- * @return
- * A string that you want to send in a WWW-Authenticate header.
- *
- * @ingroup oauth2_error
- */
- protected function getDefaultAuthenticationRealm() {
- return "Service";
- }
-
- // End stuff that should get overridden.
-
- /**
- * Creates an OAuth2.0 server-side instance.
- *
- * @param $config
- * An associative array as below:
- * - access_token_lifetime: (optional) The lifetime of access token in
- * seconds.
- * - auth_code_lifetime: (optional) The lifetime of authorization code in
- * seconds.
- * - refresh_token_lifetime: (optional) The lifetime of refresh token in
- * seconds.
- * - display_error: (optional) Whether to show verbose error messages in
- * the response.
- */
- public function __construct($config = array()) {
- foreach ($config as $name => $value) {
- $this->setVariable($name, $value);
- }
- }
-
- // Resource protecting (Section 5).
-
- /**
- * Check that a valid access token has been provided.
- *
- * The scope parameter defines any required scope that the token must have.
- * If a scope param is provided and the token does not have the required
- * scope, we bounce the request.
- *
- * Some implementations may choose to return a subset of the protected
- * resource (i.e. "public" data) if the user has not provided an access
- * token or if the access token is invalid or expired.
- *
- * The IETF spec says that we should send a 401 Unauthorized header and
- * bail immediately so that's what the defaults are set to.
- *
- * @param $scope
- * A space-separated string of required scope(s), if you want to check
- * for scope.
- * @param $exit_not_present
- * If TRUE and no access token is provided, send a 401 header and exit,
- * otherwise return FALSE.
- * @param $exit_invalid
- * If TRUE and the implementation of getAccessToken() returns NULL, exit,
- * otherwise return FALSE.
- * @param $exit_expired
- * If TRUE and the access token has expired, exit, otherwise return FALSE.
- * @param $exit_scope
- * If TRUE the access token does not have the required scope(s), exit,
- * otherwise return FALSE.
- * @param $realm
- * If you want to specify a particular realm for the WWW-Authenticate
- * header, supply it here.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
- *
- * @ingroup oauth2_section_5
- */
- public function verifyAccessToken($scope = NULL, $exit_not_present = TRUE, $exit_invalid = TRUE, $exit_expired = TRUE, $exit_scope = TRUE, $realm = NULL) {
- $token_param = $this->getAccessTokenParams();
- if ($token_param === FALSE) // Access token was not provided
- return $exit_not_present ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_BAD_REQUEST, $realm, OAUTH2_ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', NULL, $scope) : FALSE;
- // Get the stored token data (from the implementing subclass)
- $token = $this->getAccessToken($token_param);
- if ($token === NULL)
- return $exit_invalid ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_INVALID_TOKEN, 'The access token provided is invalid.', NULL, $scope) : FALSE;
-
- // Check token expiration (I'm leaving this check separated, later we'll fill in better error messages)
- if (isset($token["expires"]) && time() > $token["expires"])
- return $exit_expired ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_EXPIRED_TOKEN, 'The access token provided has expired.', NULL, $scope) : FALSE;
-
- // Check scope, if provided
- // If token doesn't have a scope, it's NULL/empty, or it's insufficient, then throw an error
- if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->checkScope($scope, $token["scope"])))
- return $exit_scope ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_FORBIDDEN, $realm, OAUTH2_ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', NULL, $scope) : FALSE;
-
- return TRUE;
- }
-
- /**
- * Check if everything in required scope is contained in available scope.
- *
- * @param $required_scope
- * Required scope to be check with.
- * @param $available_scope
- * Available scope to be compare with.
- *
- * @return
- * TRUE if everything in required scope is contained in available scope,
- * and False if it isn't.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
- *
- * @ingroup oauth2_section_5
- */
- private function checkScope($required_scope, $available_scope) {
- // The required scope should match or be a subset of the available scope
- if (!is_array($required_scope))
- $required_scope = explode(" ", $required_scope);
-
- if (!is_array($available_scope))
- $available_scope = explode(" ", $available_scope);
-
- return (count(array_diff($required_scope, $available_scope)) == 0);
- }
-
- /**
- * Pulls the access token out of the HTTP request.
- *
- * Either from the Authorization header or GET/POST/etc.
- *
- * @return
- * Access token value if present, and FALSE if it isn't.
- *
- * @todo Support PUT or DELETE.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1
- *
- * @ingroup oauth2_section_5
- */
- private function getAccessTokenParams() {
- $auth_header = $this->getAuthorizationHeader();
-
- if ($auth_header !== FALSE) {
- // Make sure only the auth header is set
- if (isset($_GET[OAUTH2_TOKEN_PARAM_NAME]) || isset($_POST[OAUTH2_TOKEN_PARAM_NAME]))
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Auth token found in GET or POST when token present in header');
-
- $auth_header = trim($auth_header);
-
- // Make sure it's Token authorization
- if (strcmp(substr($auth_header, 0, 5), "OAuth ") !== 0)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Auth header found that doesn\'t start with "OAuth"');
-
- // Parse the rest of the header
- if (preg_match('/\s*OAuth\s*="(.+)"/', substr($auth_header, 5), $matches) == 0 || count($matches) < 2)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Malformed auth header');
-
- return $matches[1];
- }
-
- if (isset($_GET[OAUTH2_TOKEN_PARAM_NAME])) {
- if (isset($_POST[OAUTH2_TOKEN_PARAM_NAME])) // Both GET and POST are not allowed
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Only send the token in GET or POST, not both');
-
- return $_GET[OAUTH2_TOKEN_PARAM_NAME];
- }
-
- if (isset($_POST[OAUTH2_TOKEN_PARAM_NAME]))
- return $_POST[OAUTH2_TOKEN_PARAM_NAME];
-
- return FALSE;
- }
-
- // Access token granting (Section 4).
-
- /**
- * Grant or deny a requested access token.
- *
- * This would be called from the "/token" endpoint as defined in the spec.
- * Obviously, you can call your endpoint whatever you want.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4
- *
- * @ingroup oauth2_section_4
- */
- public function grantAccessToken() {
- $filters = array(
- "grant_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_GRANT_TYPE_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
- "scope" => array("flags" => FILTER_REQUIRE_SCALAR),
- "code" => array("flags" => FILTER_REQUIRE_SCALAR),
- "redirect_uri" => array("filter" => FILTER_SANITIZE_URL),
- "username" => array("flags" => FILTER_REQUIRE_SCALAR),
- "password" => array("flags" => FILTER_REQUIRE_SCALAR),
- "assertion_type" => array("flags" => FILTER_REQUIRE_SCALAR),
- "assertion" => array("flags" => FILTER_REQUIRE_SCALAR),
- "refresh_token" => array("flags" => FILTER_REQUIRE_SCALAR),
- );
-
- $input = filter_input_array(INPUT_POST, $filters);
-
- // Grant Type must be specified.
- if (!$input["grant_type"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing');
-
- // Make sure we've implemented the requested grant type
- if (!in_array($input["grant_type"], $this->getSupportedGrantTypes()))
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE);
-
- // Authorize the client
- $client = $this->getClientCredentials();
-
- if ($this->checkClientCredentials($client[0], $client[1]) === FALSE)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
-
- if (!$this->checkRestrictedGrantType($client[0], $input["grant_type"]))
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNAUTHORIZED_CLIENT);
-
- // Do the granting
- switch ($input["grant_type"]) {
- case OAUTH2_GRANT_TYPE_AUTH_CODE:
- if (!$input["code"] || !$input["redirect_uri"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
-
- $stored = $this->getAuthCode($input["code"]);
-
- // Ensure that the input uri starts with the stored uri
- if ($stored === NULL || (strcasecmp(substr($input["redirect_uri"], 0, strlen($stored["redirect_uri"])), $stored["redirect_uri"]) !== 0) || $client[0] != $stored["client_id"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
-
- if ($stored["expires"] < time())
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN);
-
- break;
- case OAUTH2_GRANT_TYPE_USER_CREDENTIALS:
- if (!$input["username"] || !$input["password"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required');
-
- $stored = $this->checkUserCredentials($client[0], $input["username"], $input["password"]);
-
- if ($stored === FALSE)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
-
- break;
- case OAUTH2_GRANT_TYPE_ASSERTION:
- if (!$input["assertion_type"] || !$input["assertion"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
-
- $stored = $this->checkAssertion($client[0], $input["assertion_type"], $input["assertion"]);
-
- if ($stored === FALSE)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
-
- break;
- case OAUTH2_GRANT_TYPE_REFRESH_TOKEN:
- if (!$input["refresh_token"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found');
-
- $stored = $this->getRefreshToken($input["refresh_token"]);
-
- if ($stored === NULL || $client[0] != $stored["client_id"])
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
-
- if ($stored["expires"] < time())
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN);
-
- // store the refresh token locally so we can delete it when a new refresh token is generated
- $this->setVariable('_old_refresh_token', $stored["token"]);
-
- break;
- case OAUTH2_GRANT_TYPE_NONE:
- $stored = $this->checkNoneAccess($client[0]);
-
- if ($stored === FALSE)
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
- }
-
- // Check scope, if provided
- if ($input["scope"] && (!is_array($stored) || !isset($stored["scope"]) || !$this->checkScope($input["scope"], $stored["scope"])))
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_SCOPE);
-
- if (!$input["scope"])
- $input["scope"] = NULL;
-
- $token = $this->createAccessToken($client[0], $input["scope"]);
-
- $this->sendJsonHeaders();
- echo json_encode($token);
- }
-
- /**
- * Internal function used to get the client credentials from HTTP basic
- * auth or POST data.
- *
- * @return
- * A list containing the client identifier and password, for example
- * @code
- * return array(
- * $_POST["client_id"],
- * $_POST["client_secret"],
- * );
- * @endcode
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2
- *
- * @ingroup oauth2_section_2
- */
- protected function getClientCredentials() {
- if (isset($_SERVER["PHP_AUTH_USER"]) && $_POST && isset($_POST["client_id"]))
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
-
- // Try basic auth
- if (isset($_SERVER["PHP_AUTH_USER"]))
- return array($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);
-
- // Try POST
- if ($_POST && isset($_POST["client_id"])) {
- if (isset($_POST["client_secret"]))
- return array($_POST["client_id"], $_POST["client_secret"]);
-
- return array($_POST["client_id"], NULL);
- }
-
- // No credentials were specified
- $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
- }
-
- // End-user/client Authorization (Section 3 of IETF Draft).
-
- /**
- * Pull the authorization request data out of the HTTP request.
- *
- * @return
- * The authorization parameters so the authorization server can prompt
- * the user for approval if valid.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
- *
- * @ingroup oauth2_section_3
- */
- public function getAuthorizeParams() {
- $filters = array(
- "client_id" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_CLIENT_ID_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
- "response_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_AUTH_RESPONSE_TYPE_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
- "redirect_uri" => array("filter" => FILTER_SANITIZE_URL),
- "state" => array("flags" => FILTER_REQUIRE_SCALAR),
- "scope" => array("flags" => FILTER_REQUIRE_SCALAR),
- );
-
- $input = filter_input_array(INPUT_GET, $filters);
-
- // Make sure a valid client id was supplied
- if (!$input["client_id"]) {
- if ($input["redirect_uri"])
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"]);
-
- $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_CLIENT); // We don't have a good URI to use
- }
-
- // redirect_uri is not required if already established via other channels
- // check an existing redirect URI against the one supplied
- $redirect_uri = $this->getRedirectUri($input["client_id"]);
-
- // At least one of: existing redirect URI or input redirect URI must be specified
- if (!$redirect_uri && !$input["redirect_uri"])
- $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST);
-
- // getRedirectUri() should return FALSE if the given client ID is invalid
- // this probably saves us from making a separate db call, and simplifies the method set
- if ($redirect_uri === FALSE)
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"]);
-
- // If there's an existing uri and one from input, verify that they match
- if ($redirect_uri && $input["redirect_uri"]) {
- // Ensure that the input uri starts with the stored uri
- if (strcasecmp(substr($input["redirect_uri"], 0, strlen($redirect_uri)), $redirect_uri) !== 0)
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_REDIRECT_URI_MISMATCH, NULL, NULL, $input["state"]);
- }
- elseif ($redirect_uri) { // They did not provide a uri from input, so use the stored one
- $input["redirect_uri"] = $redirect_uri;
- }
-
- // type and client_id are required
- if (!$input["response_type"])
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_REQUEST, 'Invalid response type.', NULL, $input["state"]);
-
- // Check requested auth response type against the list of supported types
- if (array_search($input["response_type"], $this->getSupportedAuthResponseTypes()) === FALSE)
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE, NULL, NULL, $input["state"]);
-
- // Restrict clients to certain authorization response types
- if ($this->checkRestrictedAuthResponseType($input["client_id"], $input["response_type"]) === FALSE)
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNAUTHORIZED_CLIENT, NULL, NULL, $input["state"]);
-
- // Validate that the requested scope is supported
- if ($input["scope"] && !$this->checkScope($input["scope"], $this->getSupportedScopes()))
- $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_SCOPE, NULL, NULL, $input["state"]);
-
- return $input;
- }
-
- /**
- * Redirect the user appropriately after approval.
- *
- * After the user has approved or denied the access request the
- * authorization server should call this function to redirect the user
- * appropriately.
- *
- * @param $is_authorized
- * TRUE or FALSE depending on whether the user authorized the access.
- * @param $params
- * An associative array as below:
- * - response_type: The requested response: an access token, an
- * authorization code, or both.
- * - client_id: The client identifier as described in Section 2.
- * - redirect_uri: An absolute URI to which the authorization server
- * will redirect the user-agent to when the end-user authorization
- * step is completed.
- * - scope: (optional) The scope of the access request expressed as a
- * list of space-delimited strings.
- * - state: (optional) An opaque value used by the client to maintain
- * state between the request and callback.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
- *
- * @ingroup oauth2_section_3
- */
- public function finishClientAuthorization($is_authorized, $params = array()) {
- $params += array(
- 'scope' => NULL,
- 'state' => NULL,
- );
- extract($params);
-
- if ($state !== NULL)
- $result["query"]["state"] = $state;
-
- if ($is_authorized === FALSE) {
- $result["query"]["error"] = OAUTH2_ERROR_USER_DENIED;
- }
- else {
- if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
- $result["query"]["code"] = $this->createAuthCode($client_id, $redirect_uri, $scope);
-
- if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
- $result["fragment"] = $this->createAccessToken($client_id, $scope);
- }
-
- $this->doRedirectUriCallback($redirect_uri, $result);
- }
-
- // Other/utility functions.
-
- /**
- * Redirect the user agent.
- *
- * Handle both redirect for success or error response.
- *
- * @param $redirect_uri
- * An absolute URI to which the authorization server will redirect
- * the user-agent to when the end-user authorization step is completed.
- * @param $params
- * Parameters to be pass though buildUri().
- *
- * @ingroup oauth2_section_3
- */
- private function doRedirectUriCallback($redirect_uri, $params) {
- header("HTTP/1.1 ". OAUTH2_HTTP_FOUND);
- header("Location: " . $this->buildUri($redirect_uri, $params));
- exit;
- }
-
- /**
- * Build the absolute URI based on supplied URI and parameters.
- *
- * @param $uri
- * An absolute URI.
- * @param $params
- * Parameters to be append as GET.
- *
- * @return
- * An absolute URI with supplied parameters.
- *
- * @ingroup oauth2_section_3
- */
- private function buildUri($uri, $params) {
- $parse_url = parse_url($uri);
-
- // Add our params to the parsed uri
- foreach ($params as $k => $v) {
- if (isset($parse_url[$k]))
- $parse_url[$k] .= "&" . http_build_query($v);
- else
- $parse_url[$k] = http_build_query($v);
- }
-
- // Put humpty dumpty back together
- return
- ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
- . ((isset($parse_url["user"])) ? $parse_url["user"] . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "")
- . ((isset($parse_url["host"])) ? $parse_url["host"] : "")
- . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
- . ((isset($parse_url["path"])) ? $parse_url["path"] : "")
- . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "")
- . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "");
- }
-
- /**
- * Handle the creation of access token, also issue refresh token if support.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
- *
- * @param $client_id
- * Client identifier related to the access token.
- * @param $scope
- * (optional) Scopes to be stored in space-separated string.
- *
- * @ingroup oauth2_section_4
- */
- protected function createAccessToken($client_id, $scope = NULL) {
- $token = array(
- "access_token" => $this->genAccessToken(),
- "expires_in" => $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME),
- "scope" => $scope
- );
-
- $this->setAccessToken($token["access_token"], $client_id, time() + $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME), $scope);
-
- // Issue a refresh token also, if we support them
- if (in_array(OAUTH2_GRANT_TYPE_REFRESH_TOKEN, $this->getSupportedGrantTypes())) {
- $token["refresh_token"] = $this->genAccessToken();
- $this->setRefreshToken($token["refresh_token"], $client_id, time() + $this->getVariable('refresh_token_lifetime', OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME), $scope);
- // If we've granted a new refresh token, expire the old one
- if ($this->getVariable('_old_refresh_token'))
- $this->unsetRefreshToken($this->getVariable('_old_refresh_token'));
- }
-
- return $token;
- }
-
- /**
- * Handle the creation of auth code.
- *
- * This belongs in a separate factory, but to keep it simple, I'm just
- * keeping it here.
- *
- * @param $client_id
- * Client identifier related to the access token.
- * @param $redirect_uri
- * An absolute URI to which the authorization server will redirect the
- * user-agent to when the end-user authorization step is completed.
- * @param $scope
- * (optional) Scopes to be stored in space-separated string.
- *
- * @ingroup oauth2_section_3
- */
- private function createAuthCode($client_id, $redirect_uri, $scope = NULL) {
- $code = $this->genAuthCode();
- $this->setAuthCode($code, $client_id, $redirect_uri, time() + $this->getVariable('auth_code_lifetime', OAUTH2_DEFAULT_AUTH_CODE_LIFETIME), $scope);
- return $code;
- }
-
- /**
- * Generate unique access token.
- *
- * Implementing classes may want to override these function to implement
- * other access token or auth code generation schemes.
- *
- * @return
- * An unique access token.
- *
- * @ingroup oauth2_section_4
- */
- protected function genAccessToken() {
- return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
- }
-
- /**
- * Generate unique auth code.
- *
- * Implementing classes may want to override these function to implement
- * other access token or auth code generation schemes.
- *
- * @return
- * An unique auth code.
- *
- * @ingroup oauth2_section_3
- */
- protected function genAuthCode() {
- return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
- }
-
- /**
- * Pull out the Authorization HTTP header and return it.
- *
- * Implementing classes may need to override this function for use on
- * non-Apache web servers.
- *
- * @return
- * The Authorization HTTP header, and FALSE if does not exist.
- *
- * @todo Handle Authorization HTTP header for non-Apache web servers.
- *
- * @ingroup oauth2_section_5
- */
- private function getAuthorizationHeader() {
- if (array_key_exists("HTTP_AUTHORIZATION", $_SERVER))
- return $_SERVER["HTTP_AUTHORIZATION"];
-
- if (function_exists("apache_request_headers")) {
- $headers = apache_request_headers();
-
- if (array_key_exists("Authorization", $headers))
- return $headers["Authorization"];
- }
-
- return FALSE;
- }
-
- /**
- * Send out HTTP headers for JSON.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.2
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
- *
- * @ingroup oauth2_section_4
- */
- private function sendJsonHeaders() {
- header("Content-Type: application/json");
- header("Cache-Control: no-store");
- }
-
- /**
- * Redirect the end-user's user agent with error message.
- *
- * @param $redirect_uri
- * An absolute URI to which the authorization server will redirect the
- * user-agent to when the end-user authorization step is completed.
- * @param $error
- * A single error code as described in Section 3.2.1.
- * @param $error_description
- * (optional) A human-readable text providing additional information,
- * used to assist in the understanding and resolution of the error
- * occurred.
- * @param $error_uri
- * (optional) A URI identifying a human-readable web page with
- * information about the error, used to provide the end-user with
- * additional information about the error.
- * @param $state
- * (optional) REQUIRED if the "state" parameter was present in the client
- * authorization request. Set to the exact value received from the client.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2
- *
- * @ingroup oauth2_error
- */
- private function errorDoRedirectUriCallback($redirect_uri, $error, $error_description = NULL, $error_uri = NULL, $state = NULL) {
- $result["query"]["error"] = $error;
-
- if ($state)
- $result["query"]["state"] = $state;
-
- if ($this->getVariable('display_error') && $error_description)
- $result["query"]["error_description"] = $error_description;
-
- if ($this->getVariable('display_error') && $error_uri)
- $result["query"]["error_uri"] = $error_uri;
-
- $this->doRedirectUriCallback($redirect_uri, $result);
- }
-
- /**
- * Send out error message in JSON.
- *
- * @param $http_status_code
- * HTTP status code message as predefined.
- * @param $error
- * A single error code.
- * @param $error_description
- * (optional) A human-readable text providing additional information,
- * used to assist in the understanding and resolution of the error
- * occurred.
- * @param $error_uri
- * (optional) A URI identifying a human-readable web page with
- * information about the error, used to provide the end-user with
- * additional information about the error.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
- *
- * @ingroup oauth2_error
- */
- private function errorJsonResponse($http_status_code, $error, $error_description = NULL, $error_uri = NULL) {
- $result['error'] = $error;
-
- if ($this->getVariable('display_error') && $error_description)
- $result["error_description"] = $error_description;
-
- if ($this->getVariable('display_error') && $error_uri)
- $result["error_uri"] = $error_uri;
-
- header("HTTP/1.1 " . $http_status_code);
- $this->sendJsonHeaders();
- echo json_encode($result);
-
- exit;
- }
-
- /**
- * Send a 401 unauthorized header with the given realm and an error, if
- * provided.
- *
- * @param $http_status_code
- * HTTP status code message as predefined.
- * @param $realm
- * The "realm" attribute is used to provide the protected resources
- * partition as defined by [RFC2617].
- * @param $scope
- * A space-delimited list of scope values indicating the required scope
- * of the access token for accessing the requested resource.
- * @param $error
- * The "error" attribute is used to provide the client with the reason
- * why the access request was declined.
- * @param $error_description
- * (optional) The "error_description" attribute provides a human-readable text
- * containing additional information, used to assist in the understanding
- * and resolution of the error occurred.
- * @param $error_uri
- * (optional) The "error_uri" attribute provides a URI identifying a human-readable
- * web page with information about the error, used to offer the end-user
- * with additional information about the error. If the value is not an
- * absolute URI, it is relative to the URI of the requested protected
- * resource.
- *
- * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2
- *
- * @ingroup oauth2_error
- */
- private function errorWWWAuthenticateResponseHeader($http_status_code, $realm, $error, $error_description = NULL, $error_uri = NULL, $scope = NULL) {
- $realm = $realm === NULL ? $this->getDefaultAuthenticationRealm() : $realm;
-
- $result = "WWW-Authenticate: OAuth realm='" . $realm . "'";
-
- if ($error)
- $result .= ", error='" . $error . "'";
-
- if ($this->getVariable('display_error') && $error_description)
- $result .= ", error_description='" . $error_description . "'";
-
- if ($this->getVariable('display_error') && $error_uri)
- $result .= ", error_uri='" . $error_uri . "'";
-
- if ($scope)
- $result .= ", scope='" . $scope . "'";
-
- header("HTTP/1.1 ". $http_status_code);
- header($result);
-
- exit;
- }
-}
+++ /dev/null
-<?php
-
-/**
- * The default Cache Lifetime (in seconds).
- */
-define("OAUTH2_DEFAULT_EXPIRES_IN", 3600);
-
-/**
- * The default Base domain for the Cookie.
- */
-define("OAUTH2_DEFAULT_BASE_DOMAIN", '');
-
-/**
- * OAuth2.0 draft v10 client-side implementation.
- *
- * @author Originally written by Naitik Shah <naitik@facebook.com>.
- * @author Update to draft v10 by Edison Wong <hswong3i@pantarei-design.com>.
- *
- * @sa <a href="https://github.com/facebook/php-sdk">Facebook PHP SDK</a>.
- */
-abstract class OAuth2Client {
-
- /**
- * Array of persistent variables stored.
- */
- protected $conf = array();
-
- /**
- * Returns a persistent variable.
- *
- * To avoid problems, always use lower case for persistent variable names.
- *
- * @param $name
- * The name of the variable to return.
- * @param $default
- * The default value to use if this variable has never been set.
- *
- * @return
- * The value of the variable.
- */
- public function getVariable($name, $default = NULL) {
- return isset($this->conf[$name]) ? $this->conf[$name] : $default;
- }
-
- /**
- * Sets a persistent variable.
- *
- * To avoid problems, always use lower case for persistent variable names.
- *
- * @param $name
- * The name of the variable to set.
- * @param $value
- * The value to set.
- */
- public function setVariable($name, $value) {
- $this->conf[$name] = $value;
- return $this;
- }
-
- // Stuff that should get overridden by subclasses.
- //
- // I don't want to make these abstract, because then subclasses would have
- // to implement all of them, which is too much work.
- //
- // So they're just stubs. Override the ones you need.
-
- /**
- * Initialize a Drupal OAuth2.0 Application.
- *
- * @param $config
- * An associative array as below:
- * - base_uri: The base URI for the OAuth2.0 endpoints.
- * - code: (optional) The authorization code.
- * - username: (optional) The username.
- * - password: (optional) The password.
- * - client_id: (optional) The application ID.
- * - client_secret: (optional) The application secret.
- * - authorize_uri: (optional) The end-user authorization endpoint URI.
- * - access_token_uri: (optional) The token endpoint URI.
- * - services_uri: (optional) The services endpoint URI.
- * - cookie_support: (optional) TRUE to enable cookie support.
- * - base_domain: (optional) The domain for the cookie.
- * - file_upload_support: (optional) TRUE if file uploads are enabled.
- */
- public function __construct($config = array()) {
- // We must set base_uri first.
- $this->setVariable('base_uri', $config['base_uri']);
- unset($config['base_uri']);
-
- // Use predefined OAuth2.0 params, or get it from $_REQUEST.
- foreach (array('code', 'username', 'password') as $name) {
- if (isset($config[$name]))
- $this->setVariable($name, $config[$name]);
- else if (isset($_REQUEST[$name]) && !empty($_REQUEST[$name]))
- $this->setVariable($name, $_REQUEST[$name]);
- unset($config[$name]);
- }
-
- // Endpoint URIs.
- foreach (array('authorize_uri', 'access_token_uri', 'services_uri') as $name) {
- if (isset($config[$name]))
- if (substr($config[$name], 0, 4) == "http")
- $this->setVariable($name, $config[$name]);
- else
- $this->setVariable($name, $this->getVariable('base_uri') . $config[$name]);
- unset($config[$name]);
- }
-
- // Other else configurations.
- foreach ($config as $name => $value) {
- $this->setVariable($name, $value);
- }
- }
-
- /**
- * Try to get session object from custom method.
- *
- * By default we generate session object based on access_token response, or
- * if it is provided from server with $_REQUEST. For sure, if it is provided
- * by server it should follow our session object format.
- *
- * Session object provided by server can ensure the correct expirse and
- * base_domain setup as predefined in server, also you may get more useful
- * information for custom functionality, too. BTW, this may require for
- * additional remote call overhead.
- *
- * You may wish to override this function with your custom version due to
- * your own server-side implementation.
- *
- * @param $access_token
- * (optional) A valid access token in associative array as below:
- * - access_token: A valid access_token generated by OAuth2.0
- * authorization endpoint.
- * - expires_in: (optional) A valid expires_in generated by OAuth2.0
- * authorization endpoint.
- * - refresh_token: (optional) A valid refresh_token generated by OAuth2.0
- * authorization endpoint.
- * - scope: (optional) A valid scope generated by OAuth2.0
- * authorization endpoint.
- *
- * @return
- * A valid session object in associative array for setup cookie, and
- * NULL if not able to generate it with custom method.
- */
- protected function getSessionObject($access_token = NULL) {
- $session = NULL;
-
- // Try generate local version of session cookie.
- if (!empty($access_token) && isset($access_token['access_token'])) {
- $session['access_token'] = $access_token['access_token'];
- $session['base_domain'] = $this->getVariable('base_domain', OAUTH2_DEFAULT_BASE_DOMAIN);
- $session['expirse'] = isset($access_token['expires_in']) ? time() + $access_token['expires_in'] : time() + $this->getVariable('expires_in', OAUTH2_DEFAULT_EXPIRES_IN);
- $session['refresh_token'] = isset($access_token['refresh_token']) ? $access_token['refresh_token'] : '';
- $session['scope'] = isset($access_token['scope']) ? $access_token['scope'] : '';
- $session['secret'] = md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
-
- // Provide our own signature.
- $sig = self::generateSignature(
- $session,
- $this->getVariable('client_secret')
- );
- $session['sig'] = $sig;
- }
-
- // Try loading session from $_REQUEST.
- if (!$session && isset($_REQUEST['session'])) {
- $session = json_decode(
- get_magic_quotes_gpc()
- ? stripslashes($_REQUEST['session'])
- : $_REQUEST['session'],
- TRUE
- );
- }
-
- return $session;
- }
-
- /**
- * Make an API call.
- *
- * Support both OAuth2.0 or normal GET/POST API call, with relative
- * or absolute URI.
- *
- * If no valid OAuth2.0 access token found in session object, this function
- * will automatically switch as normal remote API call without "oauth_token"
- * parameter.
- *
- * Assume server reply in JSON object and always decode during return. If
- * you hope to issue a raw query, please use makeRequest().
- *
- * @param $path
- * The target path, relative to base_path/service_uri or an absolute URI.
- * @param $method
- * (optional) The HTTP method (default 'GET').
- * @param $params
- * (optional The GET/POST parameters.
- *
- * @return
- * The JSON decoded response object.
- *
- * @throws OAuth2Exception
- */
- public function api($path, $method = 'GET', $params = array()) {
- if (is_array($method) && empty($params)) {
- $params = $method;
- $method = 'GET';
- }
-
- // json_encode all params values that are not strings.
- foreach ($params as $key => $value) {
- if (!is_string($value)) {
- $params[$key] = json_encode($value);
- }
- }
-
- $result = json_decode($this->makeOAuth2Request(
- $this->getUri($path),
- $method,
- $params
- ), TRUE);
-
- // Results are returned, errors are thrown.
- if (is_array($result) && isset($result['error'])) {
- $e = new OAuth2Exception($result);
- switch ($e->getType()) {
- // OAuth 2.0 Draft 10 style.
- case 'invalid_token':
- $this->setSession(NULL);
- default:
- $this->setSession(NULL);
- }
- throw $e;
- }
- return $result;
- }
-
- // End stuff that should get overridden.
-
- /**
- * Default options for cURL.
- */
- public static $CURL_OPTS = array(
- CURLOPT_CONNECTTIMEOUT => 10,
- CURLOPT_RETURNTRANSFER => TRUE,
- CURLOPT_HEADER => TRUE,
- CURLOPT_TIMEOUT => 60,
- CURLOPT_USERAGENT => 'oauth2-draft-v10',
- CURLOPT_HTTPHEADER => array("Accept: application/json"),
- );
-
- /**
- * Set the Session.
- *
- * @param $session
- * (optional) The session object to be set. NULL if hope to frush existing
- * session object.
- * @param $write_cookie
- * (optional) TRUE if a cookie should be written. This value is ignored
- * if cookie support has been disabled.
- *
- * @return
- * The current OAuth2.0 client-side instance.
- */
- public function setSession($session = NULL, $write_cookie = TRUE) {
- $this->setVariable('_session', $this->validateSessionObject($session));
- $this->setVariable('_session_loaded', TRUE);
- if ($write_cookie) {
- $this->setCookieFromSession($this->getVariable('_session'));
- }
- return $this;
- }
-
- /**
- * Get the session object.
- *
- * This will automatically look for a signed session via custom method,
- * OAuth2.0 grant type with authorization_code, OAuth2.0 grant type with
- * password, or cookie that we had already setup.
- *
- * @return
- * The valid session object with OAuth2.0 infomration, and NULL if not
- * able to discover any cases.
- */
- public function getSession() {
- if (!$this->getVariable('_session_loaded')) {
- $session = NULL;
- $write_cookie = TRUE;
-
- // Try obtain login session by custom method.
- $session = $this->getSessionObject(NULL);
- $session = $this->validateSessionObject($session);
-
- // grant_type == authorization_code.
- if (!$session && $this->getVariable('code')) {
- $access_token = $this->getAccessTokenFromAuthorizationCode($this->getVariable('code'));
- $session = $this->getSessionObject($access_token);
- $session = $this->validateSessionObject($session);
- }
-
- // grant_type == password.
- if (!$session && $this->getVariable('username') && $this->getVariable('password')) {
- $access_token = $this->getAccessTokenFromPassword($this->getVariable('username'), $this->getVariable('password'));
- $session = $this->getSessionObject($access_token);
- $session = $this->validateSessionObject($session);
- }
-
- // Try loading session from cookie if necessary.
- if (!$session && $this->getVariable('cookie_support')) {
- $cookie_name = $this->getSessionCookieName();
- if (isset($_COOKIE[$cookie_name])) {
- $session = array();
- parse_str(trim(
- get_magic_quotes_gpc()
- ? stripslashes($_COOKIE[$cookie_name])
- : $_COOKIE[$cookie_name],
- '"'
- ), $session);
- $session = $this->validateSessionObject($session);
- // Write only if we need to delete a invalid session cookie.
- $write_cookie = empty($session);
- }
- }
-
- $this->setSession($session, $write_cookie);
- }
-
- return $this->getVariable('_session');
- }
-
- /**
- * Gets an OAuth2.0 access token from session.
- *
- * This will trigger getSession() and so we MUST initialize with required
- * configuration.
- *
- * @return
- * The valid OAuth2.0 access token, and NULL if not exists in session.
- */
- public function getAccessToken() {
- $session = $this->getSession();
- return isset($session['access_token']) ? $session['access_token'] : NULL;
- }
-
- /**
- * Get access token from OAuth2.0 token endpoint with authorization code.
- *
- * This function will only be activated if both access token URI, client
- * identifier and client secret are setup correctly.
- *
- * @param $code
- * Authorization code issued by authorization server's authorization
- * endpoint.
- *
- * @return
- * A valid OAuth2.0 JSON decoded access token in associative array, and
- * NULL if not enough parameters or JSON decode failed.
- */
- private function getAccessTokenFromAuthorizationCode($code) {
- if ($this->getVariable('access_token_uri') && $this->getVariable('client_id') && $this->getVariable('client_secret')) {
- return json_decode($this->makeRequest(
- $this->getVariable('access_token_uri'),
- 'POST',
- array(
- 'grant_type' => 'authorization_code',
- 'client_id' => $this->getVariable('client_id'),
- 'client_secret' => $this->getVariable('client_secret'),
- 'code' => $code,
- 'redirect_uri' => $this->getCurrentUri(),
- )
- ), TRUE);
- }
- return NULL;
- }
-
- /**
- * Get access token from OAuth2.0 token endpoint with basic user
- * credentials.
- *
- * This function will only be activated if both username and password
- * are setup correctly.
- *
- * @param $username
- * Username to be check with.
- * @param $password
- * Password to be check with.
- *
- * @return
- * A valid OAuth2.0 JSON decoded access token in associative array, and
- * NULL if not enough parameters or JSON decode failed.
- */
- private function getAccessTokenFromPassword($username, $password) {
- if ($this->getVariable('access_token_uri') && $this->getVariable('client_id') && $this->getVariable('client_secret')) {
- return json_decode($this->makeRequest(
- $this->getVariable('access_token_uri'),
- 'POST',
- array(
- 'grant_type' => 'password',
- 'client_id' => $this->getVariable('client_id'),
- 'client_secret' => $this->getVariable('client_secret'),
- 'username' => $username,
- 'password' => $password,
- )
- ), TRUE);
- }
- return NULL;
- }
-
- /**
- * Make an OAuth2.0 Request.
- *
- * Automatically append "oauth_token" in query parameters if not yet
- * exists and able to discover a valid access token from session. Otherwise
- * just ignore setup with "oauth_token" and handle the API call AS-IS, and
- * so may issue a plain API call without OAuth2.0 protection.
- *
- * @param $path
- * The target path, relative to base_path/service_uri or an absolute URI.
- * @param $method
- * (optional) The HTTP method (default 'GET').
- * @param $params
- * (optional The GET/POST parameters.
- *
- * @return
- * The JSON decoded response object.
- *
- * @throws OAuth2Exception
- */
- protected function makeOAuth2Request($path, $method = 'GET', $params = array()) {
- if ((!isset($params['oauth_token']) || empty($params['oauth_token'])) && $oauth_token = $this->getAccessToken()) {
- $params['oauth_token'] = $oauth_token;
- }
- return $this->makeRequest($path, $method, $params);
- }
-
- /**
- * Makes an HTTP request.
- *
- * This method can be overriden by subclasses if developers want to do
- * fancier things or use something other than cURL to make the request.
- *
- * @param $path
- * The target path, relative to base_path/service_uri or an absolute URI.
- * @param $method
- * (optional) The HTTP method (default 'GET').
- * @param $params
- * (optional The GET/POST parameters.
- * @param $ch
- * (optional) An initialized curl handle
- *
- * @return
- * The JSON decoded response object.
- */
- protected function makeRequest($path, $method = 'GET', $params = array(), $ch = NULL) {
- if (!$ch)
- $ch = curl_init();
-
- $opts = self::$CURL_OPTS;
- if ($params) {
- switch ($method) {
- case 'GET':
- $path .= '?' . http_build_query($params, NULL, '&');
- break;
- // Method override as we always do a POST.
- default:
- if ($this->getVariable('file_upload_support')) {
- $opts[CURLOPT_POSTFIELDS] = $params;
- }
- else {
- $opts[CURLOPT_POSTFIELDS] = http_build_query($params, NULL, '&');
- }
- }
- }
- $opts[CURLOPT_URL] = $path;
-
- // Disable the 'Expect: 100-continue' behaviour. This causes CURL to wait
- // for 2 seconds if the server does not support this header.
- if (isset($opts[CURLOPT_HTTPHEADER])) {
- $existing_headers = $opts[CURLOPT_HTTPHEADER];
- $existing_headers[] = 'Expect:';
- $opts[CURLOPT_HTTPHEADER] = $existing_headers;
- }
- else {
- $opts[CURLOPT_HTTPHEADER] = array('Expect:');
- }
-
- curl_setopt_array($ch, $opts);
- $result = curl_exec($ch);
-
- if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT
- error_log('Invalid or no certificate authority found, using bundled information');
- curl_setopt($ch, CURLOPT_CAINFO,
- dirname(__FILE__) . '/fb_ca_chain_bundle.crt');
- $result = curl_exec($ch);
- }
-
- if ($result === FALSE) {
- $e = new OAuth2Exception(array(
- 'code' => curl_errno($ch),
- 'message' => curl_error($ch),
- ));
- curl_close($ch);
- throw $e;
- }
- curl_close($ch);
-
- // Split the HTTP response into header and body.
- list($headers, $body) = explode("\r\n\r\n", $result);
- $headers = explode("\r\n", $headers);
-
- // We catch HTTP/1.1 4xx or HTTP/1.1 5xx error response.
- if (strpos($headers[0], 'HTTP/1.1 4') !== FALSE || strpos($headers[0], 'HTTP/1.1 5') !== FALSE) {
- $result = array(
- 'code' => 0,
- 'message' => '',
- );
-
- if (preg_match('/^HTTP\/1.1 ([0-9]{3,3}) (.*)$/', $headers[0], $matches)) {
- $result['code'] = $matches[1];
- $result['message'] = $matches[2];
- }
-
- // In case retrun with WWW-Authenticate replace the description.
- foreach ($headers as $header) {
- if (preg_match("/^WWW-Authenticate:.*error='(.*)'/", $header, $matches)) {
- $result['error'] = $matches[1];
- }
- }
-
- return json_encode($result);
- }
-
- return $body;
- }
-
- /**
- * The name of the cookie that contains the session object.
- *
- * @return
- * The cookie name.
- */
- private function getSessionCookieName() {
- return 'oauth2_' . $this->getVariable('client_id');
- }
-
- /**
- * Set a JS Cookie based on the _passed in_ session.
- *
- * It does not use the currently stored session - you need to explicitly
- * pass it in.
- *
- * @param $session
- * The session to use for setting the cookie.
- */
- protected function setCookieFromSession($session = NULL) {
- if (!$this->getVariable('cookie_support'))
- return;
-
- $cookie_name = $this->getSessionCookieName();
- $value = 'deleted';
- $expires = time() - 3600;
- $base_domain = $this->getVariable('base_domain', OAUTH2_DEFAULT_BASE_DOMAIN);
- if ($session) {
- $value = '"' . http_build_query($session, NULL, '&') . '"';
- $base_domain = isset($session['base_domain']) ? $session['base_domain'] : $base_domain;
- $expires = isset($session['expires']) ? $session['expires'] : time() + $this->getVariable('expires_in', OAUTH2_DEFAULT_EXPIRES_IN);
- }
-
- // Prepend dot if a domain is found.
- if ($base_domain)
- $base_domain = '.' . $base_domain;
-
- // If an existing cookie is not set, we dont need to delete it.
- if ($value == 'deleted' && empty($_COOKIE[$cookie_name]))
- return;
-
- if (headers_sent())
- error_log('Could not set cookie. Headers already sent.');
- else
- setcookie($cookie_name, $value, $expires, '/', $base_domain);
- }
-
- /**
- * Validates a session_version = 3 style session object.
- *
- * @param $session
- * The session object.
- *
- * @return
- * The session object if it validates, NULL otherwise.
- */
- protected function validateSessionObject($session) {
- // Make sure some essential fields exist.
- if (is_array($session) && isset($session['access_token']) && isset($session['sig'])) {
- // Validate the signature.
- $session_without_sig = $session;
- unset($session_without_sig['sig']);
-
- $expected_sig = self::generateSignature(
- $session_without_sig,
- $this->getVariable('client_secret')
- );
-
- if ($session['sig'] != $expected_sig) {
- error_log('Got invalid session signature in cookie.');
- $session = NULL;
- }
- }
- else {
- $session = NULL;
- }
- return $session;
- }
-
- /**
- * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
- * generate an equivalent using other environment variables.
- */
- function getRequestUri() {
- if (isset($_SERVER['REQUEST_URI'])) {
- $uri = $_SERVER['REQUEST_URI'];
- }
- else {
- if (isset($_SERVER['argv'])) {
- $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
- }
- elseif (isset($_SERVER['QUERY_STRING'])) {
- $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
- }
- else {
- $uri = $_SERVER['SCRIPT_NAME'];
- }
- }
- // Prevent multiple slashes to avoid cross site requests via the Form API.
- $uri = '/' . ltrim($uri, '/');
-
- return $uri;
- }
-
- /**
- * Returns the Current URL.
- *
- * @return
- * The current URL.
- */
- protected function getCurrentUri() {
- $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'
- ? 'https://'
- : 'http://';
- $current_uri = $protocol . $_SERVER['HTTP_HOST'] . $this->getRequestUri();
- $parts = parse_url($current_uri);
-
- $query = '';
- if (!empty($parts['query'])) {
- $params = array();
- parse_str($parts['query'], $params);
- $params = array_filter($params);
- if (!empty($params)) {
- $query = '?' . http_build_query($params, NULL, '&');
- }
- }
-
- // Use port if non default.
- $port = isset($parts['port']) &&
- (($protocol === 'http://' && $parts['port'] !== 80) || ($protocol === 'https://' && $parts['port'] !== 443))
- ? ':' . $parts['port'] : '';
-
- // Rebuild.
- return $protocol . $parts['host'] . $port . $parts['path'] . $query;
- }
-
- /**
- * Build the URL for given path and parameters.
- *
- * @param $path
- * (optional) The path.
- * @param $params
- * (optional) The query parameters in associative array.
- *
- * @return
- * The URL for the given parameters.
- */
- protected function getUri($path = '', $params = array()) {
- $url = $this->getVariable('services_uri') ? $this->getVariable('services_uri') : $this->getVariable('base_uri');
-
- if (!empty($path))
- if (substr($path, 0, 4) == "http")
- $url = $path;
- else
- $url = rtrim($url, '/') . '/' . ltrim($path, '/');
-
- if (!empty($params))
- $url .= '?' . http_build_query($params, NULL, '&');
-
- return $url;
- }
-
- /**
- * Generate a signature for the given params and secret.
- *
- * @param $params
- * The parameters to sign.
- * @param $secret
- * The secret to sign with.
- *
- * @return
- * The generated signature
- */
- protected function generateSignature($params, $secret) {
- // Work with sorted data.
- ksort($params);
-
- // Generate the base string.
- $base_string = '';
- foreach ($params as $key => $value) {
- $base_string .= $key . '=' . $value;
- }
- $base_string .= $secret;
-
- return md5($base_string);
- }
-}