├── guide └── oauthz │ ├── menu.md │ ├── api.md │ ├── index.md │ ├── extension.md │ ├── server.md │ ├── client.md │ └── demo.md ├── license ├── doc ├── oauth-er.png ├── oauth-mysql.sql ├── oauth-oracle.sql ├── oauth-postgresql.sql └── oauth-mssql.sql ├── changelog.txt ├── views ├── oauthz-client.php ├── oauthz-server-signin.php ├── oauthz-server-xrds.php ├── oauthz-server.php ├── oauthz-client-response.php ├── oauthz-server-authorize.php ├── oauthz-server-client.php ├── oauthz-server-error.php ├── oauthz-server-register.php └── oauthz-template.php ├── roadmap.md ├── classes ├── model │ ├── oauthz.php │ └── oauthz │ │ ├── authorize.php │ │ ├── token.php │ │ └── client.php ├── oauthz │ ├── exception │ │ ├── authorize.php │ │ ├── token.php │ │ └── access.php │ ├── token │ │ ├── bearer.php │ │ ├── basic.php │ │ ├── mac.php │ │ └── digest.php │ ├── extension.php │ ├── authentication.php │ ├── core.php │ ├── server.php │ ├── exception.php │ ├── extension │ │ ├── code.php │ │ ├── assertion.php │ │ ├── authorization │ │ │ └── code.php │ │ ├── refresh │ │ │ └── token.php │ │ ├── client │ │ │ └── credentials.php │ │ ├── password.php │ │ └── token.php │ ├── controller.php │ ├── token.php │ ├── api.php │ └── client.php ├── oauthz.php └── controller │ ├── server.php │ ├── authorize.php │ ├── client.php │ ├── api.php │ └── oauth.php ├── config ├── userguide.php ├── oauth-api.php ├── oauth-client.php └── oauth-server.php ├── messages └── oauthz.php ├── README.md └── i18n └── en.php /guide/oauthz/menu.md: -------------------------------------------------------------------------------- 1 | ## [Oauthy]() 2 | - [Demo](demo) 3 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yahasana/kohana-oauthz/HEAD/license -------------------------------------------------------------------------------- /guide/oauthz/api.md: -------------------------------------------------------------------------------- 1 | ## Develop Protected Web Service API ## 2 | 3 | 4 | -------------------------------------------------------------------------------- /doc/oauth-er.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yahasana/kohana-oauthz/HEAD/doc/oauth-er.png -------------------------------------------------------------------------------- /guide/oauthz/index.md: -------------------------------------------------------------------------------- 1 | # Oauthy 2 | 3 | OAuth protocol 2.0 implement for Kohana 3, including server and client -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 2 | Dec 20, 2011 3 | 1) Refactor to Oauth 2.0 rev 22 4 | 2) authorization code with bearer token_type support -------------------------------------------------------------------------------- /guide/oauthz/extension.md: -------------------------------------------------------------------------------- 1 | ## Extension ## 2 | 3 | ### Customize extension ### 4 | 5 | ### Develop new extension ### 6 | -------------------------------------------------------------------------------- /guide/oauthz/server.md: -------------------------------------------------------------------------------- 1 | ## Server ## 2 | 3 | ### Install and configuration ### 4 | 5 | ### Resources management interface ### -------------------------------------------------------------------------------- /guide/oauthz/client.md: -------------------------------------------------------------------------------- 1 | ## Client component ## 2 | 3 | ### Install and configuration ### 4 | 5 | ### Communicate with OAuth 2 server ### 6 | -------------------------------------------------------------------------------- /views/oauthz-client.php: -------------------------------------------------------------------------------- 1 | 2 | Do you want to import you personal information from example.com ?
3 | Yeah, of cause ! Let's go 4 | -------------------------------------------------------------------------------- /roadmap.md: -------------------------------------------------------------------------------- 1 | #Development roadmap 2 | 3 | ## Server mode ## 4 | 5 | 1. Extensions 6 | code, token, authorization_code, implicit, client credentials 7 | 8 | 2. Authentications 9 | bear, mac 10 | 11 | ### Model process ### 12 | 13 | ### Client profiles support ### 14 | 15 | 16 | ## Client mode ## 17 | 18 | 19 | ## Demo app ## 20 | 21 | 22 | ## User guide book ## 23 | -------------------------------------------------------------------------------- /views/oauthz-server-signin.php: -------------------------------------------------------------------------------- 1 | get('user')) 3 | { 4 | $usermail = $user['mail']; 5 | } 6 | else 7 | { 8 | $usermail = ''; 9 | } 10 | ?>
11 | 12 |

Login with a valid email address

13 | 14 | 15 |
-------------------------------------------------------------------------------- /views/oauthz-server-xrds.php: -------------------------------------------------------------------------------- 1 | '; 4 | ?> 6 | 2011-05-10T00:00:00Z 7 | http://oauth 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /classes/model/oauthz.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Kohana_Model 11 | * * 12 | */ 13 | class Model_Oauthz extends Kohana_Model { 14 | 15 | protected $_db = 'default'; 16 | 17 | public static function factory($name, $db = NULL) 18 | { 19 | // Add the model prefix 20 | $class = 'Model_Oauthz_'.$name; 21 | 22 | return new $class($db); 23 | } 24 | 25 | } // END Model_Oauthz 26 | -------------------------------------------------------------------------------- /config/userguide.php: -------------------------------------------------------------------------------- 1 | array( 7 | 8 | // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' 9 | 'oauthy' => array( 10 | 11 | // Whether this modules userguide pages should be shown 12 | 'enabled' => TRUE, 13 | 14 | // The name that should show up on the userguide index page 15 | 'name' => 'oauthy', 16 | 17 | // A short description of this module, shown on the index page 18 | 'description' => 'Oauth server and client for Kohana', 19 | ) 20 | ) 21 | ); -------------------------------------------------------------------------------- /messages/oauthz.php: -------------------------------------------------------------------------------- 1 | ':field must not be empty', 5 | 'matches' => ':field must be the same as :param1', 6 | 'regex' => ':field does not match the required format', 7 | 'exact_length' => ':field must be exactly :param1 characters long', 8 | 'min_length' => ':field must be at least :param1 characters long', 9 | 'max_length' => ':field must be less than :param1 characters long', 10 | 'in_array' => ':field must be one of the available options', 11 | 'digit' => ':field must be a digit', 12 | 'decimal' => ':field must be a decimal with :param1 places', 13 | 'range' => ':field must be within the range of :param1 to :param2', 14 | ); -------------------------------------------------------------------------------- /classes/oauthz/exception/authorize.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | class Oauthz_Exception_Authorize extends Oauthz_Exception { 13 | 14 | public function __construct($message, array $state = NULL, $code = 0) 15 | { 16 | $error_description = I18n::get('Authorization Errors Response'); 17 | 18 | $this->error_description = $error_description[$message]; 19 | 20 | // Pass the message to the parent 21 | parent::__construct($message, $state, $code); 22 | } 23 | 24 | } // END Oauthz_Exception_Authorize 25 | -------------------------------------------------------------------------------- /classes/oauthz/exception/token.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | class Oauthz_Exception_Token extends Oauthz_Exception { 13 | 14 | public function __construct($message, array $state = NULL, $code = 0) 15 | { 16 | $error_description = I18n::get('Token Errors Response'); 17 | 18 | $this->error_description = $error_description[$message]; 19 | 20 | // Pass the message to the parent 21 | parent::__construct($message, $state, $code); 22 | } 23 | 24 | } // END Oauthz_Exception_Token 25 | -------------------------------------------------------------------------------- /classes/oauthz/exception/access.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | class Oauthz_Exception_Access extends Oauthz_Exception { 13 | 14 | public function __construct($message, array $state = NULL, $code = 0) 15 | { 16 | $error_description = I18n::get('Access Errors Response'); 17 | 18 | $this->error_description = $error_description[$message]; 19 | 20 | // Pass the message to the parent 21 | parent::__construct($message, $state, $code); 22 | } 23 | 24 | } // END Oauthz_Exception_Access 25 | -------------------------------------------------------------------------------- /views/oauthz-server.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 |
Applications list you have registered
API KeyAPI SecretRedirect URIScopeSSH KeyOP
DEL
render(); ?>

13 | 14 | Register another request indentifier 15 | -------------------------------------------------------------------------------- /classes/oauthz.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | abstract class Oauthz extends Oauthz_Core { 13 | 14 | public static function get($key = NULL, $default = NULL) 15 | { 16 | if ($key === NULL) 17 | { 18 | $default = Request::$method === 'POST' ? $_POST : $_GET; 19 | } 20 | else 21 | { 22 | $data = Request::$method === 'POST' ? $_POST : $_GET; 23 | 24 | if(isset($data[$key])) $default = $data[$key]; 25 | } 26 | return $default; 27 | } 28 | 29 | public static function is_login() 30 | { 31 | return Session::instance()->get('user') OR Cookie::get('user'); 32 | } 33 | 34 | } // END Oauthz 35 | -------------------------------------------------------------------------------- /classes/controller/server.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Server 11 | * * 12 | */ 13 | class Controller_Server extends Oauthz_Server { 14 | 15 | public function __construct(Request $request) 16 | { 17 | if( ! Session::instance()->get('user')) 18 | { 19 | $request->redirect('oauth/signin'); 20 | } 21 | 22 | parent::__construct($request); 23 | } 24 | 25 | public function action_index() 26 | { 27 | $server = new Model_Oauthz_Client; 28 | 29 | $data = $server->lists(array('user_id' => $_SESSION['user']['uid'])); 30 | 31 | $this->template->content = new View('oauthz-server', $data); 32 | 33 | $this->request->response = $this->template->render(); 34 | } 35 | 36 | } // END Controller Consumer 37 | -------------------------------------------------------------------------------- /views/oauthz-client-response.php: -------------------------------------------------------------------------------- 1 | 4 |

The first request and response

5 | 6 | 7 | 8 |
URIClick! to access it directly
access_token
response [json]
9 | 10 |

The second request and response

11 | 12 | 13 | 14 |
URIClick! to access it directly
access_token
response [json]
15 |
16 |
17 | Look cool, hum
18 | But uham, do not try to refresh this page to refetch these info!
19 | if you do, yup please have a try. -------------------------------------------------------------------------------- /classes/oauthz/token/bearer.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Authentication 11 | * * 12 | */ 13 | class Oauthz_Token_Bearer extends Oauthz_Authentication { 14 | 15 | public function verify($token) 16 | { 17 | if($data = static::parse()) 18 | { 19 | // TODO 20 | } 21 | 22 | return $data; 23 | } 24 | 25 | public static function parse($digest = NULL) 26 | { 27 | if ($digest === NULL AND isset($_SERVER['HTTP_AUTHORIZATION']) 28 | AND strpos(strtolower($_SERVER['HTTP_AUTHORIZATION']), 'bearer') === 0) 29 | { 30 | $params = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 7)), 2); 31 | 32 | // TODO 33 | } 34 | 35 | return empty($data) ? FALSE : $data; 36 | } 37 | 38 | } // END Oauthz_Token_Bearer 39 | -------------------------------------------------------------------------------- /classes/controller/authorize.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Controller 11 | * * 12 | */ 13 | class Controller_Authorize extends Oauthz_Controller { 14 | 15 | public function action_index() 16 | { 17 | //~ are you sure to access your infomation data from webservice sp.example.com 18 | //~ Yes - send request token to sp.example.com 19 | //~ $uri = 'http://localhost/oauth/request'; 20 | //$token = parent::request_token(); 21 | 22 | //~ $uri = 'http://localhost/oauth/authorize'; 23 | parent::action_authorize(); 24 | } 25 | 26 | public function action_okay() 27 | { 28 | echo 'ha ha hahahha!'; 29 | } 30 | 31 | public function action_test() 32 | { 33 | extract(array('hel'=>'helo'),EXTR_PREFIX_ALL,'this->'); 34 | $this->request->response = $this->_hel; 35 | } 36 | 37 | } // END Controller Consumer 38 | -------------------------------------------------------------------------------- /classes/oauthz/extension.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | abstract class Oauthz_Extension { 13 | 14 | /** 15 | * OPTION if the "state" parameter was present in the client authorization request. 16 | * 17 | * @access protected 18 | * @var string $state 19 | */ 20 | protected $state; 21 | 22 | /** 23 | * Create grant_type or token_type object 24 | * 25 | * @access public 26 | * @param string $type 27 | * @param array $args 28 | * @return mix 29 | */ 30 | public static function factory($type, array $args) 31 | { 32 | $type = 'Oauthz_Extension_'.$type; 33 | 34 | if(class_exists($type)) 35 | { 36 | return new $type($args); 37 | } 38 | 39 | return FALSE; 40 | } 41 | 42 | /** 43 | * Obtain token 44 | * 45 | * @access public 46 | * @return mix 47 | */ 48 | abstract public function execute(); 49 | 50 | } // END Oauthz_Extension 51 | -------------------------------------------------------------------------------- /views/oauthz-server-authorize.php: -------------------------------------------------------------------------------- 1 |

OAuth Test Client

2 |

Note: we don't store any of the information you type in.

3 | do you want to let the to access your information? 7 |

Approve access 8 | Deny access
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
5 | 6 | 10 |
Service list you have register
client_idredirect_uriconfirm_typeclient_descOP
DEL
16 | 17 |
18 |
19 | 20 | 21 |
6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Authentication 11 | * * 12 | */ 13 | class Oauthz_Token_Basic extends Oauthz_Authentication { 14 | 15 | public function verify($token) 16 | { 17 | if($data = static::parse()) 18 | { 19 | $data = $data['client_id'] === $token['client_id'] AND $data['client_secret'] === $token['client_secret']; 20 | } 21 | 22 | return $data; 23 | } 24 | 25 | public static function parse($digest = NULL) 26 | { 27 | // mod_php 28 | if(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])) 29 | { 30 | $data = array('client_id' => $_SERVER['PHP_AUTH_USER'], 'client_secret' => $_SERVER['PHP_AUTH_PW']); 31 | } 32 | // most other servers 33 | elseif (isset($_SERVER['HTTP_AUTHORIZATION']) 34 | AND strpos(strtolower($_SERVER['HTTP_AUTHORIZATION']), 'basic') === 0) 35 | { 36 | $params = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)), 2); 37 | 38 | $data = array('client_id' => $params[0], 'client_secret' => $params[1]); 39 | } 40 | 41 | return empty($data) ? FALSE : $data; 42 | } 43 | 44 | } // END Oauthz_Token_Basic 45 | -------------------------------------------------------------------------------- /classes/controller/client.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Client_Controller 11 | * * 12 | */ 13 | class Controller_Client extends Oauthz_Client { 14 | 15 | public function action_index() 16 | { 17 | $template = new View('oauthz-template'); 18 | 19 | $template->content = new View('oauthz-client'); 20 | 21 | $this->request->response = $template->render(); 22 | } 23 | 24 | public function action_test() 25 | { 26 | try 27 | { 28 | $resource = Remote::get('http://docs/api/get/1',array( 29 | CURLOPT_POST => TRUE, 30 | CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded;charset=UTF-8'), 31 | CURLOPT_POSTFIELDS => http_build_query(array( 32 | 'client_id' => 'OA_4bfbc43769917', 33 | //'oauth_token' => $token->access_token, 34 | //'refresh_token' => $token->refresh_token, 35 | //'expires_in' => $token->expires_in 36 | ), '', '&') 37 | )); 38 | } 39 | catch (Exception $e) 40 | { 41 | $resource = $e->getMessage(); 42 | } 43 | echo '
'.print_r($resource,TRUE).'
'; 44 | } 45 | 46 | } // END Controller Consumer 47 | -------------------------------------------------------------------------------- /config/oauth-api.php: -------------------------------------------------------------------------------- 1 | array( 10 | 11 | 'formats' => array( 12 | 'json' => FALSE, // 'application/json' 13 | 'xml' => FALSE, // 'application/xml' 14 | 'form' => FALSE, // 'text/plain' 15 | 'html' => FALSE, // 'text/html' 16 | 'csv' => FALSE, // 'application/csv' 17 | 'php' => FALSE, // 'text/plain' 18 | 'serialize' => FALSE // 'application/vnd.php.serialized' 19 | ), 20 | 21 | 'methods' => array( 22 | 'HEAD' => TRUE, 23 | 'GET' => TRUE, 24 | 'POST' => TRUE, 25 | 'PUT' => TRUE, 26 | 'DELETE' => TRUE 27 | ), 28 | 29 | /** 30 | * Parameters should be required when access protected resource 31 | * cryptographic token or bear token 32 | */ 33 | 'bearer' => array( 34 | 'access_token' => TRUE, 35 | 'scope' => FALSE 36 | ), 37 | 38 | 'mac' => array( 39 | 'access_token' => TRUE, 40 | 'mac_key' => TRUE, 41 | 'mac_algorithm' => TRUE, 42 | 'scope' => FALSE 43 | ), 44 | 45 | 'max_requests' => array( 46 | 500, // common client 47 | 1000, // first class client 48 | 1500 // vip client 49 | ) 50 | 51 | ) 52 | 53 | ); // END OAuth API config 54 | -------------------------------------------------------------------------------- /classes/controller/api.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Api 11 | * * 12 | */ 13 | class Controller_Api extends Oauthz_Api { 14 | 15 | protected $_exclude = array('xrds', 'index'); 16 | 17 | /** 18 | * Accessing Protected Resources 19 | * 20 | * @access public 21 | * @return void 22 | */ 23 | public function action_index() 24 | { 25 | $methods = array(); 26 | foreach(get_class_methods($this) as $method) 27 | { 28 | if(substr($method, 0, 7) === 'action_') 29 | { 30 | $method = ltrim($method, 'action_'); 31 | $methods[$method] = url::site('/api/'.$method, TRUE); 32 | } 33 | } 34 | $this->request->response = str_replace('\\/', '/', json_encode($methods)); 35 | } 36 | 37 | public function action_get($id = NULL) 38 | { 39 | $data = array( 40 | array('Humm', 'Hah'), 41 | array('Zzz', 'Yaaa'), 42 | array('Giii', 'Neee') 43 | ); 44 | $this->request->response = json_encode(isset($data[$id]) ? $data[$id] : array('Hello OAuth 2.0, when you see this info, it means you successfully access protected resources',)); 45 | } 46 | 47 | public function action_create() 48 | { 49 | // 50 | } 51 | 52 | public function action_update() 53 | { 54 | // 55 | } 56 | 57 | public function action_delete() 58 | { 59 | // 60 | } 61 | 62 | } // END Controller_Api 63 | -------------------------------------------------------------------------------- /guide/oauthz/demo.md: -------------------------------------------------------------------------------- 1 | # Understand the demo step by step 2 | 3 | Note: before runing these steps, you have to setup the OAuth server. see README 4 | 5 | ### Getting the client ID from OAuth server ### 6 | 7 | + Access `http://example.com/oauth/index` 8 | + Login by an valid email address. e.g. `demo@example.com` 9 | + Register a client ID in the server page. e.g. `client_id: OAL_4D2ACB5280EF8, client_secrect: demo` 10 | 11 | ### Setting client connection settings ### 12 | 13 | 1. Request URIs options, 14 | 15 | 'oauth-uri' => 'http://example.com/oauth/code', // the uri for requesting authorization_code 16 | 'token-uri' => 'http://example.com/oauth/token', // the uri for requesting access_token 17 | 'api-uri' => 'http://example.com/api/', // the uri for requesting protected resource 18 | 'redirect_uri' => 'http://my-web-server.com/client/do' // the uri holded by yourself 19 | 20 | 2. Config the OAuth client ID and secrect `config/oauth-client.php` with the `client_id` and `client_secrect` above. 21 | 3. Note: Other options is for future, they are still in clound and waiting for implement. 22 | 23 | ### Go on, thinking you are the smart user and try to experiement the stupid service ### 24 | 25 | + Access `http://my-web-server.com/client/index` 26 | + the script in your web site now is connecting to `http://example.com/oauth/code` to get an `authorization_code`. 27 | this works in background and can NOT feel,even you are smart 28 | + What you see is `http://example.com/oauth/code?response_type=code&client_id=OA_4bfbc43769917&redirect_uri=http%3A%2F%2Fmy-web-server.com%2Fclient%2Fdo` 29 | + To approve or not to? approve will grant my-web-server.com steal some protected information, which owned by you in example.com. 30 | No? okay that is it. 31 | 32 | ### How these work? ### 33 | Hmm, I have no more time to complete this answer at this moment. help me please. 34 | -------------------------------------------------------------------------------- /classes/oauthz/authentication.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | abstract class Oauthz_Authentication { 13 | 14 | protected $_client_id; 15 | 16 | protected $_token; 17 | 18 | public static function factory($token_type, array $config, array $params) 19 | { 20 | $token_type = 'Oauthz_Token_'.$token_type; 21 | 22 | $oauth = new $token_type; 23 | 24 | foreach($config as $key => $val) 25 | { 26 | if($val === TRUE) 27 | { 28 | if(isset($params[$key]) AND $value = rawurldecode($params[$key])) 29 | { 30 | $oauth->$key = $value; 31 | } 32 | else 33 | { 34 | throw new Oauthz_Exception_Access('invalid_grant', self::state($params)); 35 | } 36 | } 37 | elseif($val !== FALSE) 38 | { 39 | $oauth->$key = $val; 40 | } 41 | } 42 | 43 | return $oauth; 44 | } 45 | 46 | public function client_id() 47 | { 48 | return $this->_client_id; 49 | } 50 | 51 | public function token() 52 | { 53 | return $this->access_token; 54 | } 55 | 56 | public static function state($params) 57 | { 58 | // Parse the "state" paramter 59 | if(isset($params['state']) AND ($state = rawurldecode($params['state']))) 60 | { 61 | $param = array('state' => $state); 62 | } 63 | else 64 | { 65 | $param = NULL; 66 | } 67 | 68 | return $param; 69 | } 70 | 71 | abstract public function verify($token); 72 | 73 | } // END Oauthz_Authentication 74 | -------------------------------------------------------------------------------- /classes/oauthz/core.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | abstract class Oauthz_Core { 13 | 14 | const VERSION = '0.22-eva'; 15 | 16 | /** 17 | * OAuth server configuration group name 18 | * 19 | * @access protected 20 | * @var string $_type 21 | */ 22 | public static $type = 'default'; 23 | 24 | public static function config($key = NULL, $type = NULL) 25 | { 26 | static $config; 27 | 28 | isset($type) OR $type = Oauthz::$type; 29 | 30 | if( ! isset($config[$type])) 31 | { 32 | if( ! $config[$type] = Kohana::config('oauth-server')->get($type)) 33 | { 34 | throw new Kohana_Exception('There is no ":group" in your config file oauth-server.php' 35 | , array(':group' => $type)); 36 | } 37 | } 38 | 39 | return isset($key) ? isset($config[$type][$key]) ? $config[$type][$key] : NULL : $config[$type]; 40 | } 41 | 42 | /** 43 | * Oauthz_Signature::factory alias 44 | * 45 | * @access public 46 | * @param string $method 47 | * @param string $identifier 48 | * @return object 49 | * @see Oauthz_Signature::factory 50 | */ 51 | public static function signature($method, $identifier) 52 | { 53 | return Oauthz_Signature::factory($method, $identifier); 54 | } 55 | 56 | public static function grant_access_uri($redirect) 57 | { 58 | return $redirect.URL::query(); 59 | } 60 | 61 | public static function access_denied_uri($redirect = NULL) 62 | { 63 | if( ! $redirect) $redirect = Arr::get($_GET, 'redirect_uri'); 64 | if( $state = Arr::get($_GET, 'state')) $state = '&state='.$state; 65 | return $redirect.'?error=access_denied'.$state; 66 | } 67 | 68 | } // END Oauthz Core 69 | -------------------------------------------------------------------------------- /classes/oauthz/server.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Kohana_Controller 11 | * * 12 | */ 13 | class Oauthz_Server extends Kohana_Controller { 14 | 15 | protected $template = 'oauthz-template'; 16 | 17 | public function before() 18 | { 19 | $this->template = new View($this->template); 20 | } 21 | 22 | public function action_register($client_id = NULL) 23 | { 24 | $data = array(); 25 | $client = new Model_Oauthz_Client; 26 | if($client_id) 27 | { 28 | $data = (array) $client->get($client_id, $_SESSION['user']['uid']); 29 | } 30 | elseif(isset($_POST['__v_state__'])) 31 | { 32 | $_POST['user_id'] = $_SESSION['user']['uid']; 33 | 34 | $valid = empty($_POST['server_id']) ? $client->append($_POST) : $client->update($_POST['server_id'], $_POST); 35 | 36 | if($valid instanceOf Validate) 37 | { 38 | $data = $valid->as_array(); 39 | $data['errors'] = $valid->errors('validate'); 40 | } 41 | else 42 | { 43 | $data += $valid; 44 | } 45 | } 46 | 47 | $this->template->content = new View('oauthz-server-register', $data); 48 | 49 | $this->request->response = $this->template->render(); 50 | } 51 | 52 | public function action_client() 53 | { 54 | $client = new Model_Oauthz_Client; 55 | 56 | $data = $client->lists(array('user_id' => $_SESSION['user']['uid'])); 57 | 58 | $this->template->content = new View('oauthz-server-client', $data); 59 | 60 | $this->request->response = $this->template->render(); 61 | } 62 | 63 | public function action_access_deny() 64 | { 65 | $this->request->status = 302; #HTTP/1.1 302 Found 66 | $this->request->headers['Content-Type'] = 'application/x-www-form-urlencoded'; 67 | $this->request->headers['Location'] = 'http://example.com/rd#error=user_denied'; 68 | } 69 | 70 | } // END Oauthz_Server 71 | -------------------------------------------------------------------------------- /config/oauth-client.php: -------------------------------------------------------------------------------- 1 | array( 8 | // uri to obtain authorization code 9 | 'oauth-uri' => 'http://docs/oauth/code', 10 | 11 | // uri to obtain the access token 12 | 'token-uri' => 'http://docs/oauth/token', 13 | 14 | // Restful web service api base url 15 | 'api-uri' => 'http://docs/api/', 16 | 17 | // Must be code, token, password, client_credentials 18 | 'protocol-flow' => 'code', 19 | 20 | // Parameters for Authorization Code flow 21 | 'code' => array( 22 | 'client_id' => 'OAL@4EED687F28A72', 23 | 'client_secret' => '000000', 24 | 'token_type' => 'bearer', // bearer, hmac-sha1, rsa-sha1, md5 25 | 'scope' => '', 26 | 'state' => 'test', 27 | 'redirect_uri' => 'http://docs/client/do' 28 | ), 29 | // Parameters for Implicit Grant Flow 30 | 'token' => array( 31 | 'client_id' => 'OAL_4D2EA62621E4F', 32 | 'client_secret' => 'sss', 33 | 'token_type' => 'BEARER', // BEARER, HMAC-SHA1, RSA-SHA1, MD5 34 | 'scope' => '', 35 | 'state' => '', 36 | 'redirect_uri' => 'http://docs/client/do' 37 | ), 38 | // Parameters for Resource Owner Password Credentials Flow 39 | 'password' => array( 40 | 'username' => 'OAL_4D2EA62621E4F', 41 | 'password' => 'sss', 42 | 'client_id' => 'OAL_4D2EA62621E4F', 43 | 'client_secret' => 'sss', 44 | 'token_type' => 'BEARER', // BEARER, HMAC-SHA1, RSA-SHA1, MD5 45 | 'scope' => '', 46 | 'state' => '', 47 | ), 48 | // Parameters for Client Credentials Flow 49 | 'client_credentials' => array( 50 | 'client_id' => 'OAL_4D2EA62621E4F', 51 | 'client_secret' => 'sss', 52 | 'token_type' => 'BEARER', // BEARER, HMAC-SHA1, RSA-SHA1, MD5 53 | 'scope' => '', 54 | 'state' => '', 55 | ) 56 | ), 57 | 58 | ); // END OAuth client config 59 | -------------------------------------------------------------------------------- /views/oauthz-server-error.php: -------------------------------------------------------------------------------- 1 | param('id')) 3 | $uri = Request::$current->controller.'/'.Request::$current->action.'/'; 4 | else 5 | $uri = Request::$current->uri.'/'; 6 | ?> 7 | 8 | '; 12 | $count = count($code_errors); 13 | foreach($code_errors as $error_code => $error_info) 14 | { 15 | ?>'.__('Authorization Response').''; 18 | $count = FALSE; 19 | } 20 | ?>'; 25 | } 26 | 27 | if(isset($token_errors)) 28 | { 29 | echo ''; 30 | $count = count($token_errors); 31 | foreach($token_errors as $error_code => $error_info) 32 | { 33 | ?>'.__('Access Token Response').''; 36 | $count = FALSE; 37 | } 38 | ?>'; 43 | } 44 | 45 | if(isset($access_errors)) 46 | { 47 | echo ''; 48 | $count = count($access_errors); 49 | foreach($access_errors as $error_code => $error_info) 50 | { 51 | ?>'.__('Access Protected Resource').''; 54 | $count = FALSE; 55 | } 56 | ?>'; 61 | } 62 | ?>

Service error codes description

Request StageError CodeDescriptionURI
/
/
/
-------------------------------------------------------------------------------- /classes/oauthz/token/mac.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Authentication 11 | * * 12 | */ 13 | class Oauthz_Token_Mac extends Oauthz_Authentication { 14 | 15 | public function verfiy($token) 16 | { 17 | // Pull the public key ID from the certificate 18 | $public_key = openssl_get_publickey($token->public_cert); 19 | 20 | // Check the computed signature against the one passed in the query 21 | $data = openssl_verify($this->identifier, base64_decode($this->signature), $public_key); 22 | 23 | // Release the key resource 24 | openssl_free_key($public_key); 25 | 26 | return $data === 1; 27 | } 28 | 29 | // function to parse the http auth header 30 | public static function parse($digest = NULL) 31 | { 32 | $info = FALSE; 33 | 34 | return $info; 35 | } 36 | 37 | public function rsa_sha1($token) 38 | { 39 | // Pull the private key ID from the certificate 40 | $private_key = openssl_get_privatekey($token->private_cert); 41 | 42 | // Sign using the key 43 | if(openssl_sign($this->identifier, $this->signature, $private_key) !== TRUE) 44 | { 45 | throw new Oauthy_Exception(''); 46 | } 47 | 48 | // Release the key resource 49 | openssl_free_key($private_key); 50 | 51 | return base64_encode($this->signature); 52 | } 53 | 54 | public function hmac_sha1($token) 55 | { 56 | $key = rawurlencode($token->client_secret); 57 | 58 | if( ! empty($token->token_secret)) 59 | { 60 | $key .= '&'.rawurlencode($token->token_secret); 61 | } 62 | 63 | return $this->signature === base64_encode(hash_hmac('sha1', $this->identifier, $key, TRUE)); 64 | } 65 | 66 | /** 67 | * Normalized request string for signature verify 68 | * 69 | * @access public 70 | * @param string $method 71 | * @param string $uri 72 | * @param array $params 73 | * @return string 74 | */ 75 | public static function identifier($method, $uri, array $params) 76 | { 77 | // The signature parameter MUST be excluded. 78 | unset($params['signature']); 79 | 80 | return $method.'&'.rawurlencode($uri).'&'.http_build_query($params, '', '&'); 81 | } 82 | 83 | } // END Oauthz_Token_Mac 84 | -------------------------------------------------------------------------------- /classes/oauthz/token/digest.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2011 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Authentication 11 | * * 12 | */ 13 | class Oauthz_Token_Digest extends Oauthz_Authentication { 14 | 15 | public function verfiy($token) 16 | { 17 | if($data = static::parse() AND $data['username'] === $token['client_id']) 18 | { 19 | // generate the valid response 20 | $realm = md5("$token['client_id']:$data['realm']:$token['client_secret']"); 21 | $method = md5("$_SERVER['REQUEST_METHOD']:$data['uri']"); 22 | 23 | $data = $data['response'] === md5("$realm:$data['nonce']:$data['nc']:$data['cnonce']:$data['qop']:$method"); 24 | } 25 | 26 | return $data; 27 | } 28 | 29 | // function to parse the http auth header 30 | public static function parse($digest = NULL) 31 | { 32 | if($digest === NULL) 33 | { 34 | // mod_php 35 | if (isset($_SERVER['PHP_AUTH_DIGEST'])) 36 | { 37 | $digest = $_SERVER['PHP_AUTH_DIGEST']; 38 | } 39 | // most other servers 40 | elseif (isset($_SERVER['HTTP_AUTHORIZATION']) 41 | AND strpos(strtolower($_SERVER['HTTP_AUTHORIZATION']), 'digest') === 0) 42 | { 43 | $digest = substr($_SERVER['HTTP_AUTHORIZATION'], 7); 44 | } 45 | } 46 | 47 | $info = FALSE; 48 | 49 | if($digest) 50 | { 51 | // protect against missing data 52 | $fields = array( 53 | 'nonce' => 1, 54 | 'nc' => 1, 55 | 'cnonce' => 1, 56 | 'qop' => 1, 57 | 'username' => 1, 58 | 'uri' => 1, 59 | 'response' => 1, 60 | 'realm' => 1 61 | ); 62 | 63 | preg_match_all('@('.implode('|', array_keys($fields)).')=[\'"]?([^\'",]+)@', $digest, $matches, PREG_SET_ORDER); 64 | 65 | $data = array(); 66 | 67 | foreach($matches as $match) 68 | { 69 | $data[$match[1]] = $match[2] ? $match[2] : ($match[3] ?: $match[4]); 70 | unset($fields[$match[1]]); 71 | } 72 | 73 | empty($fields) AND $info = $data; 74 | } 75 | 76 | return $info; 77 | } 78 | 79 | } // END Oauthz_Token_Digest 80 | -------------------------------------------------------------------------------- /views/oauthz-server-register.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | Client ID *:'.$client_id.'
'; ?> 7 | 8 |
10 | 11 |
13 | 14 | ☺☺
16 | 17 | 18 |
19 | 20 |
25 | 26 |
27 | 28 |
29 | 30 | 31 |
32 | 33 | 39 |
40 | 41 |
43 | 44 |
45 | 46 |
47 | 48 | Note:

☺, This should be generated from system and updated regularly in some days 49 |
☺☺, MAY include an application/x-www-form-urlencoded formatted query component, but fragment component is not allowed

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Kohana OAuth 2 Module ![project status](http://stillmaintained.com/Yahasana/kohana-oauthy.png)# 2 | 3 | #OAuth protocal v2 [draft 22](http://tools.ietf.org/wg/oauth/) 4 | 5 | ### Requirement ### 6 | 1) php v5.2+, curl extension to run the demo 7 | 8 | 2) MySQL, SQLite, MSSQL or NOSQL database 9 | 10 | 3) Kohana [v3.0.x](http://dev.kohanaframework.org/attachments/download/1649/kohana-3.0.10.zip), I have no plan to support v3.1+ ATM 11 | 12 | 4) Apache, Nignx, a web server 13 | 14 | ## Server ## 15 | 16 | ### Install and configuration ### 17 | 18 | 1) Create the oauth data tabe by execute `oauth.sql` in doc directory 19 | 20 | 2) Configurate the server parameters in `config/oauthz-server.php` 21 | 22 | ### Modify interface show to users ### 23 | 24 | 1) Client have to register an account before they access the protected resources. they can also manage their account 25 | 26 | 2) Resource owners personal information management. 27 | 28 | ``` php 29 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Controller 11 | * * 12 | */ 13 | class Controller_Oauth extends Oauthz_Controller { 14 | 15 | public function action_index() 16 | { 17 | $template = new View('oauthz-template'); 18 | $template->content = '

Hello guest.

'; 19 | $this->request->response = $template; 20 | } 21 | 22 | public function action_code() 23 | { 24 | $query = URL::query(); 25 | $template = new View('oauthz-template'); 26 | $view = new View('oauthz-server-authorize', array('authorized' => TRUE, 'query' => $query)); 27 | $template->content = $view->render(); 28 | $this->request->response = $template; 29 | } 30 | 31 | public function action_error($error_code = NULL) 32 | { 33 | $error = array(); 34 | 35 | $errors['code_errors'] = I18n::get('Authorization Errors Response'); 36 | $errors['token_errors'] = I18n::get('Token Errors Response'); 37 | $errors['access_errors'] = I18n::get('Access Errors Response'); 38 | 39 | if(isset($errors['code_errors'][$error_code])) 40 | { 41 | $error['code_errors'][$error_code] = $errors['code_errors'][$error_code]; 42 | } 43 | 44 | if(isset($errors['token_errors'][$error_code])) 45 | { 46 | $error['token_errors'][$error_code] = $errors['code_errors'][$error_code]; 47 | } 48 | 49 | if(isset($errors['access_errors'][$error_code])) 50 | { 51 | $error['access_errors'][$error_code] = $errors['code_errors'][$error_code]; 52 | } 53 | 54 | if($error) 55 | { 56 | $error['error_code'] = $error_code; 57 | $errors = $error; 58 | } 59 | 60 | $template = new View('oauthz-template'); 61 | $view = new View('oauthz-server-error', $errors); 62 | $template->content = $view->render(); 63 | $this->request->response = $template; 64 | } 65 | 66 | public function action_signin() 67 | { 68 | if( ! empty($_POST['usermail']) AND Validate::email($_POST['usermail'])) 69 | { 70 | $user = array( 71 | 'uid' => $_SERVER['REQUEST_TIME'], 72 | 'mail' => $_POST['usermail'] 73 | ); 74 | Cookie::set('user', json_encode($user)); 75 | Session::instance()->set('user', $user); 76 | $this->request->redirect(arr::get($_GET, 'redirect', 'server/index')); 77 | } 78 | elseif($user = Oauthz::is_login()) 79 | { 80 | Session::instance()->set('user', json_decode($user, TRUE)); 81 | $this->request->redirect(arr::get($_GET, 'redirect', 'server/index')); 82 | } 83 | 84 | $template = new View('oauthz-template'); 85 | $view = new View('oauthz-server-signin'); 86 | $template->content = $view->render(); 87 | $this->request->response = $template; 88 | } 89 | 90 | public function action_logout() 91 | { 92 | Cookie::delete('user'); 93 | Session::instance()->delete('user'); 94 | $this->request->redirect('oauth/index'); 95 | } 96 | 97 | public function action_okay() 98 | { 99 | echo $this->request->referrer; 100 | } 101 | 102 | } // END Controller Consumer 103 | -------------------------------------------------------------------------------- /i18n/en.php: -------------------------------------------------------------------------------- 1 | 'Authorization Response', 5 | 'Access Token Response' => 'Access Token Response', 6 | 'Access Protected Resource' => 'Access Protected Resource', 7 | 8 | // Error Response 9 | 'Authorization Errors Response' => array( 10 | 'invalid_request' => 'The request is missing a required parameter, includes an 11 | unsupported parameter or parameter value, or is otherwise malformed.', 12 | 13 | 'unauthorized_client' => 'The client is not authorized to request an authorization 14 | code using this method.', 15 | 16 | 'access_denied' => 'The resource owner or authorization server denied the request.', 17 | 18 | 'unsupported_response_type' => 'The authorization server does not support obtaining an 19 | authorization code using this method.', 20 | 21 | 'invalid_scope' => 'The requested scope is invalid, unknown, or malformed.', 22 | 23 | 'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.', 24 | 25 | 'temporarily_unavailable'=> 'The authorization server is currently unable to handle the 26 | request due to a temporary overloading or maintenance of the server.' 27 | ), 28 | // Error Response 29 | 'Token Errors Response' => array( 30 | 'invalid_request' => 'The request is missing a required parameter, includes an 31 | unsupported parameter or parameter value, or is otherwise malformed.', 32 | 33 | 'unauthorized_client' => 'The client is not authorized to request an access token using this method.', 34 | 35 | 'access_denied' => 'The resource owner or authorization server denied the request.', 36 | 37 | 'unsupported_response_type' => 'The authorization server does not support obtaining an access token using this method.', 38 | 39 | 'invalid_scope' => 'The requested scope is invalid, unknown, or malformed.', 40 | 41 | 'server_error' => 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.', 42 | 43 | 'temporarily_unavailable'=> 'The authorization server is currently unable to handle the 44 | request due to a temporary overloading or maintenance of the server.' 45 | ), 46 | // Error Response 47 | 'Access Errors Response' => array( 48 | 'invalid_request' => 'The request is missing a required parameter, includes an 49 | unsupported parameter or parameter value, repeats a 50 | parameter, includes multiple credentials, utilizes more 51 | than one mechanism for authenticating the client, or is 52 | otherwise malformed.', 53 | 54 | 'invalid_client' => 'Client authentication failed (e.g. unknown client, no 55 | client credentials included, multiple client credentials 56 | included, or unsupported credentials type).', 57 | 58 | 'invalid_grant' => 'The provided authorization grant is invalid, expired, 59 | revoked, or does not match the redirection URI used in 60 | the authorization request.', 61 | 62 | 'unauthorized_client' => 'The authenticated client is not authorized to use this 63 | authorization grant type.', 64 | 65 | 'unsupported_grant_type'=> 'The authorization grant type is not supported by the 66 | authorization server.', 67 | 68 | 'invalid_scope' => 'The requested scope is invalid, unknown, malformed, or 69 | exceeds the previously granted scope.' 70 | ) 71 | 72 | ); 73 | -------------------------------------------------------------------------------- /classes/oauthz/exception.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | class Oauthz_Exception extends Exception { 13 | 14 | /** 15 | * REQUIRED. A single error code 16 | * 17 | * @access public 18 | * @var string $error 19 | */ 20 | public $error; 21 | 22 | /** 23 | * OPTIONAL. A human-readable text providing additional information, 24 | * used to assist in the understanding and resolution of the error occurred. 25 | * 26 | * @access public 27 | * @var string $error_description 28 | */ 29 | public $error_description; 30 | 31 | /** 32 | * OPTIONAL. A URI identifying a human-readable web page with 33 | * information about the error, used to provide the resource owner 34 | * with additional information about the error. 35 | * 36 | * @access public 37 | * @var string $error_uri 38 | */ 39 | protected $error_uri; 40 | 41 | /** 42 | * Initial OAuth error codes from config settings 43 | * 44 | * @access public 45 | * @param string $message 46 | * @param string $state extras parameters 47 | * @param string $code default [ 0 ] 48 | * @return void 49 | */ 50 | public function __construct($message, array $state = NULL, $code = 0) 51 | { 52 | $this->error = $message; 53 | $this->state = array_filter((array) $state); 54 | 55 | // Pass the message to the parent 56 | parent::__construct($message, $code); 57 | } 58 | 59 | public function as_json() 60 | { 61 | $state = $this->state; 62 | 63 | $params = array('error' => $this->error); 64 | 65 | if(isset($state['error_uri'])) 66 | { 67 | $params['error_uri'] = url::site($state['error_uri'], TRUE); 68 | 69 | // don't append the customize error_uri to querystring 70 | unset($state['error_uri']); 71 | } 72 | else 73 | { 74 | $params['error_uri'] = url::site(Oauthz::config('error_uri'), TRUE).'/'.$this->error; 75 | } 76 | 77 | if(isset($state['error_description'])) 78 | { 79 | $params['error_description'] = __($state['error_description']); 80 | 81 | // don't append the customize error_description to querystring 82 | unset($state['error_description']); 83 | } 84 | else 85 | { 86 | $params['error_description'] = $this->error_description; 87 | } 88 | 89 | empty($state) OR $params['error_uri'] .= '?'.http_build_query($state, '', '&'); 90 | 91 | // JSON_UNESCAPED_SLASHES 92 | return str_replace('\\/', '/', json_encode($params)); 93 | } 94 | 95 | public function as_query() 96 | { 97 | if(isset($this->state['error_uri'])) 98 | { 99 | $params = array('error' => $this->error) + $this->state; 100 | 101 | if(isset($params['error_description'])) 102 | { 103 | $params['error_description'] = __($params['error_description']); 104 | } 105 | else 106 | { 107 | $params['error_description'] = $this->error_description; 108 | } 109 | 110 | $error_uri = url::site($params['error_uri'], TRUE); 111 | 112 | // don't append error_uri to querystring 113 | unset($params['error_uri']); 114 | 115 | $error_uri .= '?'.http_build_query($params, '', '&'); 116 | } 117 | else 118 | { 119 | // no need to expose error, error_description 120 | $error_uri = url::site(Oauthz::config('error_uri'), TRUE).'/'.$this->error; 121 | 122 | empty($this->state) OR $error_uri .= '?'.http_build_query($this->state, '', '&'); 123 | } 124 | 125 | return $error_uri; 126 | } 127 | 128 | } // END OAuth_Exception 129 | -------------------------------------------------------------------------------- /classes/oauthz/extension/code.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Code extends Oauthz_Extension { 16 | 17 | /** 18 | * REQUIRED. The client identifier as described in Section 2.1. 19 | * 20 | * @access public 21 | * @var string $client_id 22 | */ 23 | public $client_id; 24 | 25 | /** 26 | * REQUIRED. The redirection URI used in the initial request. 27 | * 28 | * @access public 29 | * @var string $redirect_uri 30 | */ 31 | public $redirect_uri; 32 | 33 | public $expires_in; 34 | 35 | /** 36 | * Load oauth parameters from GET or POST 37 | * 38 | * @access public 39 | * @param string $flag default [ FALSE ] 40 | * @return void 41 | * @throw Oauthz_Exception_Authorize Error Codes: invalid_request 42 | */ 43 | public function __construct(array $args) 44 | { 45 | // Parse the "state" paramter 46 | if(isset($_GET['state'])) 47 | { 48 | $this->state['state'] = rawurldecode($_GET['state']); 49 | 50 | unset($args['state']); 51 | } 52 | 53 | // Check all required parameters should not be empty 54 | foreach($args as $key => $val) 55 | { 56 | if($val === TRUE) 57 | { 58 | if(isset($_GET[$key]) AND $value = rawurldecode($_GET[$key])) 59 | { 60 | $this->$key = $value; 61 | } 62 | else 63 | { 64 | throw new Oauthz_Exception_Authorize('invalid_request', $this->state); 65 | } 66 | } 67 | elseif($val !== FALSE) 68 | { 69 | $this->$key = $val; 70 | } 71 | } 72 | } 73 | 74 | /** 75 | * Populate the oauth token from the request info and client info store in the server 76 | * 77 | * @access public 78 | * @param array $client 79 | * @return Oauthz_Token 80 | * @throw Oauthz_Exception_Authorize Error Codes: invalid_scope, unauthorized_client 81 | */ 82 | public function execute() 83 | { 84 | // Verify the client and generate a code if successes 85 | if($client = Model_Oauthz::factory('Token') 86 | ->code($this->client_id, $this->token_type, $this->expires_in)) 87 | { 88 | // audit 89 | } 90 | else 91 | { 92 | // Invalid client_id 93 | throw new Oauthz_Exception_Authorize('unauthorized_client', $this->state); 94 | } 95 | 96 | if($client['redirect_uri'] !== $this->redirect_uri) 97 | { 98 | throw new Oauthz_Exception_Authorize('unauthorized_client', $this->state); 99 | } 100 | 101 | if( ! empty($this->scope) AND ! empty($client['scope'])) 102 | { 103 | if( ! in_array($this->scope, explode(' ', $client['scope']))) 104 | { 105 | // Redirect to client uri 106 | $params = $this->state; 107 | $params['error_uri'] = $this->redirect_uri; 108 | 109 | throw new Oauthz_Exception_Authorize('invalid_scope', $params); 110 | } 111 | } 112 | 113 | // Grants Authorization 114 | $token = new Oauthz_Token; 115 | 116 | $token->code = $client['code']; 117 | $token->token_type = $client['token_type']; 118 | $token->expires_in = $client['expires_in']; 119 | 120 | isset($this->state['state']) AND $token->state = $this->state['state']; 121 | 122 | return $this->redirect_uri.'?'.$token->as_query(); 123 | } 124 | 125 | } // END Oauthz_Extension_Code 126 | -------------------------------------------------------------------------------- /classes/oauthz/extension/assertion.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Oauthz_Extension 11 | * * 12 | */ 13 | class Oauthz_Extension_Assertion extends Oauthz_Extension { 14 | 15 | /** 16 | * assertion_type 17 | * REQUIRED. The format of the assertion as defined by the 18 | * authorization server. The value MUST be an absolute URI. 19 | */ 20 | public $assertion_type; 21 | 22 | /** 23 | * assertion 24 | * REQUIRED. The assertion. 25 | */ 26 | public $assertion; 27 | 28 | /** 29 | * client_id 30 | * OPTIONAL. The client identifier as described in Section 2.1. 31 | * The authorization server MAY require including the client 32 | * credentials with the request based on the assertion properties. 33 | * client_secret 34 | * OPTIONAL. The client secret as described in Section 2.1. MUST 35 | * NOT be included if the "client_id" parameter is omitted. 36 | * scope 37 | * OPTIONAL. The scope of the access request expressed as a list 38 | * of space-delimited strings. The value of the "scope" parameter 39 | * is defined by the authorization server. If the value contains 40 | * multiple space-delimited strings, their order does not matter, 41 | * and each string adds an additional access range to the 42 | * requested scope. 43 | * format 44 | * OPTIONAL. The response format requested by the client. Value 45 | * MUST be one of "json", "xml", or "form". Alternatively, the 46 | * client MAY use the HTTP "Accept" header field with the desired 47 | * media type. Defaults to "json" if omitted and no "Accept" 48 | * header field is present. 49 | */ 50 | 51 | public function __construct($args = NULL) 52 | { 53 | $params = Oauthz::parse_query(); 54 | $this->assertion_type = Arr::get($params, 'username'); 55 | $this->assertion = Arr::get($params, 'password'); 56 | 57 | if(NULL !== $client_id = Arr::get($params, 'client_id')) 58 | $this->client_id = $client_id; 59 | 60 | if(NULL !== $client_secret = Arr::get($params, 'client_secret')) 61 | $this->client_secret = $client_secret; 62 | 63 | if(NULL !== $scope = Arr::get($params, 'scope')) 64 | $this->scope = $scope; 65 | 66 | if(NULL !== $format = Arr::get($params, 'format')) 67 | $this->format = $format; 68 | } 69 | 70 | public function execute() 71 | { 72 | $token = new Model_Oauthz_Token; 73 | 74 | if( ! $client = $token->assertion($this->client_id)) 75 | { 76 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 77 | } 78 | 79 | if($client['assertion_type'] !== $this->assertion_type) 80 | { 81 | throw new Oauthz_Exception_Token('unknown-format', $this->state); 82 | } 83 | 84 | if($client['assertion'] !== $this->assertion 85 | OR (property_exists($this, 'client_id') AND $client['client_id'] !== $this->client_id) 86 | OR (property_exists($this, 'client_secret') AND $client['client_secret'] !== sha1($this->client_secret)) 87 | OR (property_exists($this, 'scope') AND ! isset($client['scope'][$this->scope])) 88 | { 89 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 90 | } 91 | 92 | $token = new Oauthz_Token; 93 | 94 | // Grants Authorization 95 | // The authorization server SHOULD NOT issue a refresh token. 96 | $token->access_token = $client['access_token']; 97 | $token->assertion_type = $this->assertion_type; 98 | 99 | return $token; 100 | } 101 | 102 | } // END Oauthz_Extension_Assertion 103 | -------------------------------------------------------------------------------- /classes/oauthz/controller.php: -------------------------------------------------------------------------------- 1 | 7 | * @package Oauthz 8 | * @copyright (c) 2010 OALite 9 | * @license ISC License (ISCL) 10 | * @link http://oalite.com 11 | * @see Kohana_Controller 12 | * * 13 | */ 14 | abstract class Oauthz_Controller extends Kohana_Controller { 15 | 16 | /** 17 | * The end-user authenticates directly with the authorization server, and grants client access to its protected resources 18 | * 19 | * @access public 20 | * @return void 21 | */ 22 | public function action_authorize() 23 | { 24 | try 25 | { 26 | // There is handler for this response type 27 | if($response_type = Arr::get($_GET, 'response_type')) 28 | { 29 | $arguments = Arr::get(Oauthz::config('params'), $response_type, array()); 30 | 31 | if($extension = Oauthz_Extension::factory($response_type, $arguments)) 32 | { 33 | if( ! Oauthz::is_login()) 34 | { 35 | $this->request->redirect(Oauthz::config('login_uri').'redirect='.rawurlencode($this->request->url().url::query())); 36 | } 37 | $response = $extension->execute(); 38 | } 39 | } 40 | 41 | // This response type is unsupported 42 | if( ! isset($response)) 43 | { 44 | $params = isset($_GET['state']) ? array('state' => $_GET['state']) : NULL; 45 | 46 | throw new Oauthz_Exception_Authorize('unsupported_response_type', $params); 47 | } 48 | } 49 | catch (Oauthz_Exception $e) 50 | { 51 | $response = $e->as_query(); 52 | } 53 | 54 | // HTTP/1.1 302 Found 55 | $this->request->status = 302; 56 | $this->request->headers['Content-Type'] = 'application/x-www-form-urlencoded'; 57 | $this->request->redirect($response); 58 | } 59 | 60 | /** 61 | * Access token handler for the client requests 62 | * 63 | * @access public 64 | * @return void 65 | */ 66 | public function action_token() 67 | { 68 | try 69 | { 70 | // There is handler for this grant type 71 | if($grant_type = Oauthz::get('grant_type')) 72 | { 73 | $arguments = Arr::get(Oauthz::config('params'), $grant_type, array()); 74 | 75 | if($extension = Oauthz_Extension::factory($grant_type, $arguments)) 76 | { 77 | $response = $extension->execute(); 78 | } 79 | } 80 | 81 | if(isset($response) AND $response instanceOf Oauthz_Token) 82 | { 83 | // HTTP/1.1 200 OK 84 | $this->request->status = 200; 85 | $this->request->headers['Content-Type'] = $response->format; 86 | } 87 | else 88 | { 89 | /** 90 | * HTTP/1.1 401 (Unauthorized) for "Authorization" request header field 91 | * HTTP/1.1 400 Bad Request for other authentication scheme 92 | */ 93 | $this->request->status = 400; 94 | 95 | $params = Oauthz::get('state') ? array('state' => Oauthz::get('state')) : NULL; 96 | 97 | throw new Oauthz_Exception_Token('unsupported_grant_type', $params); 98 | } 99 | } 100 | catch (Oauthz_Exception $e) 101 | { 102 | $this->request->headers['Content-Type'] = 'application/json'; 103 | 104 | $response = $e->as_json(); 105 | } 106 | 107 | $this->request->headers['Expires'] = 'Sat, 26 Jul 1997 05:00:00 GMT'; 108 | $this->request->headers['Cache-Control'] = 'no-store, must-revalidate'; 109 | $this->request->response = $response; 110 | } 111 | 112 | } // END Oauthz Server Controller 113 | -------------------------------------------------------------------------------- /config/oauth-server.php: -------------------------------------------------------------------------------- 1 | array( 8 | 9 | 'realm' => 'REST API', 10 | 11 | // '' - no login required, 'basic' - unsecure login, 'digest' - more secure login, 'OAuth' - cool 12 | 'auth' => 'OAuth', 13 | 14 | /** 15 | * Set to FALSE to ignore the HTTP Accept and speed up each request a little. 16 | * Only do this if you are using the follow formats or /format/xml in URLs 17 | */ 18 | 'http_accept'=> FALSE, 19 | 20 | // login uri for the resource owner, before he approves the client's request 21 | // [!!] this uri will be appended the redirect parameter. therefore the last char must be '?' or '&' 22 | // e.g. /user/signin?, /user/signin?my=xxx& 23 | 'login_uri' => '/oauth/signin?', 24 | 25 | // Error info base uri 26 | 'error_uri' => '/oauth/error', 27 | 28 | /** 29 | * TODO: Authentication methods for each flows 30 | */ 31 | 'methods' => array( 32 | 'authorization_code' => array('basic', 'digest', 'mac'), 33 | 'access_token' => array('bearer', 'mac'), 34 | ), 35 | 36 | /** 37 | * Parameters should be required when request authorization code 38 | * cryptographic token or bear token 39 | * 40 | * TRUE: the value of this parameter is obtained from request parameters 41 | * FALSE: this parameter is disabled 42 | * otherwise: this parameter will be binded to token object 43 | */ 44 | 'params' => array( 45 | // Parameters should be required for response_type endpoint 46 | 'code' => array( 47 | 'client_id' => TRUE, 48 | 'redirect_uri' => TRUE, 49 | 'scope' => FALSE, 50 | 'state' => FALSE, 51 | // authorization code expires time, default is 2 minutes 52 | 'expires_in' => 60, 53 | 54 | // The follow Parameters are used for access token request 55 | 'token_type' => 'bearer' 56 | ), 57 | // Parameters should be required for grant_type endpoint 58 | 'authorization_code' => array( 59 | 'code' => TRUE, 60 | 'redirect_uri' => TRUE, 61 | 'client_id' => TRUE, 62 | 'client_secret' => TRUE, 63 | // authorization code expires time, default is 5 minutes 64 | 'expires_in' => 300 65 | ), 66 | 'token' => array( 67 | 'code' => TRUE, 68 | 'redirect_uri' => TRUE, 69 | 'client_id' => TRUE, 70 | 'client_secret' => TRUE, 71 | 'scope' => FALSE, 72 | 'state' => FALSE, 73 | 'mac_key' => FALSE, 74 | 'mac_algorithm' => FALSE, 75 | // token expires time, default is 1 hour 76 | 'expires_in' => 3600 77 | ), 78 | 'password' => array( 79 | 'username' => TRUE, 80 | 'password' => TRUE, 81 | 'client_id' => TRUE, 82 | 'client_secret' => TRUE, 83 | 'mac_key' => FALSE, 84 | 'mac_algorithm' => FALSE, 85 | // token expires time, default is 1 hour 86 | 'expires_in' => 3600 87 | ), 88 | 'refresh_token' => array( 89 | 'refresh_token' => TRUE, 90 | 'client_id' => TRUE, 91 | 'client_secret' => TRUE, 92 | 'mac_key' => FALSE, 93 | 'mac_algorithm' => FALSE, 94 | // refresh token expires time, default is 1 day 95 | 'expires_in' => 86400 96 | ), 97 | // TODO 98 | 'assertion' => array( 99 | 'assertion_type' => TRUE, 100 | 'assertion' => TRUE, 101 | 'client_id' => TRUE, 102 | 'client_secret' => TRUE 103 | ) 104 | ), 105 | 'scopes' => array( 106 | 'get' => TRUE, 107 | 'create' => TRUE, 108 | 'update' => TRUE, 109 | 'delete' => TRUE 110 | ) 111 | ) 112 | 113 | ); // END OAuth server config 114 | -------------------------------------------------------------------------------- /classes/oauthz/token.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * * 11 | */ 12 | class Oauthz_Token { 13 | 14 | public $format = 'json'; 15 | 16 | public function __construct(array $params = array()) 17 | { 18 | foreach($params as $key => $val) 19 | { 20 | $this->$key = $val; 21 | } 22 | } 23 | 24 | public function as_json() 25 | { 26 | if(empty($this->error)) 27 | { 28 | $json = get_object_vars($this); 29 | foreach($json as $key => $val) 30 | { 31 | if(empty($val)) unset($json[$key]); 32 | } 33 | } 34 | else 35 | { 36 | $json = array('error' => $this->error); 37 | 38 | isset($this->state) AND $json['state'] = $this->state; 39 | } 40 | 41 | // JSON_UNESCAPED_SLASHES 42 | return str_replace('\\/', '/', json_encode($json)); 43 | } 44 | 45 | public function as_xml() 46 | { 47 | $doc = new DOMDocument('1.0', 'UTF-8'); 48 | $doc->formatOutput = true; 49 | 50 | $oauth = $doc->createElement('OAuth'); 51 | $doc->appendChild($oauth); 52 | 53 | if(empty($this->error)) 54 | { 55 | foreach(get_object_vars($this) as $key => $val) 56 | { 57 | if(empty($val)) continue; 58 | 59 | $node = $doc->createElement($key); 60 | $node->appendChild($doc->createTextNode($val)); 61 | $oauth->appendChild($node); 62 | } 63 | } 64 | else 65 | { 66 | $node = $doc->createElement('error'); 67 | $node->appendChild($doc->createTextNode($this->error)); 68 | $oauth->appendChild($node); 69 | 70 | if(isset($this->state)) 71 | { 72 | $node = $doc->createElement('state'); 73 | $node->appendChild($doc->createTextNode($this->state)); 74 | $oauth->appendChild($node); 75 | } 76 | } 77 | return $doc->saveXML(); 78 | } 79 | 80 | public function as_query() 81 | { 82 | if(empty($this->error)) 83 | { 84 | $form = get_object_vars($this); 85 | foreach($form as $key => $val) 86 | { 87 | if(empty($val)) unset($form[$key]); 88 | } 89 | } 90 | else 91 | { 92 | $form = array('error' => $this->error); 93 | 94 | isset($this->state) AND $form['state'] = $this->state; 95 | 96 | isset($this->error_uri) AND $form['error_uri'] = $this->error_uri; 97 | 98 | isset($this->error_description) AND $form['error_description'] = $this->error_description; 99 | } 100 | return http_build_query($form, '', '&'); 101 | } 102 | 103 | public function as_html() 104 | { 105 | $text = '
'; 106 | if(empty($this->error)) 107 | { 108 | $form = get_object_vars($this); 109 | foreach($form as $key => $val) 110 | { 111 | if(empty($val)) continue; 112 | 113 | $text .= '
'.$key.'
'.$val.'
'; 114 | } 115 | } 116 | else 117 | { 118 | $text .= '
error
'.$this->error.'
'; 119 | 120 | isset($this->state) AND $text .= '
state
'.$this->state.'
'; 121 | } 122 | return $text.'
'; 123 | } 124 | 125 | /** 126 | * Serialize token to string that a server would respond to 127 | * 128 | * @access public 129 | * @return string 130 | */ 131 | public function __toString() 132 | { 133 | $format = $this->format; 134 | $this->format = NULL; 135 | switch($format) 136 | { 137 | case 'xml': 138 | case 'application/xhtml+xml': 139 | $res = $this->as_xml(); 140 | $this->format = 'application/xml'; 141 | break; 142 | case 'form': 143 | $res = $this->as_query(); 144 | $this->format = 'application/x-www-form-urlencoded'; 145 | break; 146 | case 'text/html': 147 | $res = $this->as_html(); 148 | $this->format = 'text/html'; 149 | break; 150 | default: 151 | $res = $this->as_json(); 152 | $this->format = 'application/json'; 153 | break; 154 | } 155 | return $res; 156 | } 157 | 158 | public function __set($key, $val) 159 | { 160 | if( ! empty($val)) $this->$key = $val; 161 | } 162 | 163 | } // END Oauthz_Token 164 | -------------------------------------------------------------------------------- /classes/oauthz/extension/authorization/code.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Authorization_Code extends Oauthz_Extension { 16 | 17 | /** 18 | * REQUIRED. The client identifier as described in Section 2.1. 19 | * 20 | * @access public 21 | * @var string $client_id 22 | */ 23 | public $client_id; 24 | 25 | /** 26 | * REQUIRED. The redirection URI used in the initial request. 27 | * 28 | * @access public 29 | * @var string $redirect_uri 30 | */ 31 | public $redirect_uri; 32 | 33 | /** 34 | * Load oauth parameters from GET or POST 35 | * 36 | * @access public 37 | * @param string $flag default [ FALSE ] 38 | * @return void 39 | */ 40 | public function __construct(array $args) 41 | { 42 | // Parse the "state" paramter 43 | if(isset($_POST['state'])) 44 | { 45 | if($state = rawurldecode($_POST['state'])) 46 | $this->state['state'] = $state; 47 | 48 | unset($args['state']); 49 | } 50 | 51 | // Check all required parameters should NOT be empty 52 | foreach($args as $key => $val) 53 | { 54 | if($val === TRUE) 55 | { 56 | if(isset($_POST[$key]) AND $value = rawurldecode($_POST[$key])) 57 | { 58 | $this->$key = $value; 59 | } 60 | else 61 | { 62 | throw new Oauthz_Exception_Authorize('invalid_request', $this->state); 63 | } 64 | } 65 | elseif($val !== FALSE) 66 | { 67 | $this->$key = $val; 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Populate the access token thu the request info and client info stored in the server 74 | * 75 | * @access public 76 | * @param array $client 77 | * @return Oauthz_Token 78 | * @throw Oauthz_Exception_Authorize Error Codes: invalid_request, invalid_scope 79 | */ 80 | public function execute() 81 | { 82 | if($client = Model_Oauthz::factory('Token') 83 | ->token($this->client_id, $this->code, $this->expires_in)) 84 | { 85 | //$audit = new Model_Oauthz_Audit; 86 | //$audit->audit_token($token); 87 | 88 | // Verify the oauth token send by client 89 | } 90 | else 91 | { 92 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 93 | } 94 | 95 | if($client['redirect_uri'] !== $this->redirect_uri) 96 | { 97 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 98 | } 99 | 100 | if($client['client_secret'] !== sha1($this->client_secret)) 101 | { 102 | // Redirect to client uri 103 | $params = $this->state; 104 | $params['error_uri'] = $this->redirect_uri; 105 | 106 | throw new Oauthz_Exception_Token('unauthorized_client', $params); 107 | } 108 | 109 | if( ! empty($this->scope) AND ! empty($client['scope'])) 110 | { 111 | if( ! in_array($this->scope, explode(' ', $client['scope']))) 112 | { 113 | $params = $this->state; 114 | $params['error_uri'] = $this->redirect_uri; 115 | 116 | throw new Oauthz_Exception_Authorize('invalid_scope', $params); 117 | } 118 | } 119 | 120 | if($client['expires_in'] < $_SERVER['REQUEST_TIME']) 121 | { 122 | $params = $this->state; 123 | $params['error_uri'] = $this->redirect_uri; 124 | 125 | throw new Oauthz_Exception_Access('invalid_grant', $params); 126 | } 127 | 128 | // Everything is ok, then return the token 129 | $token = new Oauthz_Token; 130 | 131 | // TODO: issue "mac" OAuth Access Token Type 132 | $token->token_type = $client['token_type']; 133 | $token->access_token = $client['access_token']; 134 | $token->refresh_token = $client['refresh_token']; 135 | $token->expires_in = $client['expires_in']; 136 | 137 | // merge other token properties, e.g. {"mac_key":"adijq39jdlaska9asud","mac_algorithm":"hmac-sha-256"} 138 | if($client['options'] AND $option = json_decode($client['options'], TRUE)) 139 | { 140 | foreach($option as $key => $val) 141 | { 142 | $token->$key = $val; 143 | } 144 | } 145 | 146 | isset($this->state['state']) AND $token->state = $this->state['state']; 147 | 148 | return $token; 149 | } 150 | 151 | } // END Oauthz_Extension_Authorization_Code 152 | -------------------------------------------------------------------------------- /classes/oauthz/api.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Kohana_Controller 11 | * * 12 | */ 13 | abstract class Oauthz_Api extends Kohana_Controller { 14 | 15 | /** 16 | * Config group name 17 | * 18 | * @access protected 19 | * @var string $_type 20 | */ 21 | protected $_type = 'default'; 22 | 23 | /** 24 | * methods exclude from OAuth protection 25 | * 26 | * @access protected 27 | * @var array $_exclude 28 | */ 29 | protected $_exclude = array('xrds'); 30 | 31 | /** 32 | * Verify the request to protected resource. 33 | * if unauthorized, redirect action to invalid_request 34 | * 35 | * @access public 36 | * @return void 37 | */ 38 | public function __construct(Request $request) 39 | { 40 | // Exclude actions do NOT need to protect 41 | if( ! in_array($request->action, $this->_exclude)) 42 | { 43 | $config = Kohana::config('oauth-api')->get($this->_type); 44 | 45 | switch(Request::$method) 46 | { 47 | case 'POST': 48 | $target = $_POST; 49 | break; 50 | case 'GET': 51 | $target = $_GET; 52 | break; 53 | } 54 | 55 | try 56 | { 57 | // Verify the request method supported in the config settings 58 | if( ! isset($target) OR empty($config['methods'][Request::$method])) 59 | { 60 | throw new Oauthz_Exception_Access('invalid_request', Oauthz_Authentication::state($target)); 61 | } 62 | 63 | $token_type = Arr::get($target, 'token_type', 'bearer'); 64 | 65 | if( ! isset($config[$token_type])) 66 | { 67 | throw new Oauthz_Exception_Access('invalid_grant', Oauthz_Authentication::state($target)); 68 | } 69 | 70 | // Process the access token from the request header or body 71 | $oauth = Oauthz_Authentication::factory($token_type, $config[$token_type], $target); 72 | 73 | // Load the token information from database 74 | if( ! $token = Model_Oauthz::factory('Token') 75 | ->access_token($oauth->token())) 76 | { 77 | throw new Oauthz_Exception_Access('unauthorized_client', Oauthz_Authentication::state($target)); 78 | } 79 | 80 | if($token['expires_in'] < $_SERVER['REQUEST_TIME']) 81 | { 82 | throw new Oauthz_Exception_Access('invalid_grant', Oauthz_Authentication::state($target)); 83 | } 84 | 85 | // Verify the access token 86 | $oauth->verify($token); 87 | } 88 | catch (Oauthz_Exception $e) 89 | { 90 | $this->exception = $e; 91 | 92 | // Redirect the action to unauthenticated 93 | $request->action = 'unauthenticated'; 94 | } 95 | } 96 | 97 | parent::__construct($request); 98 | } 99 | 100 | /** 101 | * Unauthorized response, only be called from internal 102 | * 103 | * @access public 104 | * @param string $error 105 | * @return void 106 | * @todo Add list of error codes 107 | */ 108 | public function action_unauthenticated() 109 | { 110 | if($this->exception->error === 'invalid_client') 111 | { 112 | // HTTP/1.1 401 Unauthorized 113 | $this->request->status = 401; 114 | // TODO: If the client attempted to authenticate via the "Authorization" request header field 115 | // the "WWW-Authenticate" response header field matching the authentication scheme used by the client. 116 | // $this->request->headers['WWW-Authenticate'] = 'OAuth2 realm=\'Service\','.http_build_query($error, '', ','); 117 | } 118 | else 119 | { 120 | // HTTP/1.1 400 Bad Request 121 | $this->request->status = 400; 122 | } 123 | 124 | $this->request->headers['Content-Type'] = 'application/json'; 125 | $this->request->headers['Expires'] = 'Sat, 26 Jul 1997 05:00:00 GMT'; 126 | $this->request->headers['Cache-Control'] = 'no-store, must-revalidate'; 127 | $this->request->response = $this->exception->as_json(); 128 | } 129 | 130 | /** 131 | * OAuth server auto-discovery for user 132 | * 133 | * @access public 134 | * @return void 135 | * @todo Add list of error codes 136 | */ 137 | public function action_xrds() 138 | { 139 | $this->request->headers['Content-Type'] = 'application/xrds+xml'; 140 | $this->request->response = View::factory('oauth-server-xrds')->render(); 141 | } 142 | 143 | } // END Oauthz_Api 144 | -------------------------------------------------------------------------------- /views/oauthz-template.php: -------------------------------------------------------------------------------- 1 | get('user'); 3 | ?> 4 | 5 | 6 | 7 | 8 | 9 | OAuth Test 10 | 11 | 12 | 13 | 99 | 100 |
101 |
102 |
103 |
120 |
121 |
133 | 134 | 135 | -------------------------------------------------------------------------------- /doc/oauth-mysql.sql: -------------------------------------------------------------------------------- 1 | /** 2 | * OAuth database table schema for MySQL 3 | * 4 | * @author sumh <42424861@qq.com> 5 | * @package Oauth 6 | * @copyright (c) 2011 OALite 7 | * @license ISC License (ISCL) 8 | * @link http://oalite.com 9 | */ 10 | 11 | /******************** Add Table: t_oauth_authorizes ************************/ 12 | 13 | /* Build Table Structure */ 14 | CREATE TABLE t_oauth_authorizes 15 | ( 16 | user_id BIGINT UNSIGNED NULL, 17 | client_id VARCHAR(127) NOT NULL, 18 | redirect_uri VARCHAR(511) NOT NULL, 19 | confirm_type TINYINT UNSIGNED DEFAULT 0 20 | COMMENT 'Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned' NOT NULL, 21 | client_level TINYINT UNSIGNED DEFAULT 0 22 | COMMENT 'diferent client levels have different max request times' NOT NULL, 23 | client_desc TEXT NULL, 24 | expires_in INTEGER UNSIGNED 25 | COMMENT 'date time' NULL, 26 | scope VARCHAR(511) NULL, 27 | created INTEGER UNSIGNED NOT NULL, 28 | modified INTEGER UNSIGNED NULL, 29 | remark TEXT NULL 30 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 31 | 32 | /* Table Items: t_oauth_authorizes */ 33 | ALTER TABLE t_oauth_authorizes ADD CONSTRAINT pkt_oauth_authorizes 34 | PRIMARY KEY (user_id); 35 | 36 | /* Set Comments */ 37 | ALTER TABLE t_oauth_authorizes COMMENT = 'Store audit information from resource owner for the resource requester'; 38 | 39 | /******************** Add Table: t_oauth_clients ************************/ 40 | 41 | /* Build Table Structure */ 42 | CREATE TABLE t_oauth_clients 43 | ( 44 | server_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, 45 | client_id VARCHAR(127) 46 | COMMENT 'AKA. API key' NOT NULL, 47 | client_secret VARCHAR(127) 48 | COMMENT 'AKA. API secret' NOT NULL, 49 | redirect_uri VARCHAR(511) 50 | COMMENT 'AKA. Callback URI' NOT NULL, 51 | scope VARCHAR(255) 52 | COMMENT 'May be create, read, update or delete. so on so for' NULL, 53 | secret_type ENUM('plaintext','md5','rsa-sha1','hmac-sha1') DEFAULT 'plaintext' 54 | COMMENT 'Secret signature encrypt type. e.g' NOT NULL, 55 | ssh_key VARCHAR(511) 56 | COMMENT 'SSH public keys' NULL, 57 | app_name VARCHAR(127) 58 | COMMENT 'Application Name' NOT NULL, 59 | app_desc TEXT 60 | COMMENT 'Application Description, When users authenticate via your app, this is what they\'ll see.' NULL, 61 | app_profile ENUM('webserver','native','useragent','autonomous') DEFAULT 'webserver' 62 | COMMENT 'Application Profile: Web Server Application, Native Application, Browser Application, Autonomous clients' NOT NULL, 63 | app_purpose VARCHAR(511) NULL, 64 | user_id BIGINT UNSIGNED 65 | COMMENT 'Ref# from users table' NULL, 66 | user_level TINYINT UNSIGNED DEFAULT 0 67 | COMMENT 'diferent client levels have different max request times' NOT NULL, 68 | enabled TINYINT UNSIGNED DEFAULT 1 69 | COMMENT '0: waiting for system administrator audit; 1: acceptable; 2: ban' NOT NULL, 70 | created INTEGER UNSIGNED 71 | COMMENT 'create datetime' NOT NULL, 72 | modified INTEGER UNSIGNED 73 | COMMENT 'modified datetime' NULL 74 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 75 | 76 | /* Table Items: t_oauth_clients */ 77 | 78 | /* Set Comments */ 79 | ALTER TABLE t_oauth_clients COMMENT = 'Used for verification of incoming requests. '; 80 | 81 | /* Add Indexes for: t_oauth_clients */ 82 | CREATE UNIQUE INDEX idx_t_oauth_clients_client_id ON t_oauth_clients (client_id); 83 | 84 | /******************** Add Table: t_oauth_logs ************************/ 85 | 86 | /* Build Table Structure */ 87 | CREATE TABLE t_oauth_logs 88 | ( 89 | log_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, 90 | client_id VARCHAR(127) NULL, 91 | token VARCHAR(63) NULL, 92 | user_id BIGINT UNSIGNED NULL, 93 | received TEXT NOT NULL, 94 | sent TEXT NOT NULL, 95 | body TEXT NOT NULL, 96 | notes TEXT NOT NULL, 97 | `timestamp` TIMESTAMP NOT NULL, 98 | remote_ip BIGINT NOT NULL 99 | ) DEFAULT CHARSET=utf8; 100 | 101 | /* Table Items: t_oauth_logs */ 102 | 103 | /* Set Comments */ 104 | ALTER TABLE t_oauth_logs COMMENT = 'Log table to hold all OAuth request when you enabled logging '; 105 | 106 | /* Add Indexes for: t_oauth_logs */ 107 | CREATE INDEX idx_t_oauth_logs_client_id_log_id ON t_oauth_logs (client_id, log_id); 108 | 109 | /******************** Add Table: t_oauth_tokens ************************/ 110 | 111 | /* Build Table Structure */ 112 | CREATE TABLE t_oauth_tokens 113 | ( 114 | token_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, 115 | client_id VARCHAR(127) NOT NULL, 116 | user_id BIGINT UNSIGNED 117 | COMMENT 'Ref# from users table' NOT NULL, 118 | code VARCHAR(127) NOT NULL, 119 | access_token VARCHAR(63) NULL, 120 | refresh_token VARCHAR(63) NULL, 121 | expire_code INTEGER UNSIGNED DEFAULT 300 122 | COMMENT 'authorization code expires in this timestamp' NOT NULL, 123 | expire_token INTEGER UNSIGNED DEFAULT 0 124 | COMMENT 'access token expires in this timestamp' NOT NULL, 125 | expire_refresh INTEGER UNSIGNED DEFAULT 0 126 | COMMENT 'refresh token expires in this timestamp' NOT NULL, 127 | `timestamp` INTEGER UNSIGNED 128 | COMMENT 'authorization code request timestamp' NOT NULL, 129 | token_type VARCHAR(127) 130 | COMMENT 'bearer, mac, etc.' NOT NULL, 131 | options TEXT 132 | COMMENT 'parameters for different token type extension in json format' NULL 133 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 134 | 135 | /* Table Items: t_oauth_tokens */ 136 | 137 | /* Set Comments */ 138 | ALTER TABLE t_oauth_tokens COMMENT = 'Table used to verify signed requests sent to a server by the consumer.When the verification is succesful then the associated user id is returned. '; 139 | 140 | /* Add Indexes for: t_oauth_tokens */ 141 | CREATE INDEX idx_t_oauth_tokens_access_token ON t_oauth_tokens (access_token); 142 | CREATE INDEX idx_t_oauth_tokens_client_id ON t_oauth_tokens (client_id); 143 | CREATE INDEX idx_t_oauth_tokens_code ON t_oauth_tokens (code); 144 | CREATE INDEX idx_t_oauth_tokens_refresh_token ON t_oauth_tokens (refresh_token); 145 | 146 | 147 | /************ Add Foreign Keys to Database ***************/ 148 | 149 | /************ Foreign Key: fk_t_oauth_tokens_t_oauth_clients ***************/ 150 | ALTER TABLE t_oauth_tokens ADD CONSTRAINT fk_t_oauth_tokens_t_oauth_clients 151 | FOREIGN KEY (client_id) REFERENCES t_oauth_clients (client_id) 152 | ON UPDATE CASCADE ON DELETE CASCADE; -------------------------------------------------------------------------------- /classes/oauthz/extension/refresh/token.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Refresh_Token extends Oauthz_Extension { 16 | 17 | /** 18 | * client_id 19 | * REQUIRED. The client identifier 20 | */ 21 | public $client_id; 22 | 23 | /** 24 | * client_secret 25 | * REQUIRED. The client secret 26 | * OPTIONAL if no client secret was issued. 27 | */ 28 | public $client_secret; 29 | 30 | /** 31 | * redirect_uri 32 | * REQUIRED unless a redirection URI has been established between 33 | * the client and authorization server via other means. An 34 | * absolute URI to which the authorization server will redirect 35 | * the user-agent to when the end-user authorization step is 36 | * completed. The authorization server SHOULD require the client 37 | * to pre-register their redirection URI. Authorization servers 38 | * MAY restrict the redirection URI to not include a query 39 | * component as defined by [RFC3986] section 3. 40 | */ 41 | public $redirect_uri; 42 | 43 | /** 44 | * state 45 | * OPTIONAL. An opaque value used by the client to maintain state 46 | * between the request and callback. The authorization server 47 | * includes this value when redirecting the user-agent back to the 48 | * client. 49 | * 50 | * scope 51 | * OPTIONAL. The scope of the access request expressed as a list 52 | * of space-delimited strings. The value of the "scope" parameter 53 | * is defined by the authorization server. If the value contains 54 | * multiple space-delimited strings, their order does not matter, 55 | * and each string adds an additional access range to the 56 | * requested scope. 57 | * 58 | * @access public 59 | * @return void 60 | * @throw Oauthz_Exception_Token Error Codes: invalid_request 61 | */ 62 | public function __construct($args = NULL) 63 | { 64 | isset($_SERVER['CONTENT_TYPE']) OR $_SERVER['CONTENT_TYPE'] = getenv('CONTENT_TYPE'); 65 | 66 | // Parse the "state" paramter 67 | if(isset($_POST['state'])) 68 | { 69 | if($state = rawurldecode($_POST['state'])) 70 | $this->state['state'] = $state; 71 | 72 | unset($args['state']); 73 | } 74 | 75 | // oauth_token already send in authorization header or the encrypt Content-Type is not single-part 76 | if(empty($_POST) OR stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === FALSE) 77 | { 78 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 79 | } 80 | else 81 | { 82 | if(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])) 83 | { 84 | $_POST += array('username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']); 85 | } 86 | // TODO Digest HTTP authentication 87 | //else if( ! empty($_SERVER['PHP_AUTH_DIGEST']) AND $digest = parent::parse_digest($_SERVER['PHP_AUTH_DIGEST'])) 88 | //{ 89 | // $_POST += array('username' => $digest['username'], 'password' => $digest['']); 90 | //} 91 | 92 | // Check all required parameters from form-encoded body 93 | foreach($args as $key => $val) 94 | { 95 | if($val === TRUE) 96 | { 97 | if(isset($_POST[$key]) AND $value = rawurldecode($_POST[$key])) 98 | { 99 | $this->$key = $value; 100 | } 101 | else 102 | { 103 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * TODO Refresh token 112 | * 113 | * @access public 114 | * @param array $client 115 | * @return Oauthz_Token 116 | * @throw Oauthz_Exception_Authorize Error Codes: invalid_request, invalid_scope 117 | */ 118 | public function execute() 119 | { 120 | if($client = Model_Oauthz::factory('Client')->lookup($this->client_id)) 121 | { 122 | // Verify the user information send by client 123 | } 124 | else 125 | { 126 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 127 | } 128 | 129 | if($client['redirect_uri'] !== $this->redirect_uri) 130 | { 131 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 132 | } 133 | 134 | if($client['client_secret'] !== sha1($this->client_secret)) 135 | { 136 | $params = $this->state; 137 | $params['error_uri'] = $this->redirect_uri; 138 | 139 | throw new Oauthz_Exception_Token('invalid_request', $params); 140 | } 141 | 142 | if(isset($this->scope) AND ! empty($client['scope'])) 143 | { 144 | if( ! in_array($this->scope, explode(' ', $client['scope']))) 145 | { 146 | $params = $this->state; 147 | $params['error_uri'] = $this->redirect_uri; 148 | 149 | throw new Oauthz_Exception_Token('invalid_scope', $params); 150 | } 151 | } 152 | 153 | $token = new Oauthz_Token; 154 | 155 | $token->token_type = $client['token_type']; 156 | $token->access_token = $client['access_token']; 157 | $token->refresh_token = $client['refresh_token']; 158 | 159 | // merge other token properties, e.g. {"mac_key":"adijq39jdlaska9asud","mac_algorithm":"hmac-sha-256"} 160 | if($client['options'] AND $option = json_decode($client['options'], TRUE)) 161 | { 162 | foreach($option as $key => $val) 163 | { 164 | $token->$key = $val; 165 | } 166 | } 167 | 168 | isset($this->state['state']) AND $token->state = $this->state['state']; 169 | 170 | return $token; 171 | } 172 | 173 | } // END Oauthz_Extension_Client_Credentials 174 | -------------------------------------------------------------------------------- /classes/oauthz/client.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Kohana_Controller 11 | * * 12 | */ 13 | class Oauthz_Client extends Kohana_Controller { 14 | 15 | /** 16 | * provider config group name 17 | * 18 | * @access protected 19 | * @var string $provider 20 | */ 21 | protected $_type = 'default'; 22 | 23 | /** 24 | * Request settings for OAuth 25 | * 26 | * @access protected 27 | * @var string $_configs 28 | */ 29 | protected $_configs = null; 30 | 31 | public function before() 32 | { 33 | $this->_configs = Kohana::config('oauth-client')->get($this->_type); 34 | } 35 | 36 | /* Obtain an an Authorization Code, ONLY for Authorization Code flow */ 37 | public function action_code($uri = NULL) 38 | { 39 | $type = $this->_configs['protocol-flow']; 40 | 41 | if($type !== 'code') 42 | { 43 | // Redirect to correct action 44 | if(isset($this->_configs[$type])) 45 | { 46 | $this->request->redirect('client/do'.($uri ? "/$uri" : '')); 47 | } 48 | else 49 | { 50 | $template = new View('oauthz-template'); 51 | $template->content = 'Unsupported protocal flow, please check your oauth-client.php config.'; 52 | return $this->request->response = $template; 53 | } 54 | } 55 | 56 | empty($uri) AND $uri = $this->_configs['oauth-uri']; 57 | 58 | // Load the paramtes and remove all empty ones 59 | $params = array_filter($this->_configs['code']); 60 | $params['response_type'] = 'code'; 61 | 62 | // Don't expose the client_secret 63 | unset($params['client_secret']); 64 | 65 | $this->request->redirect($uri.'?'.http_build_query($params, '', '&')); 66 | } 67 | 68 | /* Request an access token and then access the protected resource */ 69 | public function action_do($uri = NULL) 70 | { 71 | $template = new View('oauthz-template'); 72 | 73 | try 74 | { 75 | $token = $this->token($uri); 76 | 77 | $param = array( 78 | CURLOPT_POST => TRUE, 79 | CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded;charset=utf-8'), 80 | CURLOPT_POSTFIELDS => http_build_query($token, '', '&') 81 | ); 82 | 83 | // Resource in json format 84 | $api_uri = $this->_configs['api-uri'].'get'; 85 | $hello = Remote::get($api_uri, $param); 86 | $resaults['first'] = array( 87 | 'uri' => $api_uri, 88 | 'info' => $hello, 89 | 'token' => $token['access_token'] 90 | ); 91 | 92 | // we try to use this token to request another more information 93 | $api_uri = $this->_configs['api-uri'].'get/1'; 94 | $world = Remote::get($api_uri, $param); 95 | $resaults['second'] = array( 96 | 'uri' => $api_uri, 97 | 'info' => $world, 98 | 'token' => $token['access_token'] 99 | ); 100 | 101 | $template->content = new View('oauthz-client-response', $resaults); 102 | } 103 | catch(Exception $e) 104 | { 105 | $error = $e->getMessage(); 106 | 107 | switch($error) 108 | { 109 | case 'access_denied': 110 | $template->content = 'You have denied this request.'; 111 | break; 112 | default: 113 | $template->content = "There must be some errors happen in this connection, 114 | please contact our web master.
error code : [$error]"; 115 | empty($e->error_description) OR $template->content .= ",
description : [$e->error_description]"; 116 | break; 117 | } 118 | } 119 | $this->request->response = $template; 120 | } 121 | 122 | /* Obtain an Access Token */ 123 | protected function token($uri = NULL) 124 | { 125 | empty($uri) OR $uri = $this->_configs['oauth-uri']; 126 | 127 | $type = $this->_configs['protocol-flow']; 128 | 129 | if(isset($this->_configs[$type])) 130 | { 131 | $params = $this->_configs[$type]; 132 | switch($type) 133 | { 134 | case 'code': 135 | if(empty($_GET['code'])) 136 | { 137 | throw new Oauthz_Exception(isset($_GET['error']) ? $_GET['error'] : 'Unknow error'); 138 | } 139 | $params['code'] = $_GET['code']; 140 | $params['grant_type'] = 'authorization_code'; 141 | break; 142 | case 'token': 143 | $params['response_type'] = 'token'; 144 | break; 145 | case 'password': 146 | $params['grant_type'] = 'password'; 147 | break; 148 | case 'client_credentials': 149 | $params['grant_type'] = 'client_credentials'; 150 | break; 151 | } 152 | 153 | // Request access token 154 | $token = Remote::get($this->_configs['token-uri'], array( 155 | CURLOPT_POST => TRUE, 156 | CURLOPT_HTTPHEADER => array('Content-Type: application/x-www-form-urlencoded;charset=utf-8'), 157 | CURLOPT_POSTFIELDS => http_build_query($params, '', '&') 158 | )); 159 | 160 | $token = json_decode($token, TRUE); 161 | 162 | if(isset($token['error'])) 163 | { 164 | throw new Oauthz_Exception_Access($token['error'], $token); 165 | } 166 | 167 | // Store the client info into the token 168 | $token['client_id'] = $params['client_id']; 169 | $token['client_secret'] = $params['client_secret']; 170 | 171 | return $token; 172 | } 173 | 174 | throw new Oauthz_Exception('Unsupported protocal flow, please check your oauth-client.php config.'); 175 | } 176 | 177 | /* Refreshing an Access Token */ 178 | protected function refresh_token(array $token) 179 | { 180 | // TODo 181 | } 182 | 183 | } // END Oauthz Client Controller 184 | -------------------------------------------------------------------------------- /classes/oauthz/extension/client/credentials.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Client_Credentials extends Oauthz_Extension { 16 | 17 | /** 18 | * client_id 19 | * REQUIRED. The client identifier 20 | */ 21 | public $client_id; 22 | 23 | /** 24 | * client_secret 25 | * REQUIRED. The client secret 26 | * OPTIONAL if no client secret was issued. 27 | */ 28 | public $client_secret; 29 | 30 | /** 31 | * redirect_uri 32 | * REQUIRED unless a redirection URI has been established between 33 | * the client and authorization server via other means. An 34 | * absolute URI to which the authorization server will redirect 35 | * the user-agent to when the end-user authorization step is 36 | * completed. The authorization server SHOULD require the client 37 | * to pre-register their redirection URI. Authorization servers 38 | * MAY restrict the redirection URI to not include a query 39 | * component as defined by [RFC3986] section 3. 40 | */ 41 | public $redirect_uri; 42 | 43 | /** 44 | * state 45 | * OPTIONAL. An opaque value used by the client to maintain state 46 | * between the request and callback. The authorization server 47 | * includes this value when redirecting the user-agent back to the 48 | * client. 49 | * 50 | * scope 51 | * OPTIONAL. The scope of the access request expressed as a list 52 | * of space-delimited strings. The value of the "scope" parameter 53 | * is defined by the authorization server. If the value contains 54 | * multiple space-delimited strings, their order does not matter, 55 | * and each string adds an additional access range to the 56 | * requested scope. 57 | * 58 | * @access public 59 | * @return void 60 | * @throw Oauthz_Exception_Token Error Codes: invalid_request 61 | */ 62 | public function __construct($args = NULL) 63 | { 64 | isset($_SERVER['CONTENT_TYPE']) OR $_SERVER['CONTENT_TYPE'] = getenv('CONTENT_TYPE'); 65 | 66 | // Parse the "state" paramter 67 | if(isset($_POST['state'])) 68 | { 69 | if($state = trim($_POST['state'])) 70 | $this->state['state'] = $state; 71 | 72 | unset($args['state']); 73 | } 74 | 75 | // oauth_token already send in authorization header or the encrypt Content-Type is not single-part 76 | if(empty($_POST) OR stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === FALSE) 77 | { 78 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 79 | } 80 | else 81 | { 82 | if(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])) 83 | { 84 | $_POST += array('username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']); 85 | } 86 | // TODO Digest HTTP authentication 87 | //else if( ! empty($_SERVER['PHP_AUTH_DIGEST']) AND $digest = parent::parse_digest($_SERVER['PHP_AUTH_DIGEST'])) 88 | //{ 89 | // $_POST += array('username' => $digest['username'], 'password' => $digest['']); 90 | //} 91 | 92 | // Check all required parameters from form-encoded body 93 | foreach($args as $key => $val) 94 | { 95 | if($val === TRUE) 96 | { 97 | if(isset($_POST[$key]) AND $value = trim($_POST[$key])) 98 | { 99 | $this->$key = $value; 100 | } 101 | else 102 | { 103 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * Populate the access token thu the request info and client info stored in the server 112 | * 113 | * @access public 114 | * @param array $client 115 | * @return Oauthz_Token 116 | * @throw Oauthz_Exception_Authorize Error Codes: invalid_request, invalid_scope 117 | */ 118 | public function execute() 119 | { 120 | if($client = Model_Oauthz::factory('Client')->lookup($this->client_id)) 121 | { 122 | // Verify the user information send by client 123 | } 124 | else 125 | { 126 | // Invalid client_id 127 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 128 | } 129 | 130 | if($client['redirect_uri'] !== $this->redirect_uri) 131 | { 132 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 133 | } 134 | 135 | if($client['client_secret'] !== sha1($this->client_secret)) 136 | { 137 | $params = $this->state; 138 | $params['error_uri'] = $this->redirect_uri; 139 | 140 | throw new Oauthz_Exception_Token('invalid_request', $params); 141 | } 142 | 143 | if(isset($this->scope) AND ! empty($client['scope'])) 144 | { 145 | if( ! in_array($this->scope, explode(' ', $client['scope']))) 146 | { 147 | $params = $this->state; 148 | $params['error_uri'] = $this->redirect_uri; 149 | 150 | throw new Oauthz_Exception_Token('invalid_scope', $params); 151 | } 152 | } 153 | 154 | if($client['expires_in'] < $_SERVER['REQUEST_TIME']) 155 | { 156 | throw new Oauthz_Exception_Access('invalid_grant', $this->state); 157 | } 158 | 159 | $token = new Oauthz_Token; 160 | 161 | // TODO: issue "mac" OAuth Access Token Type 162 | $token->token_type = $client['token_type']; 163 | $token->access_token = $client['access_token']; 164 | $token->refresh_token = $client['refresh_token']; 165 | $token->expires_in = $client['expires_in']; 166 | 167 | // merge other token properties, e.g. {"mac_key":"adijq39jdlaska9asud","mac_algorithm":"hmac-sha-256"} 168 | if($client['options'] AND $option = json_decode($client['options'], TRUE)) 169 | { 170 | foreach($option as $key => $val) 171 | { 172 | $token->$key = $val; 173 | } 174 | } 175 | 176 | isset($this->state['state']) AND $token->state = $this->state['state']; 177 | 178 | return $token; 179 | } 180 | 181 | } // END Oauthz_Extension_Client_Credentials 182 | -------------------------------------------------------------------------------- /classes/oauthz/extension/password.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Password extends Oauthz_Extension { 16 | 17 | /** 18 | * client_id 19 | * REQUIRED. The client identifier 20 | */ 21 | public $client_id; 22 | 23 | /** 24 | * client_secret 25 | * REQUIRED. The client secret 26 | * OPTIONAL if no client secret was issued. 27 | */ 28 | public $client_secret; 29 | 30 | /** 31 | * username 32 | * REQUIRED. The end-user's username. 33 | */ 34 | public $username; 35 | 36 | /** 37 | * password 38 | * REQUIRED. The end-user's password. 39 | */ 40 | public $password; 41 | 42 | /** 43 | * redirect_uri 44 | * REQUIRED unless a redirection URI has been established between 45 | * the client and authorization server via other means. An 46 | * absolute URI to which the authorization server will redirect 47 | * the user-agent to when the end-user authorization step is 48 | * completed. The authorization server SHOULD require the client 49 | * to pre-register their redirection URI. Authorization servers 50 | * MAY restrict the redirection URI to not include a query 51 | * component as defined by [RFC3986] section 3. 52 | */ 53 | public $redirect_uri; 54 | 55 | /** 56 | * state 57 | * OPTIONAL. An opaque value used by the client to maintain state 58 | * between the request and callback. The authorization server 59 | * includes this value when redirecting the user-agent back to the 60 | * client. 61 | * 62 | * scope 63 | * OPTIONAL. The scope of the access request expressed as a list 64 | * of space-delimited strings. The value of the "scope" parameter 65 | * is defined by the authorization server. If the value contains 66 | * multiple space-delimited strings, their order does not matter, 67 | * and each string adds an additional access range to the 68 | * requested scope. 69 | * 70 | * @access public 71 | * @return void 72 | * @throw Oauthz_Exception_Token Error Codes: invalid_request 73 | */ 74 | public function __construct($args = NULL) 75 | { 76 | isset($_SERVER['CONTENT_TYPE']) OR $_SERVER['CONTENT_TYPE'] = getenv('CONTENT_TYPE'); 77 | 78 | // Parse the "state" paramter 79 | if(isset($_POST['state'])) 80 | { 81 | if($state = rawurldecode($_POST['state'])) 82 | $this->state['state'] = $state; 83 | 84 | unset($args['state']); 85 | } 86 | 87 | // oauth_token already send in authorization header or the encrypt Content-Type is not single-part 88 | if(empty($_POST) OR stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === FALSE) 89 | { 90 | throw new Oauthz_Exception_Token('invalid_request'); 91 | } 92 | else 93 | { 94 | // TODO move this request data detect into authorization handler 95 | if(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])) 96 | { 97 | $_POST += array('username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']); 98 | } 99 | // TODO Digest HTTP authentication 100 | //else if( ! empty($_SERVER['PHP_AUTH_DIGEST']) AND $digest = parent::parse_digest($_SERVER['PHP_AUTH_DIGEST'])) 101 | //{ 102 | // $_POST += array('username' => $digest['username'], 'password' => $digest['']); 103 | //} 104 | 105 | // Check all required parameters from form-encoded body 106 | foreach($args as $key => $val) 107 | { 108 | if($val === TRUE) 109 | { 110 | if(isset($_POST[$key]) AND $value = rawurldecode($_POST[$key])) 111 | { 112 | $this->$key = $value; 113 | } 114 | else 115 | { 116 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Populate the access token thu the request info and client info stored in the server 125 | * 126 | * @access public 127 | * @param array $client 128 | * @return Oauthz_Token 129 | * @throw Oauthz_Exception_Token Error Codes: unauthorized_client, invalid_request, invalid_scope 130 | */ 131 | public function execute() 132 | { 133 | if($client = Model_Oauthz::factory('Client')->lookup($this->client_id)) 134 | { 135 | // Audit 136 | } 137 | else 138 | { 139 | // Invalid client_id 140 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 141 | } 142 | 143 | // TODO password should be hashed with much more stronger method 144 | if($client['client_secret'] !== sha1($this->client_secret) 145 | OR $client['username'] !== $this->username 146 | OR $client['password'] !== sha1($this->password) 147 | OR $client['redirect_uri'] !== $this->redirect_uri) 148 | { 149 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 150 | } 151 | 152 | if(isset($this->scope) AND ! empty($client['scope']) 153 | AND ! in_array($this->scope, explode(' ', $client['scope']))) 154 | { 155 | throw new Oauthz_Exception_Token('invalid_scope', $this->state); 156 | } 157 | 158 | if($client['expires_in'] < $_SERVER['REQUEST_TIME']) 159 | { 160 | $params = $this->state; 161 | $params['error_uri'] = $this->redirect_uri; 162 | 163 | throw new Oauthz_Exception_Access('invalid_grant', $params); 164 | } 165 | 166 | $token = new Oauthz_Token; 167 | 168 | // TODO: issue "mac" OAuth Access Token Type 169 | $token->token_type = $client['token_type']; 170 | $token->access_token = $client['access_token']; 171 | $token->refresh_token = $client['refresh_token']; 172 | 173 | // merge other token properties, e.g. {"mac_key":"adijq39jdlaska9asud","mac_algorithm":"hmac-sha-256"} 174 | if($client['options'] AND $option = json_decode($client['options'], TRUE)) 175 | { 176 | foreach($option as $key => $val) 177 | { 178 | $token->$key = $val; 179 | } 180 | } 181 | 182 | isset($this->state['state']) AND $token->state = $this->state['state']; 183 | 184 | return $token; 185 | } 186 | 187 | } // END Oauthz_Extension_Password 188 | -------------------------------------------------------------------------------- /doc/oauth-oracle.sql: -------------------------------------------------------------------------------- 1 | /** 2 | * OAuth database table schema for Oracle 3 | * 4 | * @author sumh <42424861@qq.com> 5 | * @package Oauth 6 | * @copyright (c) 2011 OALite 7 | * @license ISC License (ISCL) 8 | * @link http://oalite.com 9 | */ 10 | 11 | /******************** Add Table: t_oauth_authorizes ************************/ 12 | 13 | /* Build Table Structure */ 14 | CREATE TABLE t_oauth_authorizes 15 | ( 16 | user_id BIGINT NULL, 17 | client_id VARCHAR(127) NOT NULL, 18 | redirect_uri VARCHAR(511) NOT NULL, 19 | confirm_type TINYINT DEFAULT 0 NOT NULL, 20 | client_level TINYINT DEFAULT 0 NOT NULL, 21 | client_desc TEXT NULL, 22 | expires_in INTEGER NULL, 23 | scope VARCHAR(511) NULL, 24 | created INTEGER NOT NULL, 25 | modified INTEGER NULL, 26 | remark TEXT NULL 27 | ) DEFAULT CHARSET=utf8; 28 | 29 | /* Table Items: t_oauth_authorizes */ 30 | ALTER TABLE t_oauth_authorizes ADD CONSTRAINT pkt_oauth_authorizes 31 | PRIMARY KEY (user_id); 32 | 33 | /* Set Comments */ 34 | COMMENT ON COLUMN t_oauth_authorizes.confirm_type IS 'Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned'; 35 | COMMENT ON COLUMN t_oauth_authorizes.client_level IS 'diferent client levels have different max request times'; 36 | COMMENT ON COLUMN t_oauth_authorizes.expires_in IS 'date time'; 37 | COMMENT ON TABLE t_oauth_authorizes IS 'Store audit information from resource owner for the resource requester'; 38 | 39 | /******************** Add Table: t_oauth_clients ************************/ 40 | 41 | /* Build Table Structure */ 42 | CREATE TABLE t_oauth_clients 43 | ( 44 | server_id BIGINT NOT NULL, 45 | client_id VARCHAR(127) NOT NULL, 46 | client_secret VARCHAR(127) NOT NULL, 47 | redirect_uri VARCHAR(511) NOT NULL, 48 | scope VARCHAR(255) NULL, 49 | secret_type ENUM('plaintext','md5','rsa-sha1','hmac-sha1') DEFAULT 'plaintext' NOT NULL, 50 | ssh_key VARCHAR(511) NULL, 51 | app_name VARCHAR(127) NOT NULL, 52 | app_desc TEXT NULL, 53 | app_profile ENUM('webserver','native','useragent','autonomous') DEFAULT 'webserver' NOT NULL, 54 | app_purpose VARCHAR(511) NULL, 55 | user_id BIGINT NULL, 56 | user_level TINYINT DEFAULT 0 NOT NULL, 57 | enabled TINYINT DEFAULT 1 NOT NULL, 58 | created INTEGER NOT NULL, 59 | modified INTEGER NULL 60 | ) DEFAULT CHARSET=utf8; 61 | 62 | /* Table Items: t_oauth_clients */ 63 | ALTER TABLE t_oauth_clients ADD CONSTRAINT pkt_oauth_clients 64 | PRIMARY KEY (server_id); 65 | 66 | /* Set Comments */ 67 | COMMENT ON COLUMN t_oauth_clients.client_id IS 'AKA. API key'; 68 | COMMENT ON COLUMN t_oauth_clients.client_secret IS 'AKA. API secret'; 69 | COMMENT ON COLUMN t_oauth_clients.redirect_uri IS 'AKA. Callback URI'; 70 | COMMENT ON COLUMN t_oauth_clients.scope IS 'May be create, read, update or delete. so on so for'; 71 | COMMENT ON COLUMN t_oauth_clients.secret_type IS 'Secret signature encrypt type. e.g'; 72 | COMMENT ON COLUMN t_oauth_clients.ssh_key IS 'SSH public keys'; 73 | COMMENT ON COLUMN t_oauth_clients.app_name IS 'Application Name'; 74 | COMMENT ON COLUMN t_oauth_clients.app_desc IS 'Application Description, When users authenticate via your app, this is what they''ll see.'; 75 | COMMENT ON COLUMN t_oauth_clients.app_profile IS 'Application Profile: Web Server Application, Native Application, Browser Application, Autonomous clients'; 76 | COMMENT ON COLUMN t_oauth_clients.user_id IS 'Ref# from users table'; 77 | COMMENT ON COLUMN t_oauth_clients.user_level IS 'diferent client levels have different max request times'; 78 | COMMENT ON COLUMN t_oauth_clients.enabled IS '0: waiting for system administrator audit; 1: acceptable; 2: ban'; 79 | COMMENT ON COLUMN t_oauth_clients.created IS 'create datetime'; 80 | COMMENT ON COLUMN t_oauth_clients.modified IS 'modified datetime'; 81 | COMMENT ON TABLE t_oauth_clients IS 'Used for verification of incoming requests. '; 82 | 83 | /* Add Indexes for: t_oauth_clients */ 84 | CREATE UNIQUE INDEX idx_t_oauth_clients_client_id ON t_oauth_clients (client_id); 85 | 86 | /******************** Add Table: t_oauth_logs ************************/ 87 | 88 | /* Build Table Structure */ 89 | CREATE TABLE t_oauth_logs 90 | ( 91 | log_id BIGINT NOT NULL, 92 | client_id VARCHAR(127) NULL, 93 | token VARCHAR(63) NULL, 94 | user_id BIGINT NULL, 95 | received TEXT NOT NULL, 96 | sent TEXT NOT NULL, 97 | body TEXT NOT NULL, 98 | notes TEXT NOT NULL, 99 | "timestamp" TIMESTAMP NOT NULL, 100 | remote_ip BIGINT NOT NULL 101 | ) DEFAULT CHARSET=utf8; 102 | 103 | /* Table Items: t_oauth_logs */ 104 | ALTER TABLE t_oauth_logs ADD CONSTRAINT pkt_oauth_logs 105 | PRIMARY KEY (log_id); 106 | 107 | /* Set Comments */ 108 | COMMENT ON TABLE t_oauth_logs IS 'Log table to hold all OAuth request when you enabled logging '; 109 | 110 | /* Add Indexes for: t_oauth_logs */ 111 | CREATE INDEX idx_t_oauth_logs_client_id_log_id ON t_oauth_logs (client_id, log_id); 112 | 113 | /******************** Add Table: t_oauth_tokens ************************/ 114 | 115 | /* Build Table Structure */ 116 | CREATE TABLE t_oauth_tokens 117 | ( 118 | token_id BIGINT NOT NULL, 119 | client_id VARCHAR(127) NOT NULL, 120 | user_id BIGINT NOT NULL, 121 | code VARCHAR(127) NOT NULL, 122 | access_token VARCHAR(63) NULL, 123 | refresh_token VARCHAR(63) NULL, 124 | expire_code INTEGER DEFAULT 300 NOT NULL, 125 | expire_token INTEGER DEFAULT 0 NOT NULL, 126 | expire_refresh INTEGER DEFAULT 0 NOT NULL, 127 | "timestamp" INTEGER NOT NULL, 128 | token_type VARCHAR(127) NOT NULL, 129 | options TEXT NULL 130 | ) DEFAULT CHARSET=utf8; 131 | 132 | /* Table Items: t_oauth_tokens */ 133 | ALTER TABLE t_oauth_tokens ADD CONSTRAINT pkt_oauth_tokens 134 | PRIMARY KEY (token_id); 135 | 136 | /* Set Comments */ 137 | COMMENT ON COLUMN t_oauth_tokens.user_id IS 'Ref# from users table'; 138 | COMMENT ON COLUMN t_oauth_tokens.expire_code IS 'authorization code expires in this timestamp'; 139 | COMMENT ON COLUMN t_oauth_tokens.expire_token IS 'access token expires in this timestamp'; 140 | COMMENT ON COLUMN t_oauth_tokens.expire_refresh IS 'refresh token expires in this timestamp'; 141 | COMMENT ON COLUMN t_oauth_tokens."timestamp" IS 'authorization code request timestamp'; 142 | COMMENT ON COLUMN t_oauth_tokens.token_type IS 'bearer, mac, etc.'; 143 | COMMENT ON COLUMN t_oauth_tokens.options IS 'parameters for different token type extension in json format'; 144 | COMMENT ON TABLE t_oauth_tokens IS 'Table used to verify signed requests sent to a server by the consumer.When the verification is succesful then the associated user id is returned. '; 145 | 146 | /* Add Indexes for: t_oauth_tokens */ 147 | CREATE INDEX idx_t_oauth_tokens_access_token ON t_oauth_tokens (access_token); 148 | CREATE INDEX idx_t_oauth_tokens_client_id ON t_oauth_tokens (client_id); 149 | CREATE INDEX idx_t_oauth_tokens_code ON t_oauth_tokens (code); 150 | CREATE INDEX idx_t_oauth_tokens_refresh_token ON t_oauth_tokens (refresh_token); 151 | 152 | 153 | /************ Add Foreign Keys to Database ***************/ 154 | 155 | /************ Foreign Key: fk_t_oauth_tokens_t_oauth_clients ***************/ 156 | ALTER TABLE t_oauth_tokens ADD CONSTRAINT fk_t_oauth_tokens_t_oauth_clients 157 | FOREIGN KEY (client_id) REFERENCES t_oauth_clients (client_id) 158 | ON DELETE CASCADE; -------------------------------------------------------------------------------- /doc/oauth-postgresql.sql: -------------------------------------------------------------------------------- 1 | /** 2 | * OAuth database table schema for PostgreSql 3 | * 4 | * @author sumh <42424861@qq.com> 5 | * @package Oauth 6 | * @copyright (c) 2011 OALite 7 | * @license ISC License (ISCL) 8 | * @link http://oalite.com 9 | */ 10 | 11 | /******************** Add Table: "t_oauth_authorizes" ************************/ 12 | 13 | /* Build Table Structure */ 14 | CREATE TABLE "t_oauth_authorizes" 15 | ( 16 | user_id BIGINT NULL, 17 | client_id VARCHAR(127) NOT NULL, 18 | redirect_uri VARCHAR(511) NOT NULL, 19 | confirm_type SMALLINT DEFAULT 0 NOT NULL, 20 | client_level SMALLINT DEFAULT 0 NOT NULL, 21 | client_desc TEXT NULL, 22 | expires_in INTEGER NULL, 23 | scope VARCHAR(511) NULL, 24 | created INTEGER NOT NULL, 25 | modified INTEGER NULL, 26 | remark TEXT NULL 27 | ) DEFAULT CHARSET=utf8; 28 | 29 | /* Table Items: "t_oauth_authorizes" */ 30 | ALTER TABLE "t_oauth_authorizes" ADD CONSTRAINT pkt_oauth_authorizes 31 | PRIMARY KEY (user_id); 32 | 33 | /* Set Comments */ 34 | COMMENT ON COLUMN "t_oauth_authorizes".confirm_type IS 'Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned'; 35 | COMMENT ON COLUMN "t_oauth_authorizes".client_level IS 'diferent client levels have different max request times'; 36 | COMMENT ON COLUMN "t_oauth_authorizes".expires_in IS 'date time'; 37 | COMMENT ON TABLE "t_oauth_authorizes" IS 'Store audit information from resource owner for the resource requester'; 38 | 39 | /******************** Add Table: "t_oauth_clients" ************************/ 40 | CREATE SEQUENCE seq_t_oauth_clients_server_id INCREMENT BY 1; 41 | 42 | /* Build Table Structure */ 43 | CREATE TABLE "t_oauth_clients" 44 | ( 45 | server_id BIGINT NOT NULL DEFAULT nextval('seq_t_oauth_clients_server_id'), 46 | client_id VARCHAR(127) NOT NULL, 47 | client_secret VARCHAR(127) NOT NULL, 48 | redirect_uri VARCHAR(511) NOT NULL, 49 | scope VARCHAR(255) NULL, 50 | secret_type ENUM('plaintext','md5','rsa-sha1','hmac-sha1') DEFAULT 'plaintext' NOT NULL, 51 | ssh_key VARCHAR(511) NULL, 52 | app_name VARCHAR(127) NOT NULL, 53 | app_desc TEXT NULL, 54 | app_profile ENUM('webserver','native','useragent','autonomous') DEFAULT 'webserver' NOT NULL, 55 | app_purpose VARCHAR(511) NULL, 56 | user_id BIGINT NULL, 57 | user_level SMALLINT DEFAULT 0 NOT NULL, 58 | enabled SMALLINT DEFAULT 1 NOT NULL, 59 | created INTEGER NOT NULL, 60 | modified INTEGER NULL 61 | ) DEFAULT CHARSET=utf8; 62 | 63 | /* Table Items: "t_oauth_clients" */ 64 | ALTER TABLE "t_oauth_clients" ADD CONSTRAINT pkt_oauth_clients 65 | PRIMARY KEY (server_id); 66 | 67 | /* Set Comments */ 68 | COMMENT ON COLUMN "t_oauth_clients".client_id IS 'AKA. API key'; 69 | COMMENT ON COLUMN "t_oauth_clients".client_secret IS 'AKA. API secret'; 70 | COMMENT ON COLUMN "t_oauth_clients".redirect_uri IS 'AKA. Callback URI'; 71 | COMMENT ON COLUMN "t_oauth_clients".scope IS 'May be create, read, update or delete. so on so for'; 72 | COMMENT ON COLUMN "t_oauth_clients".secret_type IS 'Secret signature encrypt type. e.g'; 73 | COMMENT ON COLUMN "t_oauth_clients".ssh_key IS 'SSH public keys'; 74 | COMMENT ON COLUMN "t_oauth_clients".app_name IS 'Application Name'; 75 | COMMENT ON COLUMN "t_oauth_clients".app_desc IS 'Application Description, When users authenticate via your app, this is what they''ll see.'; 76 | COMMENT ON COLUMN "t_oauth_clients".app_profile IS 'Application Profile: Web Server Application, Native Application, Browser Application, Autonomous clients'; 77 | COMMENT ON COLUMN "t_oauth_clients".user_id IS 'Ref# from users table'; 78 | COMMENT ON COLUMN "t_oauth_clients".user_level IS 'diferent client levels have different max request times'; 79 | COMMENT ON COLUMN "t_oauth_clients".enabled IS '0: waiting for system administrator audit; 1: acceptable; 2: ban'; 80 | COMMENT ON COLUMN "t_oauth_clients".created IS 'create datetime'; 81 | COMMENT ON COLUMN "t_oauth_clients".modified IS 'modified datetime'; 82 | COMMENT ON TABLE "t_oauth_clients" IS 'Used for verification of incoming requests. '; 83 | 84 | /* Add Indexes for: t_oauth_clients */ 85 | CREATE UNIQUE INDEX idx_t_oauth_clients_client_id ON "t_oauth_clients" (client_id); 86 | 87 | /******************** Add Table: "t_oauth_logs" ************************/ 88 | CREATE SEQUENCE seq_t_oauth_logs_log_id INCREMENT BY 1; 89 | 90 | /* Build Table Structure */ 91 | CREATE TABLE "t_oauth_logs" 92 | ( 93 | log_id BIGINT NOT NULL DEFAULT nextval('seq_t_oauth_logs_log_id'), 94 | client_id VARCHAR(127) NULL, 95 | token VARCHAR(63) NULL, 96 | user_id BIGINT NULL, 97 | received TEXT NOT NULL, 98 | sent TEXT NOT NULL, 99 | body TEXT NOT NULL, 100 | notes TEXT NOT NULL, 101 | "timestamp" TIMESTAMP NOT NULL, 102 | remote_ip BIGINT NOT NULL 103 | ) DEFAULT CHARSET=utf8; 104 | 105 | /* Table Items: "t_oauth_logs" */ 106 | ALTER TABLE "t_oauth_logs" ADD CONSTRAINT pkt_oauth_logs 107 | PRIMARY KEY (log_id); 108 | 109 | /* Set Comments */ 110 | COMMENT ON TABLE "t_oauth_logs" IS 'Log table to hold all OAuth request when you enabled logging '; 111 | 112 | /* Add Indexes for: t_oauth_logs */ 113 | CREATE INDEX idx_t_oauth_logs_client_id_log_id ON "t_oauth_logs" (client_id, log_id); 114 | 115 | /******************** Add Table: "t_oauth_tokens" ************************/ 116 | CREATE SEQUENCE seq_t_oauth_tokens_token_id INCREMENT BY 1; 117 | 118 | /* Build Table Structure */ 119 | CREATE TABLE "t_oauth_tokens" 120 | ( 121 | token_id BIGINT NOT NULL DEFAULT nextval('seq_t_oauth_tokens_token_id'), 122 | client_id VARCHAR(127) NOT NULL, 123 | user_id BIGINT NOT NULL, 124 | code VARCHAR(127) NOT NULL, 125 | access_token VARCHAR(63) NULL, 126 | refresh_token VARCHAR(63) NULL, 127 | expire_code INTEGER DEFAULT 300 NOT NULL, 128 | expire_token INTEGER DEFAULT 0 NOT NULL, 129 | expire_refresh INTEGER DEFAULT 0 NOT NULL, 130 | "timestamp" INTEGER NOT NULL, 131 | token_type VARCHAR(127) NOT NULL, 132 | options TEXT NULL 133 | ) DEFAULT CHARSET=utf8; 134 | 135 | /* Table Items: "t_oauth_tokens" */ 136 | ALTER TABLE "t_oauth_tokens" ADD CONSTRAINT pkt_oauth_tokens 137 | PRIMARY KEY (token_id); 138 | 139 | /* Set Comments */ 140 | COMMENT ON COLUMN "t_oauth_tokens".user_id IS 'Ref# from users table'; 141 | COMMENT ON COLUMN "t_oauth_tokens".expire_code IS 'authorization code expires in this timestamp'; 142 | COMMENT ON COLUMN "t_oauth_tokens".expire_token IS 'access token expires in this timestamp'; 143 | COMMENT ON COLUMN "t_oauth_tokens".expire_refresh IS 'refresh token expires in this timestamp'; 144 | COMMENT ON COLUMN "t_oauth_tokens"."timestamp" IS 'authorization code request timestamp'; 145 | COMMENT ON COLUMN "t_oauth_tokens".token_type IS 'bearer, mac, etc.'; 146 | COMMENT ON COLUMN "t_oauth_tokens".options IS 'parameters for different token type extension in json format'; 147 | COMMENT ON TABLE "t_oauth_tokens" IS 'Table used to verify signed requests sent to a server by the consumer.When the verification is succesful then the associated user id is returned. '; 148 | 149 | /* Add Indexes for: t_oauth_tokens */ 150 | CREATE INDEX idx_t_oauth_tokens_access_token ON "t_oauth_tokens" (access_token); 151 | CREATE INDEX idx_t_oauth_tokens_client_id ON "t_oauth_tokens" (client_id); 152 | CREATE INDEX idx_t_oauth_tokens_code ON "t_oauth_tokens" (code); 153 | CREATE INDEX idx_t_oauth_tokens_refresh_token ON "t_oauth_tokens" (refresh_token); 154 | 155 | 156 | /************ Add Foreign Keys to Database ***************/ 157 | 158 | /************ Foreign Key: fk_t_oauth_tokens_t_oauth_clients ***************/ 159 | ALTER TABLE "t_oauth_tokens" ADD CONSTRAINT fk_t_oauth_tokens_t_oauth_clients 160 | FOREIGN KEY (client_id) REFERENCES "t_oauth_clients" (client_id) 161 | ON UPDATE CASCADE ON DELETE CASCADE; -------------------------------------------------------------------------------- /classes/model/oauthz/authorize.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Model_Oauthz 11 | * * 12 | */ 13 | class Model_Oauthz_Authorize extends Model_Oauthz { 14 | 15 | public function get($user_id) 16 | { 17 | return ctype_digit((string) $user_id) 18 | ? DB::select('user_id','client_id','redirect_uri','confirm_type','client_level','modified','created','scope','expired_date','remark','client_desc') 19 | ->from('t_oauth_authorizes') 20 | ->where('user_id', '=', $user_id) 21 | ->execute($this->_db) 22 | ->current() 23 | : NULL; 24 | } 25 | 26 | /** 27 | * Insert authorize 28 | * 29 | * @access public 30 | * @param array $params 31 | * confirm_type: Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned 32 | * client_level: diferent authorize levels have different max request times 33 | * expired_date: date time 34 | * 35 | * @return mix array(insert_id, affect_rows) or validate object 36 | */ 37 | public function append(array $params) 38 | { 39 | if(isset($params['expired_date']) AND $timetamp = strtotime($params['expired_date'])) 40 | $params['expired_date'] = $timetamp; 41 | else 42 | unset($params['expired_date']); 43 | 44 | $valid = Validate::factory($params) 45 | ->rule('client_id', 'not_empty') 46 | ->rule('redirect_uri', 'not_empty') 47 | ->rule('modified', 'not_empty'); 48 | 49 | $rules = array_intersect_key(array ( 50 | 'client_id' => array ('max_length' => array (128)), 51 | 'redirect_uri' => array ('max_length' => array (512)), 52 | 'confirm_type' => array ('range' => array (0,255)), 53 | 'client_level' => array ('range' => array (0,255)), 54 | 'scope' => array ('max_length' => array (512)), 55 | 'expired_date' => array ('range' => array (0,4294967295)), 56 | 'client_desc' => array ('max_length' => array (65535)), 57 | ), $params); 58 | 59 | foreach($rules as $field => $rule) 60 | foreach($rule as $r => $p) 61 | $valid->rule($field, $r, $p); 62 | 63 | if($valid->check()) 64 | { 65 | $valid = $valid->as_array(); 66 | 67 | foreach($valid as $key => $val) 68 | { 69 | if($val === '') $valid[$key] = NULL; 70 | } 71 | 72 | $valid['created'] = $_SERVER['REQUEST_TIME']; 73 | 74 | return DB::insert('t_oauth_authorizes', array_keys($valid)) 75 | ->values(array_values($valid)) 76 | ->execute($this->_db); 77 | } 78 | else 79 | { 80 | // Validation failed, collect the errors 81 | return $valid; 82 | } 83 | } 84 | 85 | /** 86 | * Update client 87 | * 88 | * @access public 89 | * @param int $user_id 90 | * @param array $params 91 | * confirm_type: Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned 92 | * client_level: diferent client levels have different max request times 93 | * expired_date: date time 94 | * 95 | * @return mix update rows affect or validate object 96 | */ 97 | public function update($user_id, array $params) 98 | { 99 | if(isset($params['expired_date']) AND $timetamp = strtotime($params['expired_date'])) 100 | $params['expired_date'] = $timetamp; 101 | else 102 | unset($params['expired_date']); 103 | 104 | $valid = Validate::factory($params); 105 | 106 | $rules = array_intersect_key(array ( 107 | 'client_id' => array ('not_empty' => NULL, 'max_length' => array (128)), 108 | 'redirect_uri' => array ('not_empty' => NULL, 'max_length' => array (512)), 109 | 'confirm_type' => array ('range' => array (0,255)), 110 | 'client_level' => array ('range' => array (0,255)), 111 | 'scope' => array ('max_length' => array (512)), 112 | 'expired_date' => array ('range' => array (0,4294967295)), 113 | 'client_desc' => array ('max_length' => array (65535)), 114 | ), $params); 115 | 116 | foreach($rules as $field => $rule) 117 | foreach($rule as $r => $p) 118 | $valid->rule($field, $r, $p); 119 | 120 | if($valid->check()) 121 | { 122 | $valid = $valid->as_array(); 123 | 124 | foreach($valid as $key => $val) 125 | { 126 | if($val === '') $valid[$key] = NULL; 127 | } 128 | 129 | $valid['modifed'] = $_SERVER['REQUEST_TIME']; 130 | 131 | return DB::update('t_oauth_authorizes') 132 | ->set($valid) 133 | ->where('user_id', '=', $user_id) 134 | ->execute($this->_db); 135 | } 136 | else 137 | { 138 | // Validation failed, collect the errors 139 | return $valid; 140 | } 141 | } 142 | 143 | public function delete($client_id, $user_id) 144 | { 145 | return ctype_digit((string) $user_id) 146 | ? DB::delete('t_oauth_authorizes') 147 | ->where('client_id', '=', $client_id) 148 | ->where('user_id', '=', $user_id) 149 | ->execute($this->_db) 150 | : NULL; 151 | } 152 | 153 | /** 154 | * List clients 155 | * 156 | * @access public 157 | * @param array $params 158 | * @param Pagination $pagination default [ NULL ] passed by reference 159 | * @param boolean $calc_total default [ TRUE ] is needed to caculate the total records for pagination 160 | * @return array array('clients' => data, 'orderby' => $params['orderby'], 'pagination' => $pagination) 161 | */ 162 | public function lists(array $params, $pagination = NULL, $calc_total = TRUE) 163 | { 164 | $pagination instanceOf Pagination OR $pagination = new Pagination; 165 | 166 | $sql = 'FROM `t_oauth_authorizes` '; 167 | 168 | // Customize where from params 169 | //$sql .= 'WHERE ... ' 170 | 171 | // caculte the total rows 172 | if($calc_total === TRUE) 173 | { 174 | $pagination->total_items = $this->_db->query( 175 | Database::SELECT, 'SELECT COUNT(`user_id`) num_rows '.$sql, FALSE 176 | )->get('num_rows'); 177 | 178 | $data['pagination'] = $pagination; 179 | 180 | if($pagination->total_items === 0) 181 | { 182 | $data['clients'] = array(); 183 | isset($params['orderby']) AND $data['orderby'] = $params['orderby']; 184 | return $data; 185 | } 186 | } 187 | 188 | // Customize order by from params 189 | if(isset($params['orderby'])) 190 | { 191 | switch($params['orderby']) 192 | { 193 | case 'level': 194 | $sql .= ' ORDER BY client_level DESC'; 195 | break; 196 | case 'expired': 197 | $sql .= ' ORDER BY expired_date DESC'; 198 | break; 199 | case 'update': 200 | $sql .= ' ORDER BY modified DESC'; 201 | break; 202 | default: 203 | $params['orderby'] = 'client'; 204 | $sql .= ' ORDER BY client_id DESC'; 205 | break; 206 | } 207 | $data['orderby'] = $params['orderby']; 208 | } 209 | 210 | $sql .= " LIMIT {$pagination->offset}, {$pagination->items_per_page}"; 211 | 212 | $data['clients'] = $this->_db->query(Database::SELECT, 'SELECT * '.$sql, FALSE); 213 | 214 | return $data; 215 | } 216 | 217 | } // END Model_Oauthz_Authorize 218 | -------------------------------------------------------------------------------- /doc/oauth-mssql.sql: -------------------------------------------------------------------------------- 1 | /** 2 | * OAuth database table schema for MSSQL 3 | * 4 | * @author sumh <42424861@qq.com> 5 | * @package Oauth 6 | * @copyright (c) 2011 OALite 7 | * @license ISC License (ISCL) 8 | * @link http://oalite.com 9 | */ 10 | 11 | /******************** Add Table: t_oauth_authorizes ************************/ 12 | 13 | /* Build Table Structure */ 14 | CREATE TABLE Server.t_oauth_authorizes 15 | ( 16 | user_id BIGINT NULL, 17 | client_id VARCHAR(127) NOT NULL, 18 | redirect_uri VARCHAR(511) NOT NULL, 19 | confirm_type TINYINT DEFAULT 0 NOT NULL, 20 | client_level TINYINT DEFAULT 0 NOT NULL, 21 | client_desc TEXT NULL, 22 | expires_in INTEGER NULL, 23 | scope VARCHAR(511) NULL, 24 | created INTEGER NOT NULL, 25 | modified INTEGER NULL, 26 | remark TEXT NULL 27 | ) DEFAULT CHARSET=utf8; 28 | 29 | /* Table Items: Server.t_oauth_authorizes */ 30 | ALTER TABLE Server.t_oauth_authorizes ADD CONSTRAINT pkt_oauth_authorizes 31 | PRIMARY KEY (user_id); 32 | 33 | /* Set Comments */ 34 | EXEC sp_addextendedproperty 'MS_Description', 'Request confirm, 0: every time; 1: only once; 2: with expired period; 3: once and banned', 35 | 'table', 't_oauth_authorizes', 'column', 'confirm_type'; 36 | EXEC sp_addextendedproperty 'MS_Description', 'diferent client levels have different max request times', 37 | 'table', 't_oauth_authorizes', 'column', 'client_level'; 38 | EXEC sp_addextendedproperty 'MS_Description', 'date time', 39 | 'table', 't_oauth_authorizes', 'column', 'expires_in'; 40 | EXEC sp_addextendedproperty 'MS_Description', 'Store audit information from resource owner for the resource requester', 41 | 'table', t_oauth_authorizes, null, null; 42 | 43 | /******************** Add Table: Server.t_oauth_clients ************************/ 44 | 45 | /* Build Table Structure */ 46 | CREATE TABLE Server.t_oauth_clients 47 | ( 48 | server_id BIGINT IDENTITY (1, 1) NOT NULL, 49 | client_id VARCHAR(127) NOT NULL, 50 | client_secret VARCHAR(127) NOT NULL, 51 | redirect_uri VARCHAR(511) NOT NULL, 52 | scope VARCHAR(255) NULL, 53 | secret_type ENUM('plaintext','md5','rsa-sha1','hmac-sha1') DEFAULT 'plaintext' NOT NULL, 54 | ssh_key VARCHAR(511) NULL, 55 | app_name VARCHAR(127) NOT NULL, 56 | app_desc TEXT NULL, 57 | app_profile ENUM('webserver','native','useragent','autonomous') DEFAULT 'webserver' NOT NULL, 58 | app_purpose VARCHAR(511) NULL, 59 | user_id BIGINT NULL, 60 | user_level TINYINT DEFAULT 0 NOT NULL, 61 | enabled TINYINT DEFAULT 1 NOT NULL, 62 | created INTEGER NOT NULL, 63 | modified INTEGER NULL 64 | ) DEFAULT CHARSET=utf8; 65 | 66 | /* Table Items: Server.t_oauth_clients */ 67 | ALTER TABLE Server.t_oauth_clients ADD CONSTRAINT pkt_oauth_clients 68 | PRIMARY KEY (server_id); 69 | 70 | /* Set Comments */ 71 | EXEC sp_addextendedproperty 'MS_Description', 'AKA. API key', 72 | 'table', 't_oauth_clients', 'column', 'client_id'; 73 | EXEC sp_addextendedproperty 'MS_Description', 'AKA. API secret', 74 | 'table', 't_oauth_clients', 'column', 'client_secret'; 75 | EXEC sp_addextendedproperty 'MS_Description', 'AKA. Callback URI', 76 | 'table', 't_oauth_clients', 'column', 'redirect_uri'; 77 | EXEC sp_addextendedproperty 'MS_Description', 'May be create, read, update or delete. so on so for', 78 | 'table', 't_oauth_clients', 'column', 'scope'; 79 | EXEC sp_addextendedproperty 'MS_Description', 'Secret signature encrypt type. e.g', 80 | 'table', 't_oauth_clients', 'column', 'secret_type'; 81 | EXEC sp_addextendedproperty 'MS_Description', 'SSH public keys', 82 | 'table', 't_oauth_clients', 'column', 'ssh_key'; 83 | EXEC sp_addextendedproperty 'MS_Description', 'Application Name', 84 | 'table', 't_oauth_clients', 'column', 'app_name'; 85 | EXEC sp_addextendedproperty 'MS_Description', 'Application Description, When users authenticate via your app, this is what they''ll see.', 86 | 'table', 't_oauth_clients', 'column', 'app_desc'; 87 | EXEC sp_addextendedproperty 'MS_Description', 'Application Profile: Web Server Application, Native Application, Browser Application, Autonomous clients', 88 | 'table', 't_oauth_clients', 'column', 'app_profile'; 89 | EXEC sp_addextendedproperty 'MS_Description', 'Ref# from users table', 90 | 'table', 't_oauth_clients', 'column', 'user_id'; 91 | EXEC sp_addextendedproperty 'MS_Description', 'diferent client levels have different max request times', 92 | 'table', 't_oauth_clients', 'column', 'user_level'; 93 | EXEC sp_addextendedproperty 'MS_Description', '0: waiting for system administrator audit; 1: acceptable; 2: ban', 94 | 'table', 't_oauth_clients', 'column', 'enabled'; 95 | EXEC sp_addextendedproperty 'MS_Description', 'create datetime', 96 | 'table', 't_oauth_clients', 'column', 'created'; 97 | EXEC sp_addextendedproperty 'MS_Description', 'modified datetime', 98 | 'table', 't_oauth_clients', 'column', 'modified'; 99 | EXEC sp_addextendedproperty 'MS_Description', 'Used for verification of incoming requests. ', 100 | 'table', t_oauth_clients, null, null; 101 | 102 | /* Add Indexes for: t_oauth_clients */ 103 | CREATE UNIQUE NONCLUSTERED INDEX idx_t_oauth_clients_client_id ON Server.t_oauth_clients (client_id); 104 | 105 | /******************** Add Table: Server.t_oauth_logs ************************/ 106 | 107 | /* Build Table Structure */ 108 | CREATE TABLE Server.t_oauth_logs 109 | ( 110 | log_id BIGINT IDENTITY (1, 1) NOT NULL, 111 | client_id VARCHAR(127) NULL, 112 | token VARCHAR(63) NULL, 113 | user_id BIGINT NULL, 114 | received TEXT NOT NULL, 115 | sent TEXT NOT NULL, 116 | body TEXT NOT NULL, 117 | notes TEXT NOT NULL, 118 | [timestamp] TIMESTAMP NOT NULL, 119 | remote_ip BIGINT NOT NULL 120 | ) DEFAULT CHARSET=utf8; 121 | 122 | /* Table Items: Server.t_oauth_logs */ 123 | ALTER TABLE Server.t_oauth_logs ADD CONSTRAINT pkt_oauth_logs 124 | PRIMARY KEY (log_id); 125 | 126 | /* Set Comments */ 127 | EXEC sp_addextendedproperty 'MS_Description', 'Log table to hold all OAuth request when you enabled logging ', 128 | 'table', t_oauth_logs, null, null; 129 | 130 | /* Add Indexes for: t_oauth_logs */ 131 | CREATE NONCLUSTERED INDEX idx_t_oauth_logs_client_id_log_id ON Server.t_oauth_logs (client_id, log_id); 132 | 133 | /******************** Add Table: Server.t_oauth_tokens ************************/ 134 | 135 | /* Build Table Structure */ 136 | CREATE TABLE Server.t_oauth_tokens 137 | ( 138 | token_id BIGINT IDENTITY (1, 1) NOT NULL, 139 | client_id VARCHAR(127) NOT NULL, 140 | user_id BIGINT NOT NULL, 141 | code VARCHAR(127) NOT NULL, 142 | access_token VARCHAR(63) NULL, 143 | refresh_token VARCHAR(63) NULL, 144 | expire_code INTEGER DEFAULT 300 NOT NULL, 145 | expire_token INTEGER DEFAULT 0 NOT NULL, 146 | expire_refresh INTEGER DEFAULT 0 NOT NULL, 147 | [timestamp] INTEGER NOT NULL, 148 | token_type VARCHAR(127) NOT NULL, 149 | options TEXT NULL 150 | ) DEFAULT CHARSET=utf8; 151 | 152 | /* Table Items: Server.t_oauth_tokens */ 153 | ALTER TABLE Server.t_oauth_tokens ADD CONSTRAINT pkt_oauth_tokens 154 | PRIMARY KEY (token_id); 155 | 156 | /* Set Comments */ 157 | EXEC sp_addextendedproperty 'MS_Description', 'Ref# from users table', 158 | 'table', 't_oauth_tokens', 'column', 'user_id'; 159 | EXEC sp_addextendedproperty 'MS_Description', 'authorization code expires in this timestamp', 160 | 'table', 't_oauth_tokens', 'column', 'expire_code'; 161 | EXEC sp_addextendedproperty 'MS_Description', 'access token expires in this timestamp', 162 | 'table', 't_oauth_tokens', 'column', 'expire_token'; 163 | EXEC sp_addextendedproperty 'MS_Description', 'refresh token expires in this timestamp', 164 | 'table', 't_oauth_tokens', 'column', 'expire_refresh'; 165 | EXEC sp_addextendedproperty 'MS_Description', 'authorization code request timestamp', 166 | 'table', 't_oauth_tokens', 'column', 'timestamp'; 167 | EXEC sp_addextendedproperty 'MS_Description', 'bearer, mac, etc.', 168 | 'table', 't_oauth_tokens', 'column', 'token_type'; 169 | EXEC sp_addextendedproperty 'MS_Description', 'parameters for different token type extension in json format', 170 | 'table', 't_oauth_tokens', 'column', 'options'; 171 | EXEC sp_addextendedproperty 'MS_Description', 'Table used to verify signed requests sent to a server by the consumer.When the verification is succesful then the associated user id is returned. ', 172 | 'table', t_oauth_tokens, null, null; 173 | 174 | /* Add Indexes for: t_oauth_tokens */ 175 | CREATE NONCLUSTERED INDEX idx_t_oauth_tokens_access_token ON Server.t_oauth_tokens (access_token); 176 | CREATE NONCLUSTERED INDEX idx_t_oauth_tokens_client_id ON Server.t_oauth_tokens (client_id); 177 | CREATE NONCLUSTERED INDEX idx_t_oauth_tokens_code ON Server.t_oauth_tokens (code); 178 | CREATE NONCLUSTERED INDEX idx_t_oauth_tokens_refresh_token ON Server.t_oauth_tokens (refresh_token); 179 | 180 | /************ Add Foreign Keys to Database ***************/ 181 | 182 | /************ Foreign Key: fk_t_oauth_tokens_t_oauth_clients ***************/ 183 | ALTER TABLE Server.t_oauth_tokens ADD CONSTRAINT fk_t_oauth_tokens_t_oauth_clients 184 | FOREIGN KEY (client_id) REFERENCES Server.t_oauth_clients (client_id) 185 | ON UPDATE CASCADE ON DELETE CASCADE; -------------------------------------------------------------------------------- /classes/model/oauthz/token.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Model_Oauthz 11 | * * 12 | */ 13 | class Model_Oauthz_Token extends Model_Oauthz { 14 | 15 | public function get($token_id) 16 | { 17 | return ctype_digit((string) $token_id) 18 | ? DB::select('*') 19 | ->from('t_oauth_tokens') 20 | ->where('token_id', '=', $token_id) 21 | ->execute($this->_db) 22 | ->current() 23 | : NULL; 24 | } 25 | 26 | /** 27 | * Insert token 28 | * 29 | * @access public 30 | * @param array $params 31 | * user_id: Ref# from users table 32 | * 33 | * @return array token 34 | */ 35 | public function code($client_id, $token_type, $expires_in = 120, array $options = NULL) 36 | { 37 | if($client_id AND $client = DB::select('client_secret','server_id','redirect_uri','user_id') 38 | ->from('t_oauth_clients') 39 | ->where('client_id' , '=', $client_id) 40 | ->where('enabled' , '=', 1) 41 | ->execute($this->_db) 42 | ->current()) 43 | { 44 | // Initial code, access_token, refresh_token at the same time 45 | $client['code'] = uniqid(); 46 | $client['expires_in'] = $_SERVER['REQUEST_TIME'] + $expires_in; 47 | 48 | isset($options) AND $options = json_encode($options); 49 | 50 | DB::insert('t_oauth_tokens', array( 51 | 'client_id', 52 | 'code', 53 | 'user_id', 54 | 'timestamp', 55 | 'expire_code', 56 | 'token_type', 57 | 'options' 58 | )) 59 | ->values(array( 60 | $client_id, 61 | $client['code'], 62 | 0, // TODO the user_id should be the resource owner 63 | $_SERVER['REQUEST_TIME'], 64 | $client['expires_in'], 65 | $token_type, 66 | $options 67 | )) 68 | ->execute($this->_db); 69 | 70 | $client['token_type'] = $token_type; 71 | 72 | return $client; 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | /** 79 | * Get the access_token and expire authorization_code 80 | * 81 | * @access public 82 | * @param string $client_id 83 | * @param string $code 84 | * @param int $expires_in default [ 3600 ] 85 | * @return mix 86 | */ 87 | public function token($client_id, $code, $expires_in = 3600) 88 | { 89 | if($client = DB::select('server_id','client_secret','redirect_uri','user_id') 90 | ->from('t_oauth_clients') 91 | ->where('client_id' , '=', $client_id) 92 | ->execute($this->_db) 93 | ->current()) 94 | { 95 | if($token = DB::select('token_id','access_token','token_type','refresh_token' 96 | ,array('expire_token', 'expires_in'),'options','expire_code') 97 | ->from('t_oauth_tokens') 98 | ->where('client_id' , '=', $client_id) 99 | ->where('code' , '=', $code) 100 | ->execute($this->_db) 101 | ->current()) 102 | { 103 | if($token['expire_code'] >= $_SERVER['REQUEST_TIME']) 104 | { 105 | // Start access_token expire time counter 106 | $token['expires_in'] = $_SERVER['REQUEST_TIME'] + $expires_in; 107 | 108 | // Generate access_token 109 | $token['access_token'] = sha1(md5($_SERVER['REQUEST_TIME'])); 110 | 111 | // Generate refresh_token 112 | $token['refresh_token'] = sha1(sha1(mt_rand())); 113 | 114 | // Update the expire timestamp of access_token to newest AND expire the code 115 | DB::update('t_oauth_tokens') 116 | ->set(array( 117 | 'expire_code' => 0, 118 | 'expire_token' => $token['expires_in'], 119 | 'access_token' => $token['access_token'], 120 | 'refresh_token' => $token['refresh_token'] 121 | )) 122 | ->where('token_id', '=', $token['token_id']) 123 | ->execute($this->_db); 124 | 125 | // Don't expose these 126 | unset($token['token_id'], $token['expire_code']); 127 | 128 | $client += $token; 129 | } 130 | elseif($token['expire_code'] == 0 AND $token['expires_in'] != 0) 131 | { 132 | // revoke all tokens previously issued based on that authorization code. 133 | DB::update('t_oauth_tokens') 134 | ->set(array( 135 | 'expire_code' => 0, 136 | 'expire_token' => 0, 137 | 'expire_refresh' => 0 138 | )) 139 | ->where('token_id', '=', $token['token_id']) 140 | ->execute($this->_db); 141 | } 142 | } 143 | } 144 | 145 | return isset($client['access_token']) ? $client : NULL; 146 | } 147 | 148 | public function access_token($token) 149 | { 150 | if($token = DB::select('access_token','token_type','refresh_token' 151 | ,array('expire_token', 'expires_in'),'options') 152 | ->from('t_oauth_tokens') 153 | ->where('access_token', '=', $token) 154 | ->execute($this->_db) 155 | ->current()) 156 | { 157 | // 158 | } 159 | 160 | return $token; 161 | } 162 | 163 | // TODO 164 | public function refresh_token($client_id, $token) 165 | { 166 | if($token = DB::select('access_token','token_type','refresh_token' 167 | ,array('expire_token', 'expires_in'),'options') 168 | ->from('t_oauth_tokens') 169 | ->where('client_id' , '=', $client_id) 170 | ->where('token' , '=', $token) 171 | ->execute($this->_db) 172 | ->current()) 173 | { 174 | // 175 | } 176 | 177 | return $token; 178 | } 179 | 180 | public function assertion($client_id) 181 | { 182 | // TODO 183 | } 184 | 185 | public function delete($token_id, $client_id) 186 | { 187 | return ctype_digit((string) $token_id) 188 | ? DB::delete('t_oauth_tokens') 189 | ->where('token_id', '=', $token_id) 190 | ->where('client_id', '=', $client_id) 191 | ->execute($this->_db) 192 | : NULL; 193 | } 194 | 195 | /** 196 | * List tokens 197 | * 198 | * @access public 199 | * @param array $params 200 | * @param Pagination $pagination default [ NULL ] passed by reference 201 | * @param boolean $calc_total default [ TRUE ] is needed to caculate the total records for pagination 202 | * @return array array('tokens' => data, 'orderby' => $params['orderby'], 'pagination' => $pagination) 203 | */ 204 | public function lists(array $params, $pagination = NULL, $calc_total = TRUE) 205 | { 206 | $pagination instanceOf Pagination OR $pagination = new Pagination; 207 | 208 | $sql = 'FROM `t_oauth_tokens` '; 209 | 210 | // Customize where from params 211 | //$sql .= 'WHERE ... ' 212 | 213 | // caculte the total rows 214 | if($calc_total === TRUE) 215 | { 216 | $pagination->total_items = $this->_db->query( 217 | Database::SELECT, 'SELECT COUNT(`token_id`) num_rows '.$sql, FALSE 218 | )->get('num_rows'); 219 | 220 | $data['pagination'] = $pagination; 221 | 222 | if($pagination->total_items === 0) 223 | { 224 | $data['tokens'] = array(); 225 | isset($params['orderby']) AND $data['orderby'] = $params['orderby']; 226 | return $data; 227 | } 228 | } 229 | 230 | // Customize order by from params 231 | if(isset($params['orderby'])) 232 | { 233 | switch($params['orderby']) 234 | { 235 | case 'client': 236 | $sql .= ' ORDER BY client_id DESC'; 237 | break; 238 | case 'user': 239 | $sql .= ' ORDER BY user_id DESC'; 240 | break; 241 | default: 242 | $params['orderby'] = 'timestamp'; 243 | $sql .= ' ORDER BY timestamp DESC'; 244 | break; 245 | } 246 | $data['orderby'] = $params['orderby']; 247 | } 248 | 249 | $sql .= " LIMIT {$pagination->offset}, {$pagination->items_per_page}"; 250 | 251 | $data['tokens'] = $this->_db->query(Database::SELECT, 'SELECT * '.$sql, FALSE); 252 | 253 | return $data; 254 | } 255 | 256 | } // END Model_Oauthz_Token 257 | -------------------------------------------------------------------------------- /classes/oauthz/extension/token.php: -------------------------------------------------------------------------------- 1 | 8 | * @package Oauthz 9 | * @copyright (c) 2010 OALite 10 | * @license ISC License (ISCL) 11 | * @link http://oalite.com 12 | * @see Oauthz_Extension 13 | * * 14 | */ 15 | class Oauthz_Extension_Token extends Oauthz_Extension { 16 | 17 | /** 18 | * REQUIRED 19 | * 20 | * @access public 21 | * @var string $oauth_token 22 | */ 23 | public $oauth_token; 24 | 25 | /** 26 | * Load request parameters from Authorization header, URI-Query parameters, Form-Encoded Body 27 | * 28 | * @access public 29 | * @param array $args 30 | * @return void 31 | * @throw Oauthz_Exception_Token Error code: invalid_request 32 | */ 33 | public function __construct(array $args) 34 | { 35 | $params = array(); 36 | /** 37 | * TODO move this request data detect into authorization handler 38 | * Load oauth token from authorization header 39 | */ 40 | if (isset($_SERVER['HTTP_AUTHORIZATION']) OR $_SERVER['HTTP_AUTHORIZATION'] = getenv('HTTP_AUTHORIZATION')) 41 | { 42 | $offset = 0; 43 | $pattern = '/([-_a-z]*)=(?:"([^"]+)"|([^\s,]+)|\'([^\']+)\')/'; 44 | if(preg_match_all($pattern, $_SERVER['HTTP_AUTHORIZATION'], $matches, PREG_SET_ORDER)) 45 | { 46 | foreach($matches as $match) 47 | { 48 | if($value = rawurldecode($match[2] ?: $match[3])) 49 | { 50 | $params[$match[1]] = $value; 51 | } 52 | } 53 | } 54 | // Replace the name of token to oauth_token 55 | if(isset($params['token'])) 56 | { 57 | // Parse the "state" paramter 58 | if(isset($params['state']) AND $state = rawurldecode($params['state'])) 59 | $this->state['state'] = $state; 60 | 61 | $params['oauth_token'] = $params['token']; 62 | unset($params['token'], $args['state']); 63 | 64 | // Check all required parameters should NOT be empty 65 | foreach($args as $key => $val) 66 | { 67 | if($val === TRUE) 68 | { 69 | if(empty($params[$key])) 70 | { 71 | throw new Oauthz_Exception_Authorize('invalid_request', $this->state); 72 | } 73 | else 74 | { 75 | $this->$key = $params[$key]; 76 | } 77 | } 78 | elseif($val !== FALSE) 79 | { 80 | $this->$key = $val; 81 | } 82 | } 83 | } 84 | 85 | $this->method = 'HEADER'; 86 | } 87 | 88 | /** 89 | * Load oauth_token from form-encoded body 90 | */ 91 | if(isset($_POST['oauth_token'])) 92 | { 93 | isset($_SERVER['CONTENT_TYPE']) OR $_SERVER['CONTENT_TYPE'] = getenv('CONTENT_TYPE'); 94 | 95 | // Parse the "state" paramter 96 | if(isset($_POST['state'])) 97 | { 98 | if($state = rawurldecode($_POST['state'])) 99 | $this->state['state'] = $state; 100 | 101 | unset($args['state']); 102 | } 103 | 104 | // oauth_token already send in authorization header or the encrypt Content-Type is not single-part 105 | if(isset($params['oauth_token']) OR stripos($_SERVER['CONTENT_TYPE'], 'application/x-www-form-urlencoded') === FALSE) 106 | { 107 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 108 | } 109 | else 110 | { 111 | // TODO move this request data detect into authorization handler 112 | if(isset($_SERVER['PHP_AUTH_USER']) AND isset($_SERVER['PHP_AUTH_PW'])) 113 | { 114 | $_POST += array('client_id' => $_SERVER['PHP_AUTH_USER'], 'client_secret' => $_SERVER['PHP_AUTH_PW']); 115 | } 116 | // TODO Digest HTTP authentication 117 | //else if( ! empty($_SERVER['PHP_AUTH_DIGEST']) AND $digest = parent::parse_digest($_SERVER['PHP_AUTH_DIGEST'])) 118 | //{ 119 | // $_POST += array('client_id' => $digest['username'], 'client_secret' => $digest['']); 120 | //} 121 | 122 | // Check all required parameters should NOT be empty 123 | foreach($args as $key => $val) 124 | { 125 | if($val === TRUE) 126 | { 127 | if(isset($_POST[$key]) AND $value = rawurldecode($_POST[$key])) 128 | { 129 | $this->$key = $value; 130 | } 131 | else 132 | { 133 | throw new Oauthz_Exception_Authorize('invalid_request', $this->state); 134 | } 135 | } 136 | elseif($val !== FALSE) 137 | { 138 | $this->$key = $val; 139 | } 140 | } 141 | } 142 | 143 | $this->method = 'POST'; 144 | } 145 | 146 | /** 147 | * Load oauth_token from uri-query component 148 | */ 149 | if(isset($_GET['oauth_token'])) 150 | { 151 | // Parse the "state" paramter 152 | if(isset($_GET['state'])) 153 | { 154 | if($state = rawurldecode($_GET['state'])) 155 | $this->state['state'] = $state; 156 | 157 | unset($args['state']); 158 | } 159 | 160 | // oauth_token already send in authorization header or form-encoded body 161 | if(isset($params['oauth_token'])) 162 | { 163 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 164 | } 165 | else 166 | { 167 | // Check all required parameters should NOT be empty 168 | foreach($args as $key => $val) 169 | { 170 | if($val === TRUE) 171 | { 172 | if(isset($_GET[$key]) AND $value = rawurldecode($_GET[$key])) 173 | { 174 | $this->$key = $value; 175 | } 176 | else 177 | { 178 | throw new Oauthz_Exception_Authorize('invalid_request', $this->state); 179 | } 180 | } 181 | elseif($val !== FALSE) 182 | { 183 | $this->$key = $val; 184 | } 185 | } 186 | } 187 | 188 | $this->method = 'GET'; 189 | } 190 | 191 | if(empty($params)) 192 | { 193 | throw new Oauthz_Exception_Token('invalid_request'); 194 | } 195 | 196 | $this->oauth_token = $params['oauth_token']; 197 | } 198 | 199 | /** 200 | * No need to authorization any more 201 | * 202 | * @access public 203 | * @param array $client 204 | * @return Oauthz_Token 205 | * @throw Oauthz_Exception_Token Error codes: invalid_scope, redirect_uri_mismatch 206 | */ 207 | public function execute() 208 | { 209 | // Verify the client and the code, load the access token if successes 210 | if($client = Model_Oauthz::factory('Token') 211 | ->token($this->client_id, $this->code, $this->expires_in)) 212 | { 213 | // Audit 214 | } 215 | else 216 | { 217 | // Invalid client_id 218 | throw new Oauthz_Exception_Token('unauthorized_client', $this->state); 219 | } 220 | 221 | if(isset($this->token_secret) AND $client['token_secret'] !== sha1($this->token_secret)) 222 | { 223 | throw new Oauthz_Exception_Token('invalid_request', $this->state); 224 | } 225 | 226 | if($client['expires_in'] < $_SERVER['REQUEST_TIME']) 227 | { 228 | throw new Oauthz_Exception_Access('invalid_grant', $this->state); 229 | } 230 | 231 | $token = new Oauthz_Token; 232 | 233 | // TODO: issue "mac" OAuth Access Token Type 234 | $token->token_type = $client['token_type']; 235 | $token->access_token = $client['access_token']; 236 | $token->refresh_token = $client['refresh_token']; 237 | $token->expires_in = $client['expires_in']; 238 | 239 | // merge other token properties, e.g. {"mac_key":"adijq39jdlaska9asud","mac_algorithm":"hmac-sha-256"} 240 | if($client['options'] AND $option = json_decode($client['options'], TRUE)) 241 | { 242 | foreach($option as $key => $val) 243 | { 244 | $token->$key = $val; 245 | } 246 | } 247 | 248 | isset($this->state['state']) AND $token->state = $this->state['state']; 249 | 250 | return $this->redirect_uri.'#'.$token->as_query(); 251 | } 252 | 253 | /** 254 | * MUST verify that the verification code, client identity, client secret, 255 | * and redirection URI are all valid and match its stored association. 256 | * 257 | * @access public 258 | * @param array $client 259 | * @return Oauthz_Token 260 | * @throw Oauthz_Exception_Token Error codes: invalid_request, unauthorized_client 261 | * @todo impletement timestamp, nonce, signature checking 262 | */ 263 | public function access_token($client) 264 | { 265 | if($client['access_token'] !== $this->oauth_token) 266 | { 267 | throw new Oauthz_Exception_Access('unauthorized_client', $this->state); 268 | } 269 | 270 | if($client['token_type'] !== $this->token_type) 271 | { 272 | throw new Oauthz_Exception_Access('unauthorized_client', $this->state); 273 | } 274 | 275 | if(isset($this->scope) AND ! empty($client['scope'])) 276 | { 277 | if( ! in_array($this->scope, explode(' ', $client['scope']))) 278 | throw new Oauthz_Exception_Access('invalid_scope', $this->state); 279 | } 280 | 281 | $token = new Oauthz_Token; 282 | 283 | if(isset($this->format)) 284 | { 285 | $token->format = $this->format; 286 | } 287 | 288 | //switch($token->token_type) 289 | //{ 290 | 291 | //} 292 | 293 | return $token; 294 | } 295 | 296 | } // END Oauthz_Extension_Token 297 | -------------------------------------------------------------------------------- /classes/model/oauthz/client.php: -------------------------------------------------------------------------------- 1 | 6 | * @package Oauthz 7 | * @copyright (c) 2010 OALite 8 | * @license ISC License (ISCL) 9 | * @link http://oalite.com 10 | * @see Model_Oauthz 11 | * * 12 | */ 13 | class Model_Oauthz_Client extends Model_Oauthz { 14 | 15 | public function get($server_id, $user_id) 16 | { 17 | return DB::select('server_id','client_id','client_secret','redirect_uri','scope','secret_type','ssh_key','app_name','app_desc','app_profile','app_purpose','user_id','user_level','enabled','created','modified') 18 | ->from('t_oauth_clients') 19 | ->where('server_id', '=', $server_id) 20 | ->where('user_id','=', $user_id) 21 | ->execute($this->_db) 22 | ->current(); 23 | } 24 | 25 | public function lookup($client_id) 26 | { 27 | return DB::select('*') 28 | ->from('t_oauth_clients') 29 | ->where('client_id', '=', $client_id) 30 | ->execute($this->_db) 31 | ->current(); 32 | } 33 | 34 | /** 35 | * Insert server 36 | * 37 | * @access public 38 | * @param array $params 39 | * client_id: AKA. API key 40 | * client_secret: AKA. API secret 41 | * redirect_uri: AKA. Callback URI 42 | * scope: May be create, read, update or delete. so on so for 43 | * secret_type: Secret signature encrypt type. e.g 44 | * ssh_key: SSH public keys 45 | * app_name: Application Name 46 | * app_desc: Application Description, When users authenticate via your app, this is what they'll see. 47 | * app_profile: Application Profile: Web Client Application, Native Application, Browser Application, Autonomous clients 48 | * user_id: Ref# from users table 49 | * user_level: diferent client levels have different max request times 50 | * enabled: 0: waiting for system administrator audit; 1: acceptable; 2: ban 51 | * created: create datetime 52 | * modified: modified datetime 53 | * 54 | * @return mix array(insert_id, affect_rows) or validate object 55 | */ 56 | public function append(array $params, $prefix = 'OAL@') 57 | { 58 | $params['client_id'] = $prefix.strtoupper(uniqid()); 59 | 60 | $valid = Validate::factory($params) 61 | ->filter(TRUE, 'trim') 62 | ->rule('client_secret', 'not_empty') 63 | ->rule('redirect_uri', 'not_empty') 64 | ->rule('app_name', 'not_empty'); 65 | 66 | $rules = array_intersect_key(array ( 67 | 'client_id' => array ('max_length' => array (128)), 68 | 'client_secret' => array ('max_length' => array (128)), 69 | 'redirect_uri' => array ('max_length' => array (512)), 70 | 'scope' => array ('max_length' => array (256)), 71 | 'secret_type' => array ('in_array' => array (array ('plaintext','md5','rsa-sha1','hmac-sha1')),), 72 | 'ssh_key' => array ('max_length' => array (512)), 73 | 'app_name' => array ('max_length' => array (128)), 74 | 'app_desc' => array ('max_length' => array (65535)), 75 | 'app_profile' => array ('in_array' => array (array ('webserver','native','useragent','autonomous')),), 76 | 'app_purpose' => array ('max_length' => array (512)), 77 | 'user_level' => array ('range' => array (0,255)), 78 | 'enabled' => array ('range' => array (0,255)), 79 | 'modified' => array ('range' => array (0,2147483647)), 80 | ), $params); 81 | 82 | foreach($rules as $field => $rule) 83 | foreach($rule as $r => $p) 84 | $valid->rule($field, $r, $p); 85 | 86 | // TODO: redirect_uri MUST NOT include a fragment component. 87 | // The client MUST NOT include any untrusted third-party scripts in the redirection endpoint 88 | // response (e.g. third-party analytics, social plug-ins, ad networks) 89 | // The client SHOULD NOT include any third-party scripts in the redirection endpoint response. 90 | 91 | if($valid->check()) 92 | { 93 | $valid = $valid->as_array(); 94 | 95 | if($this->unique_client($valid['redirect_uri'], $params['user_id'])) 96 | { 97 | return $valid->error('redirect_uri', 'not unique'); 98 | } 99 | 100 | foreach($valid as $key => $val) 101 | { 102 | if($val === '') $valid[$key] = NULL; 103 | } 104 | 105 | $valid['user_id'] = $params['user_id']; 106 | $valid['created'] = $_SERVER['REQUEST_TIME']; 107 | $valid['client_secret'] = sha1($params['client_secret']); 108 | 109 | $query = DB::insert('t_oauth_clients', array_keys($valid)) 110 | ->values(array_values($valid)) 111 | ->execute($this->_db); 112 | 113 | $valid['server_id'] = $query[0]; 114 | $valid['affected_rows'] = $query[1]; 115 | 116 | $valid += $params; 117 | } 118 | 119 | // Validation data, or collection of the errors 120 | return $valid; 121 | } 122 | 123 | /** 124 | * Update server 125 | * 126 | * @access public 127 | * @param int $server_id 128 | * @param array $params 129 | * client_id: AKA. API key 130 | * client_secret: AKA. API secret 131 | * redirect_uri: AKA. Callback URI 132 | * scope: May be create, read, update or delete. so on so for 133 | * secret_type: Secret signature encrypt type. e.g 134 | * ssh_key: SSH public keys 135 | * app_name: Application Name 136 | * app_desc: Application Description, When users authenticate via your app, this is what they'll see. 137 | * app_profile: Application Profile: Web Client Application, Native Application, Browser Application, Autonomous clients 138 | * user_id: Ref# from users table 139 | * user_level: diferent client levels have different max request times 140 | * enabled: 0: waiting for system administrator audit; 1: acceptable; 2: ban 141 | * created: create datetime 142 | * modified: modified datetime 143 | * 144 | * @return mix update rows affect or validate object 145 | */ 146 | public function update($server_id, array $params) 147 | { 148 | 149 | if(empty($params['client_secret'])) unset($params['client_secret']); 150 | 151 | $valid = Validate::factory($params) 152 | ->filter(TRUE, 'trim'); 153 | 154 | $rules = array_intersect_key(array ( 155 | 'client_id' => array ('not_empty' => NULL, 'max_length' => array (128)), 156 | 'client_secret' => array ('max_length' => array (128)), 157 | 'redirect_uri' => array ('not_empty' => NULL, 'max_length' => array (512)), 158 | 'scope' => array ('max_length' => array (256)), 159 | 'secret_type' => array ('in_array' => array (array ('plaintext','md5','rsa-sha1','hmac-sha1')),), 160 | 'ssh_key' => array ('max_length' => array (512)), 161 | 'app_name' => array ('not_empty' => NULL, 'max_length' => array (128)), 162 | 'app_desc' => array ('max_length' => array (65535)), 163 | 'app_profile' => array ('in_array' => array (array ('webserver','native','useragent','autonomous')),), 164 | 'app_purpose' => array ('max_length' => array (512)), 165 | 'user_level' => array ('range' => array (0,255)), 166 | 'enabled' => array ('range' => array (0,255)), 167 | 'created' => array ('not_empty' => NULL, 'range' => array (0,4294967295)), 168 | ), $params); 169 | 170 | foreach($rules as $field => $rule) 171 | foreach($rule as $r => $p) 172 | $valid->rule($field, $r, $p); 173 | 174 | if($valid->check()) 175 | { 176 | $valid = $valid->as_array(); 177 | 178 | if($this->unique_client($valid['redirect_uri'], $params['user_id']) === 1) 179 | { 180 | unset($valid['redirect_uri']); 181 | } 182 | 183 | foreach($valid as $key => $val) 184 | { 185 | if($val === '') $valid[$key] = NULL; 186 | } 187 | 188 | $valid['modified'] = $_SERVER['REQUEST_TIME']; 189 | $valid['affected_rows'] = DB::update('t_oauth_clients') 190 | ->set($valid) 191 | ->where('server_id', '=', $server_id) 192 | ->where('user_id', '=', $params['user_id']) 193 | ->execute($this->_db); 194 | 195 | $valid += $params; 196 | } 197 | 198 | // Validation data, or collection of the errors 199 | return $valid; 200 | } 201 | 202 | public function delete($server_id, $user_id) 203 | { 204 | return ctype_digit((string) $server_id) 205 | ? DB::delete('t_oauth_clients') 206 | ->where('server_id', '=', $server_id) 207 | ->where('user_id','=', $user_id) 208 | ->execute($this->_db) 209 | : NULL; 210 | } 211 | 212 | /** 213 | * List servers 214 | * 215 | * @access public 216 | * @param array $params 217 | * @param Pagination $pagination default [ NULL ] passed by reference 218 | * @param boolean $calc_total default [ TRUE ] is needed to caculate the total records for pagination 219 | * @return array array('servers' => data, 'orderby' => $params['orderby'], 'pagination' => $pagination) 220 | */ 221 | public function lists(array $params, $pagination = NULL, $calc_total = TRUE) 222 | { 223 | $pagination instanceOf Pagination OR $pagination = new Pagination; 224 | 225 | $sql = 'FROM `t_oauth_clients` '; 226 | 227 | // Customize where from params 228 | $sql .= 'WHERE user_id='.$this->_db->quote($params['user_id']); 229 | 230 | // caculte the total rows 231 | if($calc_total === TRUE) 232 | { 233 | $pagination->total_items = $this->_db->query( 234 | Database::SELECT, 'SELECT COUNT(`server_id`) num_rows '.$sql, FALSE 235 | )->get('num_rows'); 236 | 237 | $data['pagination'] = $pagination; 238 | 239 | if($pagination->total_items === 0) 240 | { 241 | $data['servers'] = array(); 242 | isset($params['orderby']) AND $data['orderby'] = $params['orderby']; 243 | return $data; 244 | } 245 | } 246 | 247 | // Customize order by from params 248 | if(isset($params['orderby'])) 249 | { 250 | switch($params['orderby']) 251 | { 252 | case 'uri': 253 | $sql .= ' ORDER BY redirect_uri DESC'; 254 | break; 255 | case 'level': 256 | $sql .= ' ORDER BY user_level DESC'; 257 | break; 258 | case 'appname': 259 | $sql .= ' ORDER BY app_name DESC'; 260 | break; 261 | default: 262 | $params['orderby'] = 'client_id'; 263 | $sql .= ' ORDER BY client_id DESC'; 264 | break; 265 | } 266 | $data['orderby'] = $params['orderby']; 267 | } 268 | 269 | $sql .= " LIMIT {$pagination->offset}, {$pagination->items_per_page}"; 270 | 271 | $data['servers'] = $this->_db->query(Database::SELECT, 'SELECT * '.$sql, FALSE); 272 | 273 | return $data; 274 | } 275 | 276 | protected function unique_client($redirect_uri, $user_id) 277 | { 278 | // Check if the username already exists in the database 279 | return DB::select(array(DB::expr('COUNT(1)'), 'total')) 280 | ->from('t_oauth_clients') 281 | ->where('redirect_uri', '=', $redirect_uri) 282 | ->where('user_id', '=', $user_id) 283 | ->execute($this->_db) 284 | ->get('total'); 285 | } 286 | 287 | } // END Model_Oauthz_Client 288 | --------------------------------------------------------------------------------