├── assets ├── css │ ├── admin.css │ ├── sweetalert.css │ └── sweetalert2.min.css ├── images │ ├── cer.png │ ├── logo.png │ └── openid-config-json.png └── js │ ├── admin.js │ └── sweetalert2.min.js ├── includes ├── actions.php ├── admin-options.php ├── admin │ └── page-server-status.php ├── ajax │ └── class-wo-ajax.php ├── filters.php └── functions.php ├── library ├── OAuth2 │ ├── Autoloader.php │ ├── ClientAssertionType │ │ ├── ClientAssertionTypeInterface.php │ │ └── HttpBasic.php │ ├── Controller │ │ ├── AuthorizeController.php │ │ ├── AuthorizeControllerInterface.php │ │ ├── ResourceController.php │ │ ├── ResourceControllerInterface.php │ │ ├── TokenController.php │ │ └── TokenControllerInterface.php │ ├── Encryption │ │ ├── EncryptionInterface.php │ │ ├── FirebaseJwt.php │ │ └── Jwt.php │ ├── GrantType │ │ ├── AuthorizationCode.php │ │ ├── ClientCredentials.php │ │ ├── GrantTypeInterface.php │ │ ├── JwtBearer.php │ │ ├── RefreshToken.php │ │ └── UserCredentials.php │ ├── OpenID │ │ ├── Controller │ │ │ ├── AuthorizeController.php │ │ │ ├── AuthorizeControllerInterface.php │ │ │ ├── UserInfoController.php │ │ │ └── UserInfoControllerInterface.php │ │ ├── GrantType │ │ │ └── AuthorizationCode.php │ │ ├── ResponseType │ │ │ ├── AuthorizationCode.php │ │ │ ├── AuthorizationCodeInterface.php │ │ │ ├── CodeIdToken.php │ │ │ ├── CodeIdTokenInterface.php │ │ │ ├── IdToken.php │ │ │ ├── IdTokenInterface.php │ │ │ ├── IdTokenToken.php │ │ │ ├── IdTokenTokenInterface.php │ │ │ ├── TokenIdToken.php │ │ │ └── TokenIdTokenInterface.php │ │ └── Storage │ │ │ ├── AuthorizationCodeInterface.php │ │ │ └── UserClaimsInterface.php │ ├── Request.php │ ├── RequestInterface.php │ ├── Response.php │ ├── ResponseInterface.php │ ├── ResponseType │ │ ├── AccessToken.php │ │ ├── AccessTokenInterface.php │ │ ├── AuthorizationCode.php │ │ ├── AuthorizationCodeInterface.php │ │ ├── CryptoToken.php │ │ ├── JwtAccessToken.php │ │ └── ResponseTypeInterface.php │ ├── Scope.php │ ├── ScopeInterface.php │ ├── Server.php │ ├── Storage │ │ ├── AccessTokenInterface.php │ │ ├── AuthorizationCodeInterface.php │ │ ├── Cassandra.php │ │ ├── ClientCredentialsInterface.php │ │ ├── ClientInterface.php │ │ ├── CouchbaseDB.php │ │ ├── CryptoToken.php │ │ ├── CryptoTokenInterface.php │ │ ├── DynamoDB.php │ │ ├── JwtAccessToken.php │ │ ├── JwtAccessTokenInterface.php │ │ ├── JwtBearerInterface.php │ │ ├── Memory.php │ │ ├── Mongo.php │ │ ├── Pdo.php │ │ ├── PublicKeyInterface.php │ │ ├── Redis.php │ │ ├── RefreshTokenInterface.php │ │ ├── ScopeInterface.php │ │ ├── UserCredentialsInterface.php │ │ └── Wordpressdb.php │ └── TokenType │ │ ├── Bearer.php │ │ ├── Mac.php │ │ └── TokenTypeInterface.php ├── authentication │ ├── Client.php │ ├── GrantType │ │ ├── AuthorizationCode.php │ │ ├── ClientCredentials.php │ │ ├── IGrantType.php │ │ ├── Password.php │ │ └── RefreshToken.php │ ├── StandardAuthentication.php │ └── index.php ├── class-wo-api.php ├── class-wo-table.php ├── content │ ├── create-new-client.php │ └── edit-client.php ├── keys │ ├── .htaccess │ ├── index.php │ ├── private_key.pem │ └── public_key.pem └── updater.php ├── readme.txt ├── wp-oauth-main.php └── wp-oauth.php /assets/css/admin.css: -------------------------------------------------------------------------------- 1 | @import url('//ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/cupertino/jquery-ui.css'); 2 | 3 | .ui-corner-all { 4 | border-radius: 0px; 5 | } 6 | 7 | .ui-widget-content { 8 | border: none; 9 | background: none; 10 | } 11 | 12 | .ui-widget-header { 13 | border: none; 14 | background: none; 15 | } 16 | 17 | .ui-state-default, 18 | .ui-widget-content .ui-state-default, 19 | .ui-widget-header .ui-state-default { 20 | border: 1px #eee solid; 21 | background: #444; 22 | color: #000; 23 | border-radius: 0px; 24 | padding: .3em 1em; 25 | opacity: 0.5; 26 | } 27 | 28 | .ui-state-default a, 29 | .ui-state-default a:link, 30 | .ui.state-default a:visited { 31 | color: #ccc !important; 32 | background: none; 33 | border: none; 34 | outline: none; 35 | } 36 | 37 | 38 | .ui-state-active, 39 | .ui-widget-content .ui-state-active, 40 | .ui-widget-header .ui-state-active { 41 | border: 1px #eee solid; 42 | background: #FFF; 43 | color: #000 !important; 44 | border-radius: 0px; 45 | opacity: 1; 46 | } 47 | 48 | .ui-state-active a, 49 | .ui-state-active a:link, 50 | .ui-state-active a:visited { 51 | color: #000 !important; 52 | border: none; 53 | outline: none; 54 | } 55 | 56 | .ui-tabs .ui-tabs-nav { 57 | padding-left: 0; 58 | } 59 | 60 | .ui-widget-content { 61 | } 62 | 63 | .ui-tabs-nav, 64 | .ui-tabs-nav li:first-of-type { 65 | padding-left: 0; 66 | } 67 | 68 | .ui-tabs .ui-tabs-panel { 69 | border-top-width: 1px; 70 | border-top-color: rgb(239, 239, 239); 71 | border-top-style: solid; 72 | background: rgb(255, 255, 255); 73 | } 74 | 75 | .ui-tabs-nav .ui-state-focus a { 76 | outline: none; 77 | } 78 | 79 | a:focus, 80 | a:active { 81 | outline: none !important; 82 | outline: 0 !important; 83 | } 84 | 85 | .settings_page_wo_settings .form-table { 86 | background: #fafafa; 87 | border-left: #0074a2 solid 5px; 88 | } 89 | 90 | .settings_page_wo_settings .form-table th { 91 | padding-left: 20px; 92 | } 93 | 94 | .ui-tabs-panel h3 { 95 | margin-top: 35px; 96 | color: #0074a2; 97 | } 98 | 99 | .notification { 100 | border-left: 4px solid #dd3d36; 101 | background: #fff; 102 | -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 103 | box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 104 | padding: 10px 12px; 105 | margin: 5px 0 15px; 106 | } 107 | 108 | .wo-popup-inner {} 109 | .wo-popup-inner h3.header { 110 | color: #0074a2; 111 | } 112 | .wo-popup-inner form { 113 | } 114 | .wo-popup-inner label { 115 | font-weight: bold; 116 | margin-top: 15px; 117 | display: block; 118 | font-size: 14px; 119 | } 120 | .wo-popup-inner input[type="text"] { 121 | display: block; 122 | padding: 10px; 123 | margin: 6px 0px; 124 | width: 100%; 125 | } 126 | 127 | .wo-popup-inner textarea { 128 | margin-top: 10px; 129 | width: 100%; 130 | resize: none; 131 | min-height: 150px; 132 | box-sizing: border-box; 133 | padding: 10px; 134 | } 135 | 136 | .wo-popup-inner input[type="submit"]{ 137 | margin-top: 20px; 138 | } 139 | 140 | #TB_title { 141 | background: #0074a2 !important; 142 | color: #FFF !important; 143 | } 144 | .tb-close-icon { 145 | color: #FFF !important; 146 | } 147 | 148 | .name.column-name a {color: #0074a2;} 149 | 150 | #the-list tr:nth-child(odd) { 151 | background: #f9f9f9; 152 | } 153 | 154 | .wo-badge { 155 | background: #0074a2 url(../images/logo.png) no-repeat center 24px; 156 | -webkit-background-size: 85px 85px; 157 | background-size: 85px 85px; 158 | color: #EDEDED; 159 | font-size: 14px; 160 | text-align: center; 161 | font-weight: 300; 162 | margin: 5px 0 0; 163 | padding-top: 120px; 164 | height: 40px; 165 | display: inline-block; 166 | width: 150px; 167 | text-rendering: optimizeLegibility; 168 | -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.2); 169 | box-shadow: 0 1px 3px rgba(0,0,0,.2); 170 | position: absolute; 171 | top: 0; 172 | right: 0; 173 | } 174 | 175 | .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { 176 | font-weight: 300; 177 | } 178 | 179 | .wo-oauth-server { 180 | background: #000; 181 | } -------------------------------------------------------------------------------- /assets/images/cer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wlcdesigns/WP-OAuth2-Server-Client/42f74340f99ccd0169a9e5dd9feaf27c1cdba202/assets/images/cer.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wlcdesigns/WP-OAuth2-Server-Client/42f74340f99ccd0169a9e5dd9feaf27c1cdba202/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/openid-config-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wlcdesigns/WP-OAuth2-Server-Client/42f74340f99ccd0169a9e5dd9feaf27c1cdba202/assets/images/openid-config-json.png -------------------------------------------------------------------------------- /assets/js/admin.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | $("#wpoauth-add-new-client").click(function(){ 4 | var site_url = $(this).data("site_url"); 5 | 6 | swal({ 7 | title: 'Create Client', 8 | html: 9 | 'Create a new client using the form below: ' + 10 | ' ' + 11 | ' ' + 12 | '', 13 | showCloseButton: false, 14 | showCancelButton: true, 15 | showLoaderOnConfirm: true, 16 | confirmButtonText: 'Save', 17 | cancelButtonText: 'Cancel', 18 | preConfirm: function(e) { 19 | return new Promise(function(resolve, reject) { 20 | //reject('You need to write something!'); 21 | if(document.getElementById('client-name').value.length === 0){ 22 | reject('You need to add a client name!'); 23 | }else if(document.getElementById('redirect-uri').value.length === 0){ 24 | reject('You need to add a redirect uri!'); 25 | }else{ 26 | console.log("go"); 27 | } 28 | 29 | 30 | 31 | if (true) { 32 | /* 33 | resolve([ 34 | document.getElementById('client-name').value, 35 | document.getElementById('redirect-uri').value 36 | ]); 37 | */ 38 | } 39 | }); 40 | }, 41 | allowOutsideClick: false 42 | }).then(function(result) { 43 | console.log(result); 44 | 45 | swal({ 46 | type: 'success', 47 | title: 'Ajax request finished!', 48 | html: 'Submitted email: ' 49 | }); 50 | 51 | }); 52 | }); 53 | 54 | /** intiate jQuery tabs */ 55 | $("#wo_tabs").tabs({ 56 | beforeActivate: function (event, ui) { 57 | var scrollTop = $(window).scrollTop(); 58 | window.location.hash = ui.newPanel.selector; 59 | $(window).scrollTop(scrollTop); 60 | } 61 | }); 62 | 63 | /** 64 | * Create New Client Form Submission Hook 65 | * @param {[type]} e [description] 66 | * @return {[type]} [description] 67 | */ 68 | $('#create-new-client').submit(function(e){ 69 | e.preventDefault(); 70 | var formData = $(this).serialize(); 71 | var data = { 72 | 'action': 'wo_create_new_client', 73 | 'data': formData 74 | }; 75 | jQuery.post(ajaxurl, data, function(response) { 76 | if(response != '1') 77 | { 78 | alert(response); 79 | } 80 | else 81 | { 82 | /** reload for the time being */ 83 | location.reload(); 84 | } 85 | }); 86 | }); 87 | 88 | 89 | })(jQuery); 90 | 91 | /** 92 | * Remove a Client 93 | */ 94 | function wo_remove_client (client_id) 95 | { 96 | if(!confirm("Are you sure you want to delete this client?")) 97 | return; 98 | 99 | var data = { 100 | 'action': 'wo_remove_client', 101 | 'data': client_id 102 | }; 103 | jQuery.post(ajaxurl, data, function(response) { 104 | if(response != '1') 105 | { 106 | alert(response); 107 | } 108 | else 109 | { 110 | jQuery("#record_"+client_id+"").remove(); 111 | } 112 | }); 113 | } 114 | 115 | /** 116 | * Update a Client 117 | * @param {[type]} form [description] 118 | * @return {[type]} [description] 119 | */ 120 | function wo_update_client(form){ 121 | alert('Submit the form'); 122 | } -------------------------------------------------------------------------------- /includes/actions.php: -------------------------------------------------------------------------------- 1 | 6 | * @package WordPress OAuth Server 7 | */ 8 | 9 | /** 10 | * Invalidate any token and refresh tokens during password reset 11 | * @param object $user WP_User Object 12 | * @param String $new_pass New Password 13 | * @return Void 14 | * 15 | * @since 3.1.8 16 | */ 17 | function wo_password_reset_action( $user, $new_pass ) { 18 | global $wpdb; 19 | $wpdb->delete( "{$wpdb->prefix}oauth_access_tokens", array( "user_id" => $user->ID ) ); 20 | $wpdb->delete( "{$wpdb->prefix}oauth_refresh_tokens", array( "user_id" => $user->ID ) ); 21 | } 22 | add_action( 'password_reset', 'wo_password_reset_action', 10, 2 ); 23 | 24 | /** 25 | * [wo_profile_update_action description] 26 | * @param int $user_id WP User ID 27 | * @return Void 28 | */ 29 | function wo_profile_update_action( $user_id ) { 30 | if ( ! isset( $_POST['pass1'] ) || '' == $_POST['pass1'] ) { 31 | return; 32 | } 33 | global $wpdb; 34 | $wpdb->delete( "{$wpdb->prefix}oauth_access_tokens", array( "user_id" => $user_id ) ); 35 | $wpdb->delete( "{$wpdb->prefix}oauth_refresh_tokens", array( "user_id" => $user_id ) ); 36 | } 37 | add_action( 'profile_update', 'wo_profile_update_action' ); -------------------------------------------------------------------------------- /includes/admin/page-server-status.php: -------------------------------------------------------------------------------- 1 | 7 |
10 | The following information is helpful when debugging or reporting an issue. Please note that the 11 | information provided here is a reference only. 12 |
13 |Plugin Build: | 16 |17 | version, '-') ? _WO()->version . " You are using a development version of the plugin." : _WO()->version;?> 18 | | 19 |
---|---|
PHP Version (): | 23 |24 | = 0 ? " OK" : " Failed - Please upgrade PHP to 5.4 or greater.";?> 25 | | 26 |
Apache Version: | 30 |31 | apache_get_version() not enabled.'; ?> 32 | | 33 |
Running CGI: | 37 |38 | OK" : " Notice - Header 'Authorization Basic' may not work as expected.";?> 39 | | 40 |
Certificates Generated: | 44 |45 | No Certificates Found" : "Certificates Found"?> 46 | | 47 |
License: | 51 |52 | Standard" : "Licensed"?> 53 | | 54 |
25 | * $config = array(
26 | * 'allow_credentials_in_request_body' => true, // whether to look for credentials in the POST body in addition to the Authorize HTTP Header
27 | * 'allow_public_clients' => true // if true, "public clients" (clients without a secret) may be authenticated
28 | * );
29 | *
30 | */
31 | public function __construct(ClientCredentialsInterface $storage, array $config = array())
32 | {
33 | $this->storage = $storage;
34 | $this->config = array_merge(array(
35 | 'allow_credentials_in_request_body' => true,
36 | 'allow_public_clients' => true,
37 | ), $config);
38 | }
39 |
40 | public function validateRequest(RequestInterface $request, ResponseInterface $response)
41 | {
42 | if (!$clientData = $this->getClientCredentials($request, $response)) {
43 | return false;
44 | }
45 |
46 | if (!isset($clientData['client_id'])) {
47 | throw new \LogicException('the clientData array must have "client_id" set');
48 | }
49 |
50 | if (!isset($clientData['client_secret']) || $clientData['client_secret'] == '') {
51 | if (!$this->config['allow_public_clients']) {
52 | $response->setError(400, 'invalid_client', 'client credentials are required');
53 |
54 | return false;
55 | }
56 |
57 | if (!$this->storage->isPublicClient($clientData['client_id'])) {
58 | $response->setError(400, 'invalid_client', 'This client is invalid or must authenticate using a client secret');
59 |
60 | return false;
61 | }
62 | } elseif ($this->storage->checkClientCredentials($clientData['client_id'], $clientData['client_secret']) === false) {
63 | $response->setError(400, 'invalid_client', 'The client credentials are invalid');
64 |
65 | return false;
66 | }
67 |
68 | $this->clientData = $clientData;
69 |
70 | return true;
71 | }
72 |
73 | public function getClientId()
74 | {
75 | return $this->clientData['client_id'];
76 | }
77 |
78 | /**
79 | * Internal function used to get the client credentials from HTTP basic
80 | * auth or POST data.
81 | *
82 | * According to the spec (draft 20), the client_id can be provided in
83 | * the Basic Authorization header (recommended) or via GET/POST.
84 | *
85 | * @return
86 | * A list containing the client identifier and password, for example
87 | * @code
88 | * return array(
89 | * "client_id" => CLIENT_ID, // REQUIRED the client id
90 | * "client_secret" => CLIENT_SECRET, // OPTIONAL the client secret (may be omitted for public clients)
91 | * );
92 | * @endcode
93 | *
94 | * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
95 | *
96 | * @ingroup oauth2_section_2
97 | */
98 | public function getClientCredentials(RequestInterface $request, ResponseInterface $response = null)
99 | {
100 | if (!is_null($request->headers('PHP_AUTH_USER')) && !is_null($request->headers('PHP_AUTH_PW'))) {
101 | return array('client_id' => $request->headers('PHP_AUTH_USER'), 'client_secret' => $request->headers('PHP_AUTH_PW'));
102 | }
103 |
104 | if ($this->config['allow_credentials_in_request_body']) {
105 | // Using POST for HttpBasic authorization is not recommended, but is supported by specification
106 | if (!is_null($request->request('client_id'))) {
107 | /**
108 | * client_secret can be null if the client's password is an empty string
109 | * @see http://tools.ietf.org/html/rfc6749#section-2.3.1
110 | */
111 |
112 | return array('client_id' => $request->request('client_id'), 'client_secret' => $request->request('client_secret'));
113 | }
114 | }
115 |
116 | if ($response) {
117 | $message = $this->config['allow_credentials_in_request_body'] ? ' or body' : '';
118 | $response->setError(400, 'invalid_client', 'Client credentials were not found in the headers'.$message);
119 | }
120 |
121 | return null;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/library/OAuth2/Controller/AuthorizeControllerInterface.php:
--------------------------------------------------------------------------------
1 | $user_id = $this->somehowDetermineUserId();
16 | * > $is_authorized = $this->somehowDetermineUserAuthorization();
17 | * > $response = new OAuth2\Response();
18 | * > $authorizeController->handleAuthorizeRequest(
19 | * > OAuth2\Request::createFromGlobals(),
20 | * > $response,
21 | * > $is_authorized,
22 | * > $user_id);
23 | * > $response->send();
24 | *
25 | */
26 | interface AuthorizeControllerInterface
27 | {
28 | /**
29 | * List of possible authentication response types.
30 | * The "authorization_code" mechanism exclusively supports 'code'
31 | * and the "implicit" mechanism exclusively supports 'token'.
32 | *
33 | * @var string
34 | * @see http://tools.ietf.org/html/rfc6749#section-4.1.1
35 | * @see http://tools.ietf.org/html/rfc6749#section-4.2.1
36 | */
37 | const RESPONSE_TYPE_AUTHORIZATION_CODE = 'code';
38 | const RESPONSE_TYPE_ACCESS_TOKEN = 'token';
39 |
40 | public function handleAuthorizeRequest(RequestInterface $request, ResponseInterface $response, $is_authorized, $user_id = null);
41 |
42 | public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response);
43 | }
44 |
--------------------------------------------------------------------------------
/library/OAuth2/Controller/ResourceController.php:
--------------------------------------------------------------------------------
1 | tokenType = $tokenType;
27 | $this->tokenStorage = $tokenStorage;
28 |
29 | $this->config = array_merge(array(
30 | 'www_realm' => 'Service',
31 | ), $config);
32 |
33 | if (is_null($scopeUtil)) {
34 | $scopeUtil = new Scope();
35 | }
36 | $this->scopeUtil = $scopeUtil;
37 | }
38 |
39 | public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response, $scope = null)
40 | {
41 | $token = $this->getAccessTokenData($request, $response);
42 |
43 | // Check if we have token data
44 | if (is_null($token)) {
45 | return false;
46 | }
47 |
48 | /**
49 | * Check scope, if provided
50 | * If token doesn't have a scope, it's null/empty, or it's insufficient, then throw 403
51 | * @see http://tools.ietf.org/html/rfc6750#section-3.1
52 | */
53 | if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->scopeUtil->checkScope($scope, $token["scope"]))) {
54 | $response->setError(403, 'insufficient_scope', 'The request requires higher privileges than provided by the access token');
55 | $response->addHttpHeaders(array(
56 | 'WWW-Authenticate' => sprintf('%s realm="%s", scope="%s", error="%s", error_description="%s"',
57 | $this->tokenType->getTokenType(),
58 | $this->config['www_realm'],
59 | $scope,
60 | $response->getParameter('error'),
61 | $response->getParameter('error_description')
62 | )
63 | ));
64 |
65 | return false;
66 | }
67 |
68 | // allow retrieval of the token
69 | $this->token = $token;
70 |
71 | return (bool) $token;
72 | }
73 |
74 | public function getAccessTokenData(RequestInterface $request, ResponseInterface $response) {
75 | // Get the token parameter
76 | if ($token_param = $this->tokenType->getAccessTokenParameter($request, $response)) {
77 | // Get the stored token data (from the implementing subclass)
78 | // Check we have a well formed token
79 | // Check token expiration (expires is a mandatory paramter)
80 | if (!$token = $this->tokenStorage->getAccessToken($token_param)) {
81 |
82 | $response->setError(401, 'invalid_token', 'The access token provided is invalid');
83 | } elseif (!isset($token["expires"]) || !isset($token["client_id"])) {
84 |
85 | $response->setError(401, 'malformed_token', 'Malformed token (missing "expires")');
86 | } elseif (time() > $token["expires"]) {
87 | $response->setError(401, 'expired_token', 'The access token provided has expired');
88 | } else {
89 | return $token;
90 | }
91 | }
92 |
93 | $authHeader = sprintf('%s realm="%s"', $this->tokenType->getTokenType(), $this->config['www_realm']);
94 |
95 | if ($error = $response->getParameter('error')) {
96 | $authHeader = sprintf('%s, error="%s"', $authHeader, $error);
97 | if ($error_description = $response->getParameter('error_description')) {
98 | $authHeader = sprintf('%s, error_description="%s"', $authHeader, $error_description);
99 | }
100 | }
101 |
102 | $response->addHttpHeaders(array('WWW-Authenticate' => $authHeader));
103 |
104 | return null;
105 | }
106 |
107 | // convenience method to allow retrieval of the token
108 | public function getToken()
109 | {
110 | return $this->token;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/library/OAuth2/Controller/ResourceControllerInterface.php:
--------------------------------------------------------------------------------
1 | if (!$resourceController->verifyResourceRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response())) {
15 | * > $response->send(); // authorization failed
16 | * > die();
17 | * > }
18 | * > return json_encode($resource); // valid token! Send the stuff!
19 | *
20 | */
21 | interface ResourceControllerInterface
22 | {
23 | public function verifyResourceRequest(RequestInterface $request, ResponseInterface $response, $scope = null);
24 |
25 | public function getAccessTokenData(RequestInterface $request, ResponseInterface $response);
26 | }
27 |
--------------------------------------------------------------------------------
/library/OAuth2/Controller/TokenControllerInterface.php:
--------------------------------------------------------------------------------
1 | $tokenController->handleTokenRequest(OAuth2\Request::createFromGlobals(), $response = new OAuth2\Response());
15 | * > $response->send();
16 | *
17 | */
18 | interface TokenControllerInterface
19 | {
20 | /**
21 | * handleTokenRequest
22 | *
23 | * @param $request
24 | * OAuth2\RequestInterface - The current http request
25 | * @param $response
26 | * OAuth2\ResponseInterface - An instance of OAuth2\ResponseInterface to contain the response data
27 | *
28 | */
29 | public function handleTokenRequest(RequestInterface $request, ResponseInterface $response);
30 |
31 | public function grantAccessToken(RequestInterface $request, ResponseInterface $response);
32 | }
33 |
--------------------------------------------------------------------------------
/library/OAuth2/Encryption/EncryptionInterface.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | class FirebaseJwt implements EncryptionInterface
10 | {
11 | public function __construct()
12 | {
13 | if (!class_exists('\JWT')) {
14 | throw new \ErrorException('firebase/php-jwt must be installed to use this feature. You can do this by running "composer require firebase/php-jwt"');
15 | }
16 | }
17 |
18 | public function encode($payload, $key, $alg = 'HS256', $keyId = null)
19 | {
20 | return \JWT::encode($payload, $key, $alg, $keyId);
21 | }
22 |
23 | public function decode($jwt, $key = null, $allowedAlgorithms = null)
24 | {
25 | try {
26 |
27 | //Maintain BC: Do not verify if no algorithms are passed in.
28 | if (!$allowedAlgorithms) {
29 | $key = null;
30 | }
31 |
32 | return (array)\JWT::decode($jwt, $key, $allowedAlgorithms);
33 | } catch (\Exception $e) {
34 | return false;
35 | }
36 | }
37 |
38 | public function urlSafeB64Encode($data)
39 | {
40 | return \JWT::urlsafeB64Encode($data);
41 | }
42 |
43 | public function urlSafeB64Decode($b64)
44 | {
45 | return \JWT::urlsafeB64Decode($b64);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/library/OAuth2/Encryption/Jwt.php:
--------------------------------------------------------------------------------
1 | generateJwtHeader($payload, $algo);
14 |
15 | $segments = array(
16 | $this->urlSafeB64Encode(json_encode($header)),
17 | $this->urlSafeB64Encode(json_encode($payload))
18 | );
19 |
20 | $signing_input = implode('.', $segments);
21 |
22 | $signature = $this->sign($signing_input, $key, $algo);
23 | $segments[] = $this->urlsafeB64Encode($signature);
24 |
25 | return implode('.', $segments);
26 | }
27 |
28 | public function decode($jwt, $key = null, $allowedAlgorithms = true)
29 | {
30 | if (!strpos($jwt, '.')) {
31 | return false;
32 | }
33 |
34 | $tks = explode('.', $jwt);
35 |
36 | if (count($tks) != 3) {
37 | return false;
38 | }
39 |
40 | list($headb64, $payloadb64, $cryptob64) = $tks;
41 |
42 | if (null === ($header = json_decode($this->urlSafeB64Decode($headb64), true))) {
43 | return false;
44 | }
45 |
46 | if (null === $payload = json_decode($this->urlSafeB64Decode($payloadb64), true)) {
47 | return false;
48 | }
49 |
50 | $sig = $this->urlSafeB64Decode($cryptob64);
51 |
52 | if ((bool) $allowedAlgorithms) {
53 | if (!isset($header['alg'])) {
54 | return false;
55 | }
56 |
57 | // check if bool arg supplied here to maintain BC
58 | if (is_array($allowedAlgorithms) && !in_array($header['alg'], $allowedAlgorithms)) {
59 | return false;
60 | }
61 |
62 | if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) {
63 | return false;
64 | }
65 | }
66 |
67 | return $payload;
68 | }
69 |
70 | private function verifySignature($signature, $input, $key, $algo = 'HS256')
71 | {
72 | // use constants when possible, for HipHop support
73 | switch ($algo) {
74 | case'HS256':
75 | case'HS384':
76 | case'HS512':
77 | return $this->hash_equals(
78 | $this->sign($input, $key, $algo),
79 | $signature
80 | );
81 |
82 | case 'RS256':
83 | return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256') === 1;
84 |
85 | case 'RS384':
86 | return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384') === 1;
87 |
88 | case 'RS512':
89 | return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512') === 1;
90 |
91 | default:
92 | throw new \InvalidArgumentException("Unsupported or invalid signing algorithm.");
93 | }
94 | }
95 |
96 | private function sign($input, $key, $algo = 'HS256')
97 | {
98 | switch ($algo) {
99 | case 'HS256':
100 | return hash_hmac('sha256', $input, $key, true);
101 |
102 | case 'HS384':
103 | return hash_hmac('sha384', $input, $key, true);
104 |
105 | case 'HS512':
106 | return hash_hmac('sha512', $input, $key, true);
107 |
108 | case 'RS256':
109 | return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256');
110 |
111 | case 'RS384':
112 | return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384');
113 |
114 | case 'RS512':
115 | return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512');
116 |
117 | default:
118 | throw new \Exception("Unsupported or invalid signing algorithm.");
119 | }
120 | }
121 |
122 | private function generateRSASignature($input, $key, $algo)
123 | {
124 | if (!openssl_sign($input, $signature, $key, $algo)) {
125 | throw new \Exception("Unable to sign data.");
126 | }
127 |
128 | return $signature;
129 | }
130 |
131 | public function urlSafeB64Encode($data)
132 | {
133 | $b64 = base64_encode($data);
134 | $b64 = str_replace(array('+', '/', "\r", "\n", '='),
135 | array('-', '_'),
136 | $b64);
137 |
138 | return $b64;
139 | }
140 |
141 | public function urlSafeB64Decode($b64)
142 | {
143 | $b64 = str_replace(array('-', '_'),
144 | array('+', '/'),
145 | $b64);
146 |
147 | return base64_decode($b64);
148 | }
149 |
150 | /**
151 | * Override to create a custom header
152 | */
153 | protected function generateJwtHeader($payload, $algorithm)
154 | {
155 | return array(
156 | 'typ' => 'JWT',
157 | 'alg' => $algorithm,
158 | );
159 | }
160 |
161 | protected function hash_equals($a, $b)
162 | {
163 | if (function_exists('hash_equals')) {
164 | return hash_equals($a, $b);
165 | }
166 | $diff = strlen($a) ^ strlen($b);
167 | for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
168 | $diff |= ord($a[$i]) ^ ord($b[$i]);
169 | }
170 |
171 | return $diff === 0;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/library/OAuth2/GrantType/AuthorizationCode.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class AuthorizationCode implements GrantTypeInterface
15 | {
16 | protected $storage;
17 | protected $authCode;
18 |
19 | /**
20 | * @param OAuth2\Storage\AuthorizationCodeInterface $storage REQUIRED Storage class for retrieving authorization code information
21 | */
22 | public function __construct(AuthorizationCodeInterface $storage)
23 | {
24 | $this->storage = $storage;
25 | }
26 |
27 | public function getQuerystringIdentifier()
28 | {
29 | return 'authorization_code';
30 | }
31 |
32 | public function validateRequest(RequestInterface $request, ResponseInterface $response)
33 | {
34 | if (!$request->request('code')) {
35 | $response->setError(400, 'invalid_request', 'Missing parameter: "code" is required');
36 |
37 | return false;
38 | }
39 |
40 | $code = $request->request('code');
41 | if (!$authCode = $this->storage->getAuthorizationCode($code)) {
42 | $response->setError(400, 'invalid_grant', 'Authorization code doesn\'t exist or is invalid for the client');
43 |
44 | return false;
45 | }
46 |
47 | // Remove id_token if not wanted
48 | $scopes = explode(' ', trim($authCode['scope']));
49 | if( !in_array('openid', $scopes ) ) {
50 | unset( $authCode['id_token'] );
51 | }
52 |
53 | /*
54 | * 4.1.3 - ensure that the "redirect_uri" parameter is present if the "redirect_uri" parameter was included in the initial authorization request
55 | * @uri - http://tools.ietf.org/html/rfc6749#section-4.1.3
56 | */
57 | if (isset($authCode['redirect_uri']) && $authCode['redirect_uri']) {
58 | if (!$request->request('redirect_uri') || urldecode($request->request('redirect_uri')) != $authCode['redirect_uri']) {
59 | $response->setError(400, 'redirect_uri_mismatch', "The redirect URI is missing or do not match", "#section-4.1.3");
60 |
61 | return false;
62 | }
63 | }
64 |
65 | if (!isset($authCode['expires'])) {
66 | throw new \Exception('Storage must return authcode with a value for "expires"');
67 | }
68 |
69 | if ($authCode["expires"] < time()) {
70 | $response->setError(400, 'invalid_grant', "The authorization code has expired");
71 |
72 | return false;
73 | }
74 |
75 | if (!isset($authCode['code'])) {
76 | $authCode['code'] = $code; // used to expire the code after the access token is granted
77 | }
78 |
79 | $this->authCode = $authCode;
80 |
81 | return true;
82 | }
83 |
84 | public function getClientId()
85 | {
86 | return $this->authCode['client_id'];
87 | }
88 |
89 | public function getScope()
90 | {
91 | return isset($this->authCode['scope']) ? $this->authCode['scope'] : null;
92 | }
93 |
94 | public function getUserId()
95 | {
96 | return isset($this->authCode['user_id']) ? $this->authCode['user_id'] : null;
97 | }
98 |
99 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
100 | {
101 | $token = $accessToken->createAccessToken($client_id, $user_id, $scope);
102 | $this->storage->expireAuthorizationCode($this->authCode['code']);
103 |
104 | return $token;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/library/OAuth2/GrantType/ClientCredentials.php:
--------------------------------------------------------------------------------
1 |
11 | *
12 | * @see OAuth2\ClientAssertionType_HttpBasic
13 | */
14 | class ClientCredentials extends HttpBasic implements GrantTypeInterface
15 | {
16 | private $clientData;
17 |
18 | public function __construct(ClientCredentialsInterface $storage, array $config = array())
19 | {
20 | /**
21 | * The client credentials grant type MUST only be used by confidential clients
22 | *
23 | * @see http://tools.ietf.org/html/rfc6749#section-4.4
24 | */
25 | $config['allow_public_clients'] = false;
26 |
27 | parent::__construct($storage, $config);
28 | }
29 |
30 | public function getQuerystringIdentifier()
31 | {
32 | return 'client_credentials';
33 | }
34 |
35 | public function getScope()
36 | {
37 | $this->loadClientData();
38 |
39 | return isset($this->clientData['scope']) ? $this->clientData['scope'] : null;
40 | }
41 |
42 | public function getUserId()
43 | {
44 | $this->loadClientData();
45 |
46 | return isset($this->clientData['user_id']) ? $this->clientData['user_id'] : null;
47 | }
48 |
49 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
50 | {
51 | /**
52 | * Client Credentials Grant does NOT include a refresh token
53 | *
54 | * @see http://tools.ietf.org/html/rfc6749#section-4.4.3
55 | */
56 | $includeRefreshToken = false;
57 |
58 | return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
59 | }
60 |
61 | private function loadClientData()
62 | {
63 | if (!$this->clientData) {
64 | $this->clientData = $this->storage->getClientDetails($this->getClientId());
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/library/OAuth2/GrantType/GrantTypeInterface.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | class JwtBearer implements GrantTypeInterface, ClientAssertionTypeInterface
22 | {
23 | private $jwt;
24 |
25 | protected $storage;
26 | protected $audience;
27 | protected $jwtUtil;
28 | protected $allowedAlgorithms;
29 |
30 | /**
31 | * Creates an instance of the JWT bearer grant type.
32 | *
33 | * @param OAuth2\Storage\JWTBearerInterface|JwtBearerInterface $storage A valid storage interface that implements storage hooks for the JWT bearer grant type.
34 | * @param string $audience The audience to validate the token against. This is usually the full URI of the OAuth token requests endpoint.
35 | * @param EncryptionInterface|OAuth2\Encryption\JWT $jwtUtil OPTONAL The class used to decode, encode and verify JWTs.
36 | * @param array $config
37 | */
38 | public function __construct(JwtBearerInterface $storage, $audience, EncryptionInterface $jwtUtil = null, array $config = array())
39 | {
40 | $this->storage = $storage;
41 | $this->audience = $audience;
42 |
43 | if (is_null($jwtUtil)) {
44 | $jwtUtil = new Jwt();
45 | }
46 |
47 | $this->config = array_merge(array(
48 | 'allowed_algorithms' => array('RS256', 'RS384', 'RS512')
49 | ), $config);
50 |
51 | $this->jwtUtil = $jwtUtil;
52 |
53 | $this->allowedAlgorithms = $this->config['allowed_algorithms'];
54 | }
55 |
56 | /**
57 | * Returns the grant_type get parameter to identify the grant type request as JWT bearer authorization grant.
58 | *
59 | * @return
60 | * The string identifier for grant_type.
61 | *
62 | * @see OAuth2\GrantType\GrantTypeInterface::getQuerystringIdentifier()
63 | */
64 | public function getQuerystringIdentifier()
65 | {
66 | return 'urn:ietf:params:oauth:grant-type:jwt-bearer';
67 | }
68 |
69 | /**
70 | * Validates the data from the decoded JWT.
71 | *
72 | * @return
73 | * TRUE if the JWT request is valid and can be decoded. Otherwise, FALSE is returned.
74 | *
75 | * @see OAuth2\GrantType\GrantTypeInterface::getTokenData()
76 | */
77 | public function validateRequest(RequestInterface $request, ResponseInterface $response)
78 | {
79 | if (!$request->request("assertion")) {
80 | $response->setError(400, 'invalid_request', 'Missing parameters: "assertion" required');
81 |
82 | return null;
83 | }
84 |
85 | // Store the undecoded JWT for later use
86 | $undecodedJWT = $request->request('assertion');
87 |
88 | // Decode the JWT
89 | $jwt = $this->jwtUtil->decode($request->request('assertion'), null, false);
90 |
91 | if (!$jwt) {
92 | $response->setError(400, 'invalid_request', "JWT is malformed");
93 |
94 | return null;
95 | }
96 |
97 | // ensure these properties contain a value
98 | // @todo: throw malformed error for missing properties
99 | $jwt = array_merge(array(
100 | 'scope' => null,
101 | 'iss' => null,
102 | 'sub' => null,
103 | 'aud' => null,
104 | 'exp' => null,
105 | 'nbf' => null,
106 | 'iat' => null,
107 | 'jti' => null,
108 | 'typ' => null,
109 | ), $jwt);
110 |
111 | if (!isset($jwt['iss'])) {
112 | $response->setError(400, 'invalid_grant', "Invalid issuer (iss) provided");
113 |
114 | return null;
115 | }
116 |
117 | if (!isset($jwt['sub'])) {
118 | $response->setError(400, 'invalid_grant', "Invalid subject (sub) provided");
119 |
120 | return null;
121 | }
122 |
123 | if (!isset($jwt['exp'])) {
124 | $response->setError(400, 'invalid_grant', "Expiration (exp) time must be present");
125 |
126 | return null;
127 | }
128 |
129 | // Check expiration
130 | if (ctype_digit($jwt['exp'])) {
131 | if ($jwt['exp'] <= time()) {
132 | $response->setError(400, 'invalid_grant', "JWT has expired");
133 |
134 | return null;
135 | }
136 | } else {
137 | $response->setError(400, 'invalid_grant', "Expiration (exp) time must be a unix time stamp");
138 |
139 | return null;
140 | }
141 |
142 | // Check the not before time
143 | if ($notBefore = $jwt['nbf']) {
144 | if (ctype_digit($notBefore)) {
145 | if ($notBefore > time()) {
146 | $response->setError(400, 'invalid_grant', "JWT cannot be used before the Not Before (nbf) time");
147 |
148 | return null;
149 | }
150 | } else {
151 | $response->setError(400, 'invalid_grant', "Not Before (nbf) time must be a unix time stamp");
152 |
153 | return null;
154 | }
155 | }
156 |
157 | // Check the audience if required to match
158 | if (!isset($jwt['aud']) || ($jwt['aud'] != $this->audience)) {
159 | $response->setError(400, 'invalid_grant', "Invalid audience (aud)");
160 |
161 | return null;
162 | }
163 |
164 | // Check the jti (nonce)
165 | // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13#section-4.1.7
166 | if (isset($jwt['jti'])) {
167 | $jti = $this->storage->getJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
168 |
169 | //Reject if jti is used and jwt is still valid (exp parameter has not expired).
170 | if ($jti && $jti['expires'] > time()) {
171 | $response->setError(400, 'invalid_grant', "JSON Token Identifier (jti) has already been used");
172 |
173 | return null;
174 | } else {
175 | $this->storage->setJti($jwt['iss'], $jwt['sub'], $jwt['aud'], $jwt['exp'], $jwt['jti']);
176 | }
177 | }
178 |
179 | // Get the iss's public key
180 | // @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06#section-4.1.1
181 | if (!$key = $this->storage->getClientKey($jwt['iss'], $jwt['sub'])) {
182 | $response->setError(400, 'invalid_grant', "Invalid issuer (iss) or subject (sub) provided");
183 |
184 | return null;
185 | }
186 |
187 | // Verify the JWT
188 | if (!$this->jwtUtil->decode($undecodedJWT, $key, $this->allowedAlgorithms)) {
189 | $response->setError(400, 'invalid_grant', "JWT failed signature verification");
190 |
191 | return null;
192 | }
193 |
194 | $this->jwt = $jwt;
195 |
196 | return true;
197 | }
198 |
199 | public function getClientId()
200 | {
201 | return $this->jwt['iss'];
202 | }
203 |
204 | public function getUserId()
205 | {
206 | return $this->jwt['sub'];
207 | }
208 |
209 | public function getScope()
210 | {
211 | return null;
212 | }
213 |
214 | /**
215 | * Creates an access token that is NOT associated with a refresh token.
216 | * If a subject (sub) the name of the user/account we are accessing data on behalf of.
217 | *
218 | * @see OAuth2\GrantType\GrantTypeInterface::createAccessToken()
219 | */
220 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
221 | {
222 | $includeRefreshToken = false;
223 |
224 | return $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/library/OAuth2/GrantType/RefreshToken.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class RefreshToken implements GrantTypeInterface
15 | {
16 | private $refreshToken;
17 |
18 | protected $storage;
19 | protected $config;
20 |
21 | /**
22 | * @param OAuth2\Storage\RefreshTokenInterface $storage REQUIRED Storage class for retrieving refresh token information
23 | * @param array $config OPTIONAL Configuration options for the server
24 | *
25 | * $config = array(
26 | * 'always_issue_new_refresh_token' => true, // whether to issue a new refresh token upon successful token request
27 | * 'unset_refresh_token_after_use' => true // whether to unset the refresh token after after using
28 | * );
29 | *
30 | */
31 | public function __construct(RefreshTokenInterface $storage, $config = array())
32 | {
33 | $this->config = array_merge(array(
34 | 'always_issue_new_refresh_token' => false,
35 | 'unset_refresh_token_after_use' => true
36 | ), $config);
37 |
38 | // to preserve B.C. with v1.6
39 | // @see https://github.com/bshaffer/oauth2-server-php/pull/580
40 | // @todo - remove in v2.0
41 | if (isset($config['always_issue_new_refresh_token']) && !isset($config['unset_refresh_token_after_use'])) {
42 | $this->config['unset_refresh_token_after_use'] = $config['always_issue_new_refresh_token'];
43 | }
44 |
45 | $this->storage = $storage;
46 | }
47 |
48 | public function getQuerystringIdentifier()
49 | {
50 | return 'refresh_token';
51 | }
52 |
53 | public function validateRequest(RequestInterface $request, ResponseInterface $response)
54 | {
55 | if (!$request->request("refresh_token")) {
56 | $response->setError(400, 'invalid_request', 'Missing parameter: "refresh_token" is required');
57 |
58 | return null;
59 | }
60 |
61 | if (!$refreshToken = $this->storage->getRefreshToken($request->request("refresh_token"))) {
62 | $response->setError(400, 'invalid_grant', 'Invalid refresh token');
63 |
64 | return null;
65 | }
66 |
67 | if ($refreshToken['expires'] > 0 && $refreshToken["expires"] < time()) {
68 | $response->setError(400, 'invalid_grant', 'Refresh token has expired');
69 |
70 | return null;
71 | }
72 |
73 | // store the refresh token locally so we can delete it when a new refresh token is generated
74 | $this->refreshToken = $refreshToken;
75 |
76 | return true;
77 | }
78 |
79 | public function getClientId()
80 | {
81 | return $this->refreshToken['client_id'];
82 | }
83 |
84 | public function getUserId()
85 | {
86 | return isset($this->refreshToken['user_id']) ? $this->refreshToken['user_id'] : null;
87 | }
88 |
89 | public function getScope()
90 | {
91 | return isset($this->refreshToken['scope']) ? $this->refreshToken['scope'] : null;
92 | }
93 |
94 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
95 | {
96 | /*
97 | * It is optional to force a new refresh token when a refresh token is used.
98 | * However, if a new refresh token is issued, the old one MUST be expired
99 | * @see http://tools.ietf.org/html/rfc6749#section-6
100 | */
101 | $issueNewRefreshToken = $this->config['always_issue_new_refresh_token'];
102 | $unsetRefreshToken = $this->config['unset_refresh_token_after_use'];
103 | $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $issueNewRefreshToken);
104 |
105 | if ($unsetRefreshToken) {
106 | $this->storage->unsetRefreshToken($this->refreshToken['refresh_token']);
107 | }
108 |
109 | return $token;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/library/OAuth2/GrantType/UserCredentials.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class UserCredentials implements GrantTypeInterface
15 | {
16 | private $userInfo;
17 |
18 | protected $storage;
19 |
20 | /**
21 | * @param OAuth2\Storage\UserCredentialsInterface $storage REQUIRED Storage class for retrieving user credentials information
22 | */
23 | public function __construct(UserCredentialsInterface $storage)
24 | {
25 | $this->storage = $storage;
26 | }
27 |
28 | public function getQuerystringIdentifier()
29 | {
30 | return 'password';
31 | }
32 |
33 | public function validateRequest(RequestInterface $request, ResponseInterface $response)
34 | {
35 | if (!$request->request("password") || !$request->request("username")) {
36 | $response->setError(400, 'invalid_request', 'Missing parameters: "username" and "password" required');
37 |
38 | return null;
39 | }
40 |
41 | if (!$this->storage->checkUserCredentials($request->request("username"), $request->request("password"))) {
42 | $response->setError(401, 'invalid_grant', 'Invalid username and password combination');
43 |
44 | return null;
45 | }
46 |
47 | $userInfo = $this->storage->getUserDetails($request->request("username"));
48 |
49 | if (empty($userInfo)) {
50 | $response->setError(400, 'invalid_grant', 'Unable to retrieve user information');
51 |
52 | return null;
53 | }
54 |
55 | if (!isset($userInfo['user_id'])) {
56 | throw new \LogicException("you must set the user_id on the array returned by getUserDetails");
57 | }
58 |
59 | $this->userInfo = $userInfo;
60 |
61 | return true;
62 | }
63 |
64 | public function getClientId()
65 | {
66 | return null;
67 | }
68 |
69 | public function getUserId()
70 | {
71 | return $this->userInfo['user_id'];
72 | }
73 |
74 | public function getScope()
75 | {
76 | return isset($this->userInfo['scope']) ? $this->userInfo['scope'] : null;
77 | }
78 |
79 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope)
80 | {
81 | return $accessToken->createAccessToken($client_id, $user_id, $scope);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/Controller/AuthorizeController.php:
--------------------------------------------------------------------------------
1 | query('prompt', 'consent');
19 | if ($prompt == 'none') {
20 | if (is_null($user_id)) {
21 | $error = 'login_required';
22 | $error_message = 'The user must log in';
23 | } else {
24 | $error = 'interaction_required';
25 | $error_message = 'The user must grant access to your application';
26 | }
27 | } else {
28 | $error = 'consent_required';
29 | $error_message = 'The user denied access to your application';
30 | }
31 |
32 | $response->setRedirect($this->config['redirect_status_code'], $redirect_uri, $this->getState(), $error, $error_message);
33 | }
34 |
35 | protected function buildAuthorizeParameters($request, $response, $user_id)
36 | {
37 | if (!$params = parent::buildAuthorizeParameters($request, $response, $user_id)) {
38 | return;
39 | }
40 |
41 | // Generate an id token if needed.
42 | if ($this->needsIdToken($this->getScope()) && $this->getResponseType() == self::RESPONSE_TYPE_AUTHORIZATION_CODE) {
43 | $params['id_token'] = $this->responseTypes['id_token']->createIdToken($this->getClientId(), $user_id, $this->nonce);
44 | }
45 |
46 | // add the nonce to return with the redirect URI
47 | $params['nonce'] = $this->nonce;
48 |
49 | return $params;
50 | }
51 |
52 | public function validateAuthorizeRequest(RequestInterface $request, ResponseInterface $response)
53 | {
54 | if (!parent::validateAuthorizeRequest($request, $response)) {
55 | return false;
56 | }
57 |
58 | $nonce = $request->query('nonce');
59 |
60 | // Validate required nonce for "id_token" and "id_token token"
61 | if (!$nonce && in_array($this->getResponseType(), array(self::RESPONSE_TYPE_ID_TOKEN, self::RESPONSE_TYPE_ID_TOKEN_TOKEN))) {
62 | $response->setError(400, 'invalid_nonce', 'This application requires you specify a nonce parameter');
63 |
64 | return false;
65 | }
66 |
67 | $this->nonce = $nonce;
68 |
69 | return true;
70 | }
71 |
72 | protected function getValidResponseTypes()
73 | {
74 | return array(
75 | self::RESPONSE_TYPE_ACCESS_TOKEN,
76 | self::RESPONSE_TYPE_AUTHORIZATION_CODE,
77 | self::RESPONSE_TYPE_ID_TOKEN,
78 | self::RESPONSE_TYPE_ID_TOKEN_TOKEN,
79 | self::RESPONSE_TYPE_CODE_ID_TOKEN,
80 | );
81 | }
82 |
83 | /**
84 | * Returns whether the current request needs to generate an id token.
85 | *
86 | * ID Tokens are a part of the OpenID Connect specification, so this
87 | * method checks whether OpenID Connect is enabled in the server settings
88 | * and whether the openid scope was requested.
89 | *
90 | * @param $request_scope
91 | * A space-separated string of scopes.
92 | *
93 | * @return
94 | * TRUE if an id token is needed, FALSE otherwise.
95 | */
96 | public function needsIdToken($request_scope)
97 | {
98 | // see if the "openid" scope exists in the requested scope
99 | return $this->scopeUtil->checkScope('openid', $request_scope);
100 | }
101 |
102 | public function getNonce()
103 | {
104 | return $this->nonce;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/Controller/AuthorizeControllerInterface.php:
--------------------------------------------------------------------------------
1 | tokenType = $tokenType;
30 | $this->tokenStorage = $tokenStorage;
31 | $this->userClaimsStorage = $userClaimsStorage;
32 |
33 | $this->config = array_merge(array(
34 | 'www_realm' => 'Service',
35 | ), $config);
36 |
37 | if (is_null($scopeUtil)) {
38 | $scopeUtil = new Scope();
39 | }
40 | $this->scopeUtil = $scopeUtil;
41 | }
42 |
43 | public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response)
44 | {
45 | if (!$this->verifyResourceRequest($request, $response, 'openid')) {
46 | return;
47 | }
48 |
49 | $token = $this->getToken();
50 | $claims = $this->userClaimsStorage->getUserClaims($token['user_id'], $token['scope']);
51 | // The sub Claim MUST always be returned in the UserInfo Response.
52 | // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
53 | $claims += array(
54 | 'sub' => $token['user_id'],
55 | );
56 | $response->addParameters($claims);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/Controller/UserInfoControllerInterface.php:
--------------------------------------------------------------------------------
1 | $response = new OAuth2\Response();
14 | * > $userInfoController->handleUserInfoRequest(
15 | * > OAuth2\Request::createFromGlobals(),
16 | * > $response;
17 | * > $response->send();
18 | *
19 | */
20 | interface UserInfoControllerInterface
21 | {
22 | public function handleUserInfoRequest(RequestInterface $request, ResponseInterface $response);
23 | }
24 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/GrantType/AuthorizationCode.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class AuthorizationCode extends BaseAuthorizationCode {
17 | public function createAccessToken(AccessTokenInterface $accessToken, $client_id, $user_id, $scope) {
18 | $includeRefreshToken = true;
19 |
20 | $config = get_option("wo_options");
21 | /*
22 | if ( isset( $this->authCode['id_token'] ) ) {
23 |
24 | // Issue a refresh token when "offline_access" is presented
25 | // http://openid.net/specs/openid-connect-core-1_0-17.html#OfflineAccess
26 | //
27 | // The document states that a server "MAY" issue a refresh token outside of the "offline_access"
28 | // and since there is a paramter "always_issue_refresh_token" we can hook into that
29 | $scopes = explode(' ', trim($scope));
30 | if(in_array('offline_access', $scopes) || $config['refresh_tokens_enabled']){
31 | $includeRefreshToken = true;
32 | }
33 | }
34 | */
35 |
36 | $token = $accessToken->createAccessToken($client_id, $user_id, $scope, $includeRefreshToken);
37 | if ( isset( $this->authCode['id_token'] ) ) {
38 | $token['id_token'] = $this->authCode['id_token'];
39 | }
40 |
41 | $this->storage->expireAuthorizationCode( $this->authCode['code'] );
42 |
43 | return $token;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/AuthorizationCode.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class AuthorizationCode extends BaseAuthorizationCode implements AuthorizationCodeInterface
13 | {
14 | public function __construct(AuthorizationCodeStorageInterface $storage, array $config = array())
15 | {
16 | parent::__construct($storage, $config);
17 | }
18 |
19 | public function getAuthorizeResponse($params, $user_id = null)
20 | {
21 | // build the URL to redirect to
22 | $result = array('query' => array());
23 |
24 | $params += array('scope' => null, 'state' => null, 'id_token' => null);
25 |
26 | $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope'], $params['id_token']);
27 |
28 | if (isset($params['state'])) {
29 | $result['query']['state'] = $params['state'];
30 | }
31 |
32 | return array($params['redirect_uri'], $result);
33 | }
34 |
35 | /**
36 | * Handle the creation of the authorization code.
37 | *
38 | * @param $client_id
39 | * Client identifier related to the authorization code
40 | * @param $user_id
41 | * User ID associated with the authorization code
42 | * @param $redirect_uri
43 | * An absolute URI to which the authorization server will redirect the
44 | * user-agent to when the end-user authorization step is completed.
45 | * @param $scope
46 | * (optional) Scopes to be stored in space-separated string.
47 | * @param $id_token
48 | * (optional) The OpenID Connect id_token.
49 | *
50 | * @see http://tools.ietf.org/html/rfc6749#section-4
51 | * @ingroup oauth2_section_4
52 | */
53 | public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null)
54 | {
55 | $code = $this->generateAuthorizationCode();
56 | $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope, $id_token);
57 |
58 | return $code;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/AuthorizationCodeInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface
12 | {
13 | /**
14 | * Handle the creation of the authorization code.
15 | *
16 | * @param $client_id Client identifier related to the authorization code
17 | * @param $user_id User ID associated with the authorization code
18 | * @param $redirect_uri An absolute URI to which the authorization server will redirect the
19 | * user-agent to when the end-user authorization step is completed.
20 | * @param $scope OPTIONAL Scopes to be stored in space-separated string.
21 | * @param $id_token OPTIONAL The OpenID Connect id_token.
22 | *
23 | * @see http://tools.ietf.org/html/rfc6749#section-4
24 | * @ingroup oauth2_section_4
25 | */
26 | public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null, $id_token = null);
27 | }
28 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/CodeIdToken.php:
--------------------------------------------------------------------------------
1 | authCode = $authCode;
13 | $this->idToken = $idToken;
14 | }
15 |
16 | public function getAuthorizeResponse($params, $user_id = null)
17 | {
18 | $result = $this->authCode->getAuthorizeResponse($params, $user_id);
19 | $id_token = $this->idToken->createIdToken($params['client_id'], $user_id, $params['nonce']);
20 | $result[1]['query']['id_token'] = $id_token;
21 |
22 | return $result;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/CodeIdTokenInterface.php:
--------------------------------------------------------------------------------
1 | userClaimsStorage = $userClaimsStorage;
20 | $this->publicKeyStorage = $publicKeyStorage;
21 | if (is_null($encryptionUtil)) {
22 | $encryptionUtil = new Jwt();
23 | }
24 | $this->encryptionUtil = $encryptionUtil;
25 |
26 | if (!isset($config['issuer'])) {
27 | throw new \LogicException('config parameter "issuer" must be set');
28 | }
29 | $this->config = array_merge(array(
30 | 'id_lifetime' => 3600,
31 | ), $config);
32 | }
33 |
34 | public function getAuthorizeResponse($params, $userInfo = null)
35 | {
36 | // build the URL to redirect to
37 | $result = array('query' => array());
38 | $params += array('scope' => null, 'state' => null, 'nonce' => null);
39 |
40 | // create the id token.
41 | list($user_id, $auth_time) = $this->getUserIdAndAuthTime($userInfo);
42 | $userClaims = $this->userClaimsStorage->getUserClaims($user_id, $params['scope']);
43 |
44 | $id_token = $this->createIdToken($params['client_id'], $userInfo, $params['nonce'], $userClaims, null);
45 | $result["fragment"] = array('id_token' => $id_token);
46 | if (isset($params['state'])) {
47 | $result["fragment"]["state"] = $params['state'];
48 | }
49 |
50 | return array($params['redirect_uri'], $result);
51 | }
52 |
53 | public function createIdToken($client_id, $userInfo, $nonce = null, $userClaims = null, $access_token = null)
54 | {
55 | // pull auth_time from user info if supplied
56 | list($user_id, $auth_time) = $this->getUserIdAndAuthTime($userInfo);
57 |
58 | $token = array(
59 | 'iss' => $this->config['issuer'],
60 | 'sub' => strval( $user_id ),
61 | 'aud' => $client_id,
62 | 'iat' => time(),
63 | 'exp' => time() + $this->config['id_lifetime'],
64 | 'auth_time' => $auth_time,
65 | );
66 |
67 | if ($nonce) {
68 | $token['nonce'] = $nonce;
69 | }
70 |
71 | if ($userClaims) {
72 | $token += $userClaims;
73 | }
74 |
75 | if ($access_token) {
76 | $token['at_hash'] = $this->createAtHash($access_token, $client_id);
77 | }
78 |
79 | return $this->encodeToken($token, $client_id);
80 | }
81 |
82 | protected function createAtHash($access_token, $client_id = null)
83 | {
84 | $algorithm = wo_get_algorithm();
85 | $hash_algorithm = 'sha' . substr($algorithm, 2);
86 | $hash = hash($hash_algorithm, $access_token);
87 | $at_hash = substr($hash, 0, strlen($hash) / 2);
88 |
89 | return $this->encryptionUtil->urlSafeB64Encode($at_hash);
90 | }
91 |
92 | protected function encodeToken(array $token, $client_id = null) {
93 | $private_key = get_private_server_key();
94 | $algorithm = wo_get_algorithm();
95 |
96 | return $this->encryptionUtil->encode($token, $private_key, $algorithm);
97 | }
98 |
99 | private function getUserIdAndAuthTime ( $userInfo ) {
100 | $auth_time = null;
101 |
102 | // support an array for user_id / auth_time
103 | if (is_array($userInfo)) {
104 | if (!isset($userInfo['user_id'])) {
105 | throw new \LogicException('if $user_id argument is an array, user_id index must be set');
106 | }
107 |
108 | $auth_time = isset($userInfo['auth_time']) ? $userInfo['auth_time'] : null;
109 | $user_id = $userInfo['user_id'];
110 | } else {
111 | $user_id = $userInfo;
112 | }
113 |
114 | if (is_null($auth_time)) {
115 | $auth_time = time();
116 | }
117 |
118 | // userInfo is a scalar, and so this is the $user_id. Auth Time is null
119 | return array($user_id, $auth_time);
120 | }
121 | }
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/IdTokenInterface.php:
--------------------------------------------------------------------------------
1 | accessToken = $accessToken;
15 | $this->idToken = $idToken;
16 | }
17 |
18 | public function getAuthorizeResponse($params, $user_id = null)
19 | {
20 | $result = $this->accessToken->getAuthorizeResponse($params, $user_id);
21 | $access_token = $result[1]['fragment']['access_token'];
22 | $id_token = $this->idToken->createIdToken($params['client_id'], $user_id, $params['nonce'], null, $access_token);
23 | $result[1]['fragment']['id_token'] = $id_token;
24 |
25 | return $result;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/IdTokenTokenInterface.php:
--------------------------------------------------------------------------------
1 | accessToken = $accessToken;
15 | $this->idToken = $idToken;
16 | }
17 |
18 | public function getAuthorizeResponse($params, $user_id = null)
19 | {
20 | $result = $this->accessToken->getAuthorizeResponse($params, $user_id);
21 | $access_token = $result[1]['fragment']['access_token'];
22 | $id_token = $this->idToken->createIdToken($params['client_id'], $user_id, $params['nonce'], null, $access_token);
23 | $result[1]['fragment']['id_token'] = $id_token;
24 |
25 | return $result;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/ResponseType/TokenIdTokenInterface.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | interface AuthorizationCodeInterface extends BaseAuthorizationCodeInterface
14 | {
15 | /**
16 | * Take the provided authorization code values and store them somewhere.
17 | *
18 | * This function should be the storage counterpart to getAuthCode().
19 | *
20 | * If storage fails for some reason, we're not currently checking for
21 | * any sort of success/failure, so you should bail out of the script
22 | * and provide a descriptive fail message.
23 | *
24 | * Required for OAuth2::GRANT_TYPE_AUTH_CODE.
25 | *
26 | * @param $code authorization code to be stored.
27 | * @param $client_id client identifier to be stored.
28 | * @param $user_id user identifier to be stored.
29 | * @param string $redirect_uri redirect URI(s) to be stored in a space-separated string.
30 | * @param int $expires expiration to be stored as a Unix timestamp.
31 | * @param string $scope OPTIONAL scopes to be stored in space-separated string.
32 | * @param string $id_token OPTIONAL the OpenID Connect id_token.
33 | *
34 | * @ingroup oauth2_section_4
35 | */
36 | public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null);
37 | }
38 |
--------------------------------------------------------------------------------
/library/OAuth2/OpenID/Storage/UserClaimsInterface.php:
--------------------------------------------------------------------------------
1 | value format.
34 | *
35 | * @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
36 | */
37 | public function getUserClaims($user_id, $scope);
38 | }
39 |
--------------------------------------------------------------------------------
/library/OAuth2/Request.php:
--------------------------------------------------------------------------------
1 | initialize($query, $request, $attributes, $cookies, $files, $server, $content, $headers);
37 | }
38 |
39 | /**
40 | * Sets the parameters for this request.
41 | *
42 | * This method also re-initializes all properties.
43 | *
44 | * @param array $query The GET parameters
45 | * @param array $request The POST parameters
46 | * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
47 | * @param array $cookies The COOKIE parameters
48 | * @param array $files The FILES parameters
49 | * @param array $server The SERVER parameters
50 | * @param string $content The raw body data
51 | *
52 | * @api
53 | */
54 | public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null, array $headers = null)
55 | {
56 | $this->request = $request;
57 | $this->query = $query;
58 | $this->attributes = $attributes;
59 | $this->cookies = $cookies;
60 | $this->files = $files;
61 | $this->server = $server;
62 | $this->content = $content;
63 | $this->headers = is_null($headers) ? $this->getHeadersFromServer($this->server) : $headers;
64 | }
65 |
66 | public function query($name, $default = null)
67 | {
68 | return isset($this->query[$name]) ? $this->query[$name] : $default;
69 | }
70 |
71 | public function request($name, $default = null)
72 | {
73 | return isset($this->request[$name]) ? $this->request[$name] : $default;
74 | }
75 |
76 | public function server($name, $default = null)
77 | {
78 | return isset($this->server[$name]) ? $this->server[$name] : $default;
79 | }
80 |
81 | public function headers($name, $default = null)
82 | {
83 | $headers = array_change_key_case($this->headers);
84 | $name = strtolower($name);
85 |
86 | return isset($headers[$name]) ? $headers[$name] : $default;
87 | }
88 |
89 | public function getAllQueryParameters()
90 | {
91 | return $this->query;
92 | }
93 |
94 | /**
95 | * Returns the request body content.
96 | *
97 | * @param Boolean $asResource If true, a resource will be returned
98 | *
99 | * @return string|resource The request body content or a resource to read the body stream.
100 | */
101 | public function getContent($asResource = false)
102 | {
103 | if (false === $this->content || (true === $asResource && null !== $this->content)) {
104 | throw new \LogicException('getContent() can only be called once when using the resource return type.');
105 | }
106 |
107 | if (true === $asResource) {
108 | $this->content = false;
109 |
110 | if( !isset( $HTTP_RAW_POST_DATA ) ) {
111 | return fopen('php://input', 'rb');
112 | }else{
113 | return $HTTP_RAW_POST_DATA;
114 | }
115 | }
116 |
117 | if (null === $this->content) {
118 | if( !isset( $HTTP_RAW_POST_DATA ) ) {
119 | $this->content = file_get_contents('php://input');
120 | }else{
121 | $this->content = $HTTP_RAW_POST_DATA;
122 | }
123 | }
124 |
125 | return $this->content;
126 | }
127 |
128 | private function getHeadersFromServer($server)
129 | {
130 | $headers = array();
131 | foreach ($server as $key => $value) {
132 | if (0 === strpos($key, 'HTTP_')) {
133 | $headers[substr($key, 5)] = $value;
134 | }
135 | // CONTENT_* are not prefixed with HTTP_
136 | elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) {
137 | $headers[$key] = $value;
138 | }
139 | }
140 |
141 | if (isset($server['PHP_AUTH_USER'])) {
142 | $headers['PHP_AUTH_USER'] = $server['PHP_AUTH_USER'];
143 | $headers['PHP_AUTH_PW'] = isset($server['PHP_AUTH_PW']) ? $server['PHP_AUTH_PW'] : '';
144 | } else {
145 | /*
146 | * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
147 | * For this workaround to work, add this line to your .htaccess file:
148 | * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
149 | *
150 | * A sample .htaccess file:
151 | * RewriteEngine On
152 | * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
153 | * RewriteCond %{REQUEST_FILENAME} !-f
154 | * RewriteRule ^(.*)$ app.php [QSA,L]
155 | */
156 |
157 | $authorizationHeader = null;
158 | if (isset($server['HTTP_AUTHORIZATION'])) {
159 | $authorizationHeader = $server['HTTP_AUTHORIZATION'];
160 | } elseif (isset($server['REDIRECT_HTTP_AUTHORIZATION'])) {
161 | $authorizationHeader = $server['REDIRECT_HTTP_AUTHORIZATION'];
162 | } elseif (function_exists('apache_request_headers')) {
163 | $requestHeaders = (array) apache_request_headers();
164 |
165 | // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
166 | $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
167 |
168 | if (isset($requestHeaders['Authorization'])) {
169 | $authorizationHeader = trim($requestHeaders['Authorization']);
170 | }
171 | }
172 |
173 | if (null !== $authorizationHeader) {
174 | $headers['AUTHORIZATION'] = $authorizationHeader;
175 | // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
176 | if (0 === stripos($authorizationHeader, 'basic')) {
177 | $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)));
178 | if (count($exploded) == 2) {
179 | list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
180 | }
181 | }
182 | }
183 | }
184 |
185 | // PHP_AUTH_USER/PHP_AUTH_PW
186 | if (isset($headers['PHP_AUTH_USER'])) {
187 | $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
188 | }
189 |
190 | return $headers;
191 | }
192 |
193 | /**
194 | * Creates a new request with values from PHP's super globals.
195 | *
196 | * @return Request A new request
197 | *
198 | * @api
199 | */
200 | public static function createFromGlobals()
201 | {
202 | $class = get_called_class();
203 | $request = new $class($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER);
204 |
205 | $contentType = $request->server('CONTENT_TYPE', '');
206 | $requestMethod = $request->server('REQUEST_METHOD', 'GET');
207 | if (0 === strpos($contentType, 'application/x-www-form-urlencoded')
208 | && in_array(strtoupper($requestMethod), array('PUT', 'DELETE'))
209 | ) {
210 | parse_str($request->getContent(), $data);
211 | $request->request = $data;
212 | } elseif (0 === strpos($contentType, 'application/json')
213 | && in_array(strtoupper($requestMethod), array('POST', 'PUT', 'DELETE'))
214 | ) {
215 | $data = json_decode($request->getContent(), true);
216 | $request->request = $data;
217 | }
218 |
219 | return $request;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/library/OAuth2/RequestInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class AccessToken implements AccessTokenInterface
13 | {
14 | protected $tokenStorage;
15 | protected $refreshStorage;
16 | protected $config;
17 |
18 | /**
19 | * @param OAuth2\Storage\AccessTokenInterface $tokenStorage REQUIRED Storage class for saving access token information
20 | * @param OAuth2\Storage\RefreshTokenInterface $refreshStorage OPTIONAL Storage class for saving refresh token information
21 | * @param array $config OPTIONAL Configuration options for the server
22 | *
23 | * $config = array(
24 | * 'token_type' => 'bearer', // token type identifier
25 | * 'access_lifetime' => 3600, // time before access token expires
26 | * 'refresh_token_lifetime' => 1209600, // time before refresh token expires
27 | * );
28 | *
29 | */
30 | public function __construct(AccessTokenStorageInterface $tokenStorage, RefreshTokenInterface $refreshStorage = null, array $config = array())
31 | {
32 | $this->tokenStorage = $tokenStorage;
33 | $this->refreshStorage = $refreshStorage;
34 |
35 | $this->config = array_merge(array(
36 | 'token_type' => 'bearer',
37 | 'access_lifetime' => 3600,
38 | 'refresh_token_lifetime' => 1209600,
39 | ), $config);
40 | }
41 |
42 | public function getAuthorizeResponse($params, $user_id = null)
43 | {
44 | // build the URL to redirect to
45 | $result = array('query' => array());
46 |
47 | $params += array('scope' => null, 'state' => null);
48 |
49 | /*
50 | * a refresh token MUST NOT be included in the fragment
51 | *
52 | * @see http://tools.ietf.org/html/rfc6749#section-4.2.2
53 | */
54 | $includeRefreshToken = false;
55 | $result["fragment"] = $this->createAccessToken($params['client_id'], $user_id, $params['scope'], $includeRefreshToken);
56 |
57 | if (isset($params['state'])) {
58 | $result["fragment"]["state"] = $params['state'];
59 | }
60 |
61 | return array($params['redirect_uri'], $result);
62 | }
63 |
64 | /**
65 | * Handle the creation of access token, also issue refresh token if supported / desirable.
66 | *
67 | * @param $client_id client identifier related to the access token.
68 | * @param $user_id user ID associated with the access token
69 | * @param $scope OPTIONAL scopes to be stored in space-separated string.
70 | * @param bool $includeRefreshToken if true, a new refresh_token will be added to the response
71 | *
72 | * @see http://tools.ietf.org/html/rfc6749#section-5
73 | * @ingroup oauth2_section_5
74 | */
75 | public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true)
76 | {
77 | $token = array(
78 | "access_token" => $this->generateAccessToken(),
79 | "expires_in" => (int) $this->config['access_lifetime'],
80 | "token_type" => $this->config['token_type'],
81 | "scope" => $scope
82 | );
83 |
84 | $this->tokenStorage->setAccessToken($token["access_token"], $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope);
85 |
86 | /*
87 | * Issue a refresh token also, if we support them
88 | *
89 | * Refresh Tokens are considered supported if an instance of OAuth2\Storage\RefreshTokenInterface
90 | * is supplied in the constructor
91 | */
92 | if ($includeRefreshToken && $this->refreshStorage) {
93 | $token["refresh_token"] = $this->generateRefreshToken();
94 | $expires = 0;
95 | if ($this->config['refresh_token_lifetime'] > 0) {
96 | $expires = time() + $this->config['refresh_token_lifetime'];
97 | }
98 | $this->refreshStorage->setRefreshToken($token['refresh_token'], $client_id, $user_id, $expires, $scope);
99 | }
100 |
101 | return $token;
102 | }
103 |
104 | /**
105 | * Generates an unique access token.
106 | *
107 | * Implementing classes may want to override this function to implement
108 | * other access token generation schemes.
109 | *
110 | * @return
111 | * An unique access token.
112 | *
113 | * @ingroup oauth2_section_4
114 | */
115 | protected function generateAccessToken () {
116 | $tokenLen = 40;
117 | return strtolower(wp_generate_password( $tokenLen, false, $extra_special_chars = false ));
118 | }
119 |
120 | /**
121 | * Generates an unique refresh token
122 | *
123 | * Implementing classes may want to override this function to implement
124 | * other refresh token generation schemes.
125 | *
126 | * @return
127 | * An unique refresh.
128 | *
129 | * @ingroup oauth2_section_4
130 | * @see OAuth2::generateAccessToken()
131 | */
132 | protected function generateRefreshToken()
133 | {
134 | return $this->generateAccessToken(); // let's reuse the same scheme for token generation
135 | }
136 |
137 | /**
138 | * Handle the revoking of refresh tokens, and access tokens if supported / desirable
139 | * RFC7009 specifies that "If the server is unable to locate the token using
140 | * the given hint, it MUST extend its search across all of its supported token types"
141 | *
142 | * @param $token
143 | * @param null $tokenTypeHint
144 | * @return boolean
145 | */
146 | public function revokeToken($token, $tokenTypeHint = null)
147 | {
148 | if ($tokenTypeHint == 'refresh_token') {
149 | if ($this->refreshStorage && $revoked = $this->refreshStorage->unsetRefreshToken($token)) {
150 | return true;
151 | }
152 | }
153 |
154 | /** @TODO remove in v2 */
155 | if (!method_exists($this->tokenStorage, 'unsetAccessToken')) {
156 | throw new \RuntimeException(
157 | sprintf('Token storage %s must implement unsetAccessToken method', get_class($this->tokenStorage)
158 | ));
159 | }
160 |
161 | $revoked = $this->tokenStorage->unsetAccessToken($token);
162 |
163 | // if a typehint is supplied and fails, try other storages
164 | // @see https://tools.ietf.org/html/rfc7009#section-2.1
165 | if (!$revoked && $tokenTypeHint != 'refresh_token') {
166 | if ($this->refreshStorage) {
167 | $revoked = $this->refreshStorage->unsetRefreshToken($token);
168 | }
169 | }
170 |
171 | return $revoked;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/AccessTokenInterface.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | interface AccessTokenInterface extends ResponseTypeInterface
10 | {
11 | /**
12 | * Handle the creation of access token, also issue refresh token if supported / desirable.
13 | *
14 | * @param $client_id client identifier related to the access token.
15 | * @param $user_id user ID associated with the access token
16 | * @param $scope OPTONAL scopes to be stored in space-separated string.
17 | * @param bool $includeRefreshToken if true, a new refresh_token will be added to the response
18 | *
19 | * @see http://tools.ietf.org/html/rfc6749#section-5
20 | * @ingroup oauth2_section_5
21 | */
22 | public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true);
23 |
24 | /**
25 | * Handle the revoking of refresh tokens, and access tokens if supported / desirable
26 | *
27 | * @param $token
28 | * @param $tokenTypeHint
29 | * @return mixed
30 | *
31 | * @todo v2.0 include this method in interface. Omitted to maintain BC in v1.x
32 | */
33 | //public function revokeToken($token, $tokenTypeHint);
34 | }
35 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/AuthorizationCode.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | class AuthorizationCode implements AuthorizationCodeInterface
12 | {
13 | protected $storage;
14 | protected $config;
15 |
16 | public function __construct(AuthorizationCodeStorageInterface $storage, array $config = array())
17 | {
18 | $this->storage = $storage;
19 | $this->config = array_merge(array(
20 | 'enforce_redirect' => false,
21 | 'auth_code_lifetime' => 30,
22 | ), $config);
23 | }
24 |
25 | public function getAuthorizeResponse($params, $user_id = null)
26 | {
27 | // build the URL to redirect to
28 | $result = array('query' => array());
29 |
30 | $params += array('scope' => null, 'state' => null);
31 |
32 | $result['query']['code'] = $this->createAuthorizationCode($params['client_id'], $user_id, $params['redirect_uri'], $params['scope']);
33 |
34 | if (isset($params['state'])) {
35 | $result['query']['state'] = $params['state'];
36 | }
37 |
38 | return array($params['redirect_uri'], $result);
39 | }
40 |
41 | /**
42 | * Handle the creation of the authorization code.
43 | *
44 | * @param $client_id
45 | * Client identifier related to the authorization code
46 | * @param $user_id
47 | * User ID associated with the authorization code
48 | * @param $redirect_uri
49 | * An absolute URI to which the authorization server will redirect the
50 | * user-agent to when the end-user authorization step is completed.
51 | * @param $scope
52 | * (optional) Scopes to be stored in space-separated string.
53 | *
54 | * @see http://tools.ietf.org/html/rfc6749#section-4
55 | * @ingroup oauth2_section_4
56 | */
57 | public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null)
58 | {
59 | $code = $this->generateAuthorizationCode();
60 | $this->storage->setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, time() + $this->config['auth_code_lifetime'], $scope);
61 |
62 | return $code;
63 | }
64 |
65 | /**
66 | * @return
67 | * TRUE if the grant type requires a redirect_uri, FALSE if not
68 | */
69 | public function enforceRedirect()
70 | {
71 | return $this->config['enforce_redirect'];
72 | }
73 |
74 | /**
75 | * Generates an unique auth code.
76 | *
77 | * Implementing classes may want to override this function to implement
78 | * other auth code generation schemes.
79 | *
80 | * @return
81 | * An unique auth code.
82 | *
83 | * @ingroup oauth2_section_4
84 | *
85 | * @since 3.1.94 The function has been converted to use wp_generate_password
86 | */
87 | protected function generateAuthorizationCode()
88 | {
89 | $tokenLen = 40;
90 | return strtolower(wp_generate_password( $tokenLen, false, $extra_special_chars = false ));
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/AuthorizationCodeInterface.php:
--------------------------------------------------------------------------------
1 |
8 | */
9 | interface AuthorizationCodeInterface extends ResponseTypeInterface
10 | {
11 | /**
12 | * @return
13 | * TRUE if the grant type requires a redirect_uri, FALSE if not
14 | */
15 | public function enforceRedirect();
16 |
17 | /**
18 | * Handle the creation of the authorization code.
19 | *
20 | * @param $client_id client identifier related to the authorization code
21 | * @param $user_id user id associated with the authorization code
22 | * @param $redirect_uri an absolute URI to which the authorization server will redirect the
23 | * user-agent to when the end-user authorization step is completed.
24 | * @param $scope OPTIONAL scopes to be stored in space-separated string.
25 | *
26 | * @see http://tools.ietf.org/html/rfc6749#section-4
27 | * @ingroup oauth2_section_4
28 | */
29 | public function createAuthorizationCode($client_id, $user_id, $redirect_uri, $scope = null);
30 | }
31 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/CryptoToken.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class CryptoToken extends AccessToken
17 | {
18 | protected $publicKeyStorage;
19 | protected $encryptionUtil;
20 |
21 | /**
22 | * @param $config
23 | * - store_encrypted_token_string (bool true)
24 | * whether the entire encrypted string is stored,
25 | * or just the token ID is stored
26 | */
27 | public function __construct(PublicKeyInterface $publicKeyStorage = null, AccessTokenStorageInterface $tokenStorage = null, RefreshTokenInterface $refreshStorage = null, array $config = array(), EncryptionInterface $encryptionUtil = null)
28 | {
29 | $this->publicKeyStorage = $publicKeyStorage;
30 | $config = array_merge(array(
31 | 'store_encrypted_token_string' => true,
32 | ), $config);
33 | if (is_null($tokenStorage)) {
34 | // a pass-thru, so we can call the parent constructor
35 | $tokenStorage = new Memory();
36 | }
37 | if (is_null($encryptionUtil)) {
38 | $encryptionUtil = new Jwt();
39 | }
40 | $this->encryptionUtil = $encryptionUtil;
41 | parent::__construct($tokenStorage, $refreshStorage, $config);
42 | }
43 |
44 | /**
45 | * Handle the creation of access token, also issue refresh token if supported / desirable.
46 | *
47 | * @param $client_id
48 | * Client identifier related to the access token.
49 | * @param $user_id
50 | * User ID associated with the access token
51 | * @param $scope
52 | * (optional) Scopes to be stored in space-separated string.
53 | * @param bool $includeRefreshToken
54 | * If true, a new refresh_token will be added to the response
55 | *
56 | * @see http://tools.ietf.org/html/rfc6749#section-5
57 | * @ingroup oauth2_section_5
58 | */
59 | public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true)
60 | {
61 | // token to encrypt
62 | $expires = time() + $this->config['access_lifetime'];
63 | $cryptoToken = array(
64 | 'id' => $this->generateAccessToken(),
65 | 'client_id' => $client_id,
66 | 'user_id' => $user_id,
67 | 'expires' => (int) $expires,
68 | 'token_type' => $this->config['token_type'],
69 | 'scope' => $scope
70 | );
71 |
72 | /*
73 | * Encode the token data into a single access_token string
74 | */
75 | $access_token = $this->encodeToken($cryptoToken, $client_id);
76 |
77 | /*
78 | * Save the token to a secondary storage. This is implemented on the
79 | * OAuth2\Storage\CryptoToken side, and will not actually store anything,
80 | * if no secondary storage has been supplied
81 | */
82 | $token_to_store = $this->config['store_encrypted_token_string'] ? $access_token : $cryptoToken['id'];
83 | $this->tokenStorage->setAccessToken($token_to_store, $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope);
84 |
85 | // token to return to the client
86 | $token = array(
87 | 'access_token' => $access_token,
88 | 'expires_in' => (int) $this->config['access_lifetime'],
89 | 'token_type' => $this->config['token_type'],
90 | 'scope' => $scope
91 | );
92 |
93 | /*
94 | * Issue a refresh token also, if we support them
95 | *
96 | * Refresh Tokens are considered supported if an instance of OAuth2\Storage\RefreshTokenInterface
97 | * is supplied in the constructor
98 | */
99 | if ($includeRefreshToken && $this->refreshStorage) {
100 | $refresh_token = $this->generateRefreshToken();
101 | $expires = 0;
102 | if ($this->config['refresh_token_lifetime'] > 0) {
103 | $expires = time() + $this->config['refresh_token_lifetime'];
104 | }
105 | $this->refreshStorage->setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope);
106 | $token['refresh_token'] = $refresh_token;
107 | }
108 |
109 | return $token;
110 | }
111 |
112 | protected function encodeToken(array $token, $client_id = null)
113 | {
114 | $private_key = get_private_server_key();
115 | $algorithm = 'RS256';
116 |
117 | return $this->encryptionUtil->encode($token, $private_key, $algorithm);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/JwtAccessToken.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class JwtAccessToken extends AccessToken
17 | {
18 | protected $publicKeyStorage;
19 | protected $encryptionUtil;
20 |
21 | /**
22 | * @param $config
23 | * - store_encrypted_token_string (bool true)
24 | * whether the entire encrypted string is stored,
25 | * or just the token ID is stored
26 | */
27 | public function __construct(PublicKeyInterface $publicKeyStorage = null, AccessTokenStorageInterface $tokenStorage = null, RefreshTokenInterface $refreshStorage = null, array $config = array(), EncryptionInterface $encryptionUtil = null)
28 | {
29 | $this->publicKeyStorage = $publicKeyStorage;
30 | $config = array_merge(array(
31 | 'store_encrypted_token_string' => true,
32 | 'issuer' => ''
33 | ), $config);
34 | if (is_null($tokenStorage)) {
35 | // a pass-thru, so we can call the parent constructor
36 | $tokenStorage = new Memory();
37 | }
38 | if (is_null($encryptionUtil)) {
39 | $encryptionUtil = new Jwt();
40 | }
41 | $this->encryptionUtil = $encryptionUtil;
42 | parent::__construct($tokenStorage, $refreshStorage, $config);
43 | }
44 |
45 | /**
46 | * Handle the creation of access token, also issue refresh token if supported / desirable.
47 | *
48 | * @param $client_id
49 | * Client identifier related to the access token.
50 | * @param $user_id
51 | * User ID associated with the access token
52 | * @param $scope
53 | * (optional) Scopes to be stored in space-separated string.
54 | * @param bool $includeRefreshToken
55 | * If true, a new refresh_token will be added to the response
56 | *
57 | * @see http://tools.ietf.org/html/rfc6749#section-5
58 | * @ingroup oauth2_section_5
59 | */
60 | public function createAccessToken($client_id, $user_id, $scope = null, $includeRefreshToken = true)
61 | {
62 | // token to encrypt
63 | $expires = time() + $this->config['access_lifetime'];
64 | $id = $this->generateAccessToken();
65 | $jwtAccessToken = array(
66 | 'id' => $id, // for BC (see #591)
67 | 'jti' => $id,
68 | 'iss' => $this->config['issuer'],
69 | 'aud' => $client_id,
70 | 'sub' => $user_id,
71 | 'exp' => $expires,
72 | 'iat' => time(),
73 | 'token_type' => $this->config['token_type'],
74 | 'scope' => $scope
75 | );
76 |
77 | /*
78 | * Encode the token data into a single access_token string
79 | */
80 | $access_token = $this->encodeToken($jwtAccessToken, $client_id);
81 |
82 | /*
83 | * Save the token to a secondary storage. This is implemented on the
84 | * OAuth2\Storage\JwtAccessToken side, and will not actually store anything,
85 | * if no secondary storage has been supplied
86 | */
87 | $token_to_store = $this->config['store_encrypted_token_string'] ? $access_token : $jwtAccessToken['id'];
88 | $this->tokenStorage->setAccessToken($token_to_store, $client_id, $user_id, $this->config['access_lifetime'] ? time() + $this->config['access_lifetime'] : null, $scope);
89 |
90 | // token to return to the client
91 | $token = array(
92 | 'access_token' => $access_token,
93 | 'expires_in' => (int) $this->config['access_lifetime'],
94 | 'token_type' => $this->config['token_type'],
95 | 'scope' => $scope
96 | );
97 |
98 | /*
99 | * Issue a refresh token also, if we support them
100 | *
101 | * Refresh Tokens are considered supported if an instance of OAuth2\Storage\RefreshTokenInterface
102 | * is supplied in the constructor
103 | */
104 | if ($includeRefreshToken && $this->refreshStorage) {
105 | $refresh_token = $this->generateRefreshToken();
106 | $expires = 0;
107 | if ($this->config['refresh_token_lifetime'] > 0) {
108 | $expires = time() + $this->config['refresh_token_lifetime'];
109 | }
110 | $this->refreshStorage->setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope);
111 | $token['refresh_token'] = $refresh_token;
112 | }
113 |
114 | return $token;
115 | }
116 |
117 | protected function encodeToken(array $token, $client_id = null)
118 | {
119 | $private_key = $this->publicKeyStorage->getPrivateKey($client_id);
120 | $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id);
121 |
122 | return $this->encryptionUtil->encode($token, $private_key, $algorithm);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/library/OAuth2/ResponseType/ResponseTypeInterface.php:
--------------------------------------------------------------------------------
1 | storage = $storage;
30 | }
31 |
32 | /**
33 | * Check if everything in required scope is contained in available scope.
34 | *
35 | * @param $required_scope
36 | * A space-separated string of scopes.
37 | *
38 | * @return
39 | * TRUE if everything in required scope is contained in available scope,
40 | * and FALSE if it isn't.
41 | *
42 | * @see http://tools.ietf.org/html/rfc6749#section-7
43 | *
44 | * @ingroup oauth2_section_7
45 | */
46 | public function checkScope($required_scope, $available_scope)
47 | {
48 | $required_scope = explode(' ', trim($required_scope));
49 | $available_scope = explode(' ', trim($available_scope));
50 |
51 | return (count(array_diff($required_scope, $available_scope)) == 0);
52 | }
53 |
54 | /**
55 | * Check if the provided scope exists in storage.
56 | *
57 | * @param $scope
58 | * A space-separated string of scopes.
59 | *
60 | * @return
61 | * TRUE if it exists, FALSE otherwise.
62 | */
63 | public function scopeExists($scope)
64 | {
65 | // Check reserved scopes first.
66 | $scope = explode(' ', trim($scope));
67 | $reservedScope = $this->getReservedScopes();
68 | $nonReservedScopes = array_diff($scope, $reservedScope);
69 | if (count($nonReservedScopes) == 0) {
70 | return true;
71 | } else {
72 | // Check the storage for non-reserved scopes.
73 | $nonReservedScopes = implode(' ', $nonReservedScopes);
74 |
75 | return $this->storage->scopeExists($nonReservedScopes);
76 | }
77 | }
78 |
79 | public function getScopeFromRequest(RequestInterface $request)
80 | {
81 | // "scope" is valid if passed in either POST or QUERY
82 | return $request->request('scope', $request->query('scope'));
83 | }
84 |
85 | public function getDefaultScope($client_id = null)
86 | {
87 | return $this->storage->getDefaultScope($client_id);
88 | }
89 |
90 | /**
91 | * Get reserved scopes needed by the server.
92 | *
93 | * In case OpenID Connect is used, these scopes must include:
94 | * 'openid', offline_access'.
95 | *
96 | * @return
97 | * An array of reserved scopes.
98 | */
99 | public function getReservedScopes()
100 | {
101 | return array('openid', 'offline_access');
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/library/OAuth2/ScopeInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface AccessTokenInterface
12 | {
13 | /**
14 | * Look up the supplied oauth_token from storage.
15 | *
16 | * We need to retrieve access token data as we create and verify tokens.
17 | *
18 | * @param $oauth_token
19 | * oauth_token to be check with.
20 | *
21 | * @return
22 | * An associative array as below, and return NULL if the supplied oauth_token
23 | * is invalid:
24 | * - expires: Stored expiration in unix timestamp.
25 | * - client_id: (optional) Stored client identifier.
26 | * - user_id: (optional) Stored user identifier.
27 | * - scope: (optional) Stored scope values in space-separated string.
28 | * - id_token: (optional) Stored id_token (if "use_openid_connect" is true).
29 | *
30 | * @ingroup oauth2_section_7
31 | */
32 | public function getAccessToken($oauth_token);
33 |
34 | /**
35 | * Store the supplied access token values to storage.
36 | *
37 | * We need to store access token data as we create and verify tokens.
38 | *
39 | * @param $oauth_token oauth_token to be stored.
40 | * @param $client_id client identifier to be stored.
41 | * @param $user_id user identifier to be stored.
42 | * @param int $expires expiration to be stored as a Unix timestamp.
43 | * @param string $scope OPTIONAL Scopes to be stored in space-separated string.
44 | *
45 | * @ingroup oauth2_section_4
46 | */
47 | public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null);
48 |
49 | /**
50 | * Expire an access token.
51 | *
52 | * This is not explicitly required in the spec, but if defined in a draft RFC for token
53 | * revoking (RFC 7009) https://tools.ietf.org/html/rfc7009
54 | *
55 | * @param $access_token
56 | * Access token to be expired.
57 | *
58 | * @ingroup oauth2_section_6
59 | *
60 | * @todo v2.0 include this method in interface. Omitted to maintain BC in v1.x
61 | */
62 | //public function unsetAccessToken($access_token);
63 | }
64 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/AuthorizationCodeInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | interface AuthorizationCodeInterface
13 | {
14 | /**
15 | * The Authorization Code grant type supports a response type of "code".
16 | *
17 | * @var string
18 | * @see http://tools.ietf.org/html/rfc6749#section-1.4.1
19 | * @see http://tools.ietf.org/html/rfc6749#section-4.2
20 | */
21 | const RESPONSE_TYPE_CODE = "code";
22 |
23 | /**
24 | * Fetch authorization code data (probably the most common grant type).
25 | *
26 | * Retrieve the stored data for the given authorization code.
27 | *
28 | * Required for OAuth2::GRANT_TYPE_AUTH_CODE.
29 | *
30 | * @param $code
31 | * Authorization code to be check with.
32 | *
33 | * @return
34 | * An associative array as below, and NULL if the code is invalid
35 | * @code
36 | * return array(
37 | * "client_id" => CLIENT_ID, // REQUIRED Stored client identifier
38 | * "user_id" => USER_ID, // REQUIRED Stored user identifier
39 | * "expires" => EXPIRES, // REQUIRED Stored expiration in unix timestamp
40 | * "redirect_uri" => REDIRECT_URI, // REQUIRED Stored redirect URI
41 | * "scope" => SCOPE, // OPTIONAL Stored scope values in space-separated string
42 | * );
43 | * @endcode
44 | *
45 | * @see http://tools.ietf.org/html/rfc6749#section-4.1
46 | *
47 | * @ingroup oauth2_section_4
48 | */
49 | public function getAuthorizationCode($code);
50 |
51 | /**
52 | * Take the provided authorization code values and store them somewhere.
53 | *
54 | * This function should be the storage counterpart to getAuthCode().
55 | *
56 | * If storage fails for some reason, we're not currently checking for
57 | * any sort of success/failure, so you should bail out of the script
58 | * and provide a descriptive fail message.
59 | *
60 | * Required for OAuth2::GRANT_TYPE_AUTH_CODE.
61 | *
62 | * @param string $code Authorization code to be stored.
63 | * @param mixed $client_id Client identifier to be stored.
64 | * @param mixed $user_id User identifier to be stored.
65 | * @param string $redirect_uri Redirect URI(s) to be stored in a space-separated string.
66 | * @param int $expires Expiration to be stored as a Unix timestamp.
67 | * @param string $scope OPTIONAL Scopes to be stored in space-separated string.
68 | *
69 | * @ingroup oauth2_section_4
70 | */
71 | public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null);
72 |
73 | /**
74 | * once an Authorization Code is used, it must be exipired
75 | *
76 | * @see http://tools.ietf.org/html/rfc6749#section-4.1.2
77 | *
78 | * The client MUST NOT use the authorization code
79 | * more than once. If an authorization code is used more than
80 | * once, the authorization server MUST deny the request and SHOULD
81 | * revoke (when possible) all tokens previously issued based on
82 | * that authorization code
83 | *
84 | */
85 | public function expireAuthorizationCode($code);
86 | }
87 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/ClientCredentialsInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface ClientCredentialsInterface extends ClientInterface
12 | {
13 |
14 | /**
15 | * Make sure that the client credentials is valid.
16 | *
17 | * @param $client_id
18 | * Client identifier to be check with.
19 | * @param $client_secret
20 | * (optional) If a secret is required, check that they've given the right one.
21 | *
22 | * @return
23 | * TRUE if the client credentials are valid, and MUST return FALSE if it isn't.
24 | * @endcode
25 | *
26 | * @see http://tools.ietf.org/html/rfc6749#section-3.1
27 | *
28 | * @ingroup oauth2_section_3
29 | */
30 | public function checkClientCredentials($client_id, $client_secret = null);
31 |
32 | /**
33 | * Determine if the client is a "public" client, and therefore
34 | * does not require passing credentials for certain grant types
35 | *
36 | * @param $client_id
37 | * Client identifier to be check with.
38 | *
39 | * @return
40 | * TRUE if the client is public, and FALSE if it isn't.
41 | * @endcode
42 | *
43 | * @see http://tools.ietf.org/html/rfc6749#section-2.3
44 | * @see https://github.com/bshaffer/oauth2-server-php/issues/257
45 | *
46 | * @ingroup oauth2_section_2
47 | */
48 | public function isPublicClient($client_id);
49 | }
50 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/ClientInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface ClientInterface
12 | {
13 | /**
14 | * Get client details corresponding client_id.
15 | *
16 | * OAuth says we should store request URIs for each registered client.
17 | * Implement this function to grab the stored URI for a given client id.
18 | *
19 | * @param $client_id
20 | * Client identifier to be check with.
21 | *
22 | * @return array
23 | * Client details. The only mandatory key in the array is "redirect_uri".
24 | * This function MUST return FALSE if the given client does not exist or is
25 | * invalid. "redirect_uri" can be space-delimited to allow for multiple valid uris.
26 | *
27 | * return array(
28 | * "redirect_uri" => REDIRECT_URI, // REQUIRED redirect_uri registered for the client
29 | * "client_id" => CLIENT_ID, // OPTIONAL the client id
30 | * "grant_types" => GRANT_TYPES, // OPTIONAL an array of restricted grant types
31 | * "user_id" => USER_ID, // OPTIONAL the user identifier associated with this client
32 | * "scope" => SCOPE, // OPTIONAL the scopes allowed for this client
33 | * );
34 | *
35 | *
36 | * @ingroup oauth2_section_4
37 | */
38 | public function getClientDetails($client_id);
39 |
40 | /**
41 | * Get the scope associated with this client
42 | *
43 | * @return
44 | * STRING the space-delineated scope list for the specified client_id
45 | */
46 | public function getClientScope($client_id);
47 |
48 | /**
49 | * Check restricted grant types of corresponding client identifier.
50 | *
51 | * If you want to restrict clients to certain grant types, override this
52 | * function.
53 | *
54 | * @param $client_id
55 | * Client identifier to be check with.
56 | * @param $grant_type
57 | * Grant type to be check with
58 | *
59 | * @return
60 | * TRUE if the grant type is supported by this client identifier, and
61 | * FALSE if it isn't.
62 | *
63 | * @ingroup oauth2_section_4
64 | */
65 | public function checkRestrictedGrantType($client_id, $grant_type);
66 | }
67 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/CryptoToken.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class CryptoToken implements CryptoTokenInterface
13 | {
14 | protected $publicKeyStorage;
15 | protected $tokenStorage;
16 | protected $encryptionUtil;
17 |
18 | /**
19 | * @param OAuth2\Encryption\PublicKeyInterface $publicKeyStorage the public key encryption to use
20 | * @param OAuth2\Storage\AccessTokenInterface $tokenStorage OPTIONAL persist the access token to another storage. This is useful if
21 | * you want to retain access token grant information somewhere, but
22 | * is not necessary when using this grant type.
23 | * @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions.
24 | */
25 | public function __construct(PublicKeyInterface $publicKeyStorage, AccessTokenInterface $tokenStorage = null, EncryptionInterface $encryptionUtil = null)
26 | {
27 | $this->publicKeyStorage = $publicKeyStorage;
28 | $this->tokenStorage = $tokenStorage;
29 | if (is_null($encryptionUtil)) {
30 | $encryptionUtil = new Jwt;
31 | }
32 | $this->encryptionUtil = $encryptionUtil;
33 | }
34 |
35 | public function getAccessToken($oauth_token)
36 | {
37 | // just decode the token, don't verify
38 | if (!$tokenData = $this->encryptionUtil->decode($oauth_token, null, false)) {
39 | return false;
40 | }
41 |
42 | $client_id = isset($tokenData['client_id']) ? $tokenData['client_id'] : null;
43 | $private_key = get_private_server_key();
44 | $algorithm = 'RS256';
45 |
46 | // now that we have the client_id, verify the token
47 | if (false === $this->encryptionUtil->decode($oauth_token, $public_key, true)) {
48 | return false;
49 | }
50 |
51 | return $tokenData;
52 | }
53 |
54 | public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null)
55 | {
56 | if ($this->tokenStorage) {
57 | return $this->tokenStorage->setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/CryptoTokenInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface CryptoTokenInterface extends AccessTokenInterface
12 | {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/JwtAccessToken.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class JwtAccessToken implements JwtAccessTokenInterface
13 | {
14 | protected $publicKeyStorage;
15 | protected $tokenStorage;
16 | protected $encryptionUtil;
17 |
18 | /**
19 | * @param OAuth2\Encryption\PublicKeyInterface $publicKeyStorage the public key encryption to use
20 | * @param OAuth2\Storage\AccessTokenInterface $tokenStorage OPTIONAL persist the access token to another storage. This is useful if
21 | * you want to retain access token grant information somewhere, but
22 | * is not necessary when using this grant type.
23 | * @param OAuth2\Encryption\EncryptionInterface $encryptionUtil OPTIONAL class to use for "encode" and "decode" functions.
24 | */
25 | public function __construct(PublicKeyInterface $publicKeyStorage, AccessTokenInterface $tokenStorage = null, EncryptionInterface $encryptionUtil = null)
26 | {
27 | $this->publicKeyStorage = $publicKeyStorage;
28 | $this->tokenStorage = $tokenStorage;
29 | if (is_null($encryptionUtil)) {
30 | $encryptionUtil = new Jwt;
31 | }
32 | $this->encryptionUtil = $encryptionUtil;
33 | }
34 |
35 | public function getAccessToken($oauth_token)
36 | {
37 | // just decode the token, don't verify
38 | if (!$tokenData = $this->encryptionUtil->decode($oauth_token, null, false)) {
39 | return false;
40 | }
41 |
42 | $client_id = isset($tokenData['aud']) ? $tokenData['aud'] : null;
43 | $public_key = $this->publicKeyStorage->getPublicKey($client_id);
44 | $algorithm = $this->publicKeyStorage->getEncryptionAlgorithm($client_id);
45 |
46 | // now that we have the client_id, verify the token
47 | if (false === $this->encryptionUtil->decode($oauth_token, $public_key, array($algorithm))) {
48 | return false;
49 | }
50 |
51 | // normalize the JWT claims to the format expected by other components in this library
52 | return $this->convertJwtToOAuth2($tokenData);
53 | }
54 |
55 | public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null)
56 | {
57 | if ($this->tokenStorage) {
58 | return $this->tokenStorage->setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope);
59 | }
60 | }
61 |
62 | public function unsetAccessToken($access_token)
63 | {
64 | if ($this->tokenStorage) {
65 | return $this->tokenStorage->unsetAccessToken($access_token);
66 | }
67 | }
68 |
69 |
70 | // converts a JWT access token into an OAuth2-friendly format
71 | protected function convertJwtToOAuth2($tokenData)
72 | {
73 | $keyMapping = array(
74 | 'aud' => 'client_id',
75 | 'exp' => 'expires',
76 | 'sub' => 'user_id'
77 | );
78 |
79 | foreach ($keyMapping as $jwtKey => $oauth2Key) {
80 | if (isset($tokenData[$jwtKey])) {
81 | $tokenData[$oauth2Key] = $tokenData[$jwtKey];
82 | unset($tokenData[$jwtKey]);
83 | }
84 | }
85 |
86 | return $tokenData;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/JwtAccessTokenInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface JwtAccessTokenInterface extends AccessTokenInterface
12 | {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/JwtBearerInterface.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | interface JwtBearerInterface
16 | {
17 | /**
18 | * Get the public key associated with a client_id
19 | *
20 | * @param $client_id
21 | * Client identifier to be checked with.
22 | *
23 | * @return
24 | * STRING Return the public key for the client_id if it exists, and MUST return FALSE if it doesn't.
25 | */
26 | public function getClientKey($client_id, $subject);
27 |
28 | /**
29 | * Get a jti (JSON token identifier) by matching against the client_id, subject, audience and expiration.
30 | *
31 | * @param $client_id
32 | * Client identifier to match.
33 | *
34 | * @param $subject
35 | * The subject to match.
36 | *
37 | * @param $audience
38 | * The audience to match.
39 | *
40 | * @param $expiration
41 | * The expiration of the jti.
42 | *
43 | * @param $jti
44 | * The jti to match.
45 | *
46 | * @return
47 | * An associative array as below, and return NULL if the jti does not exist.
48 | * - issuer: Stored client identifier.
49 | * - subject: Stored subject.
50 | * - audience: Stored audience.
51 | * - expires: Stored expiration in unix timestamp.
52 | * - jti: The stored jti.
53 | */
54 | public function getJti($client_id, $subject, $audience, $expiration, $jti);
55 |
56 | /**
57 | * Store a used jti so that we can check against it to prevent replay attacks.
58 | * @param $client_id
59 | * Client identifier to insert.
60 | *
61 | * @param $subject
62 | * The subject to insert.
63 | *
64 | * @param $audience
65 | * The audience to insert.
66 | *
67 | * @param $expiration
68 | * The expiration of the jti.
69 | *
70 | * @param $jti
71 | * The jti to insert.
72 | */
73 | public function setJti($client_id, $subject, $audience, $expiration, $jti);
74 | }
75 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/PublicKeyInterface.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | interface PublicKeyInterface
12 | {
13 | public function getPublicKey($client_id = null);
14 | public function getPrivateKey($client_id = null);
15 | public function getEncryptionAlgorithm($client_id = null);
16 | }
17 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/RefreshTokenInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | interface RefreshTokenInterface
13 | {
14 | /**
15 | * Grant refresh access tokens.
16 | *
17 | * Retrieve the stored data for the given refresh token.
18 | *
19 | * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN.
20 | *
21 | * @param $refresh_token
22 | * Refresh token to be check with.
23 | *
24 | * @return
25 | * An associative array as below, and NULL if the refresh_token is
26 | * invalid:
27 | * - refresh_token: Refresh token identifier.
28 | * - client_id: Client identifier.
29 | * - user_id: User identifier.
30 | * - expires: Expiration unix timestamp, or 0 if the token doesn't expire.
31 | * - scope: (optional) Scope values in space-separated string.
32 | *
33 | * @see http://tools.ietf.org/html/rfc6749#section-6
34 | *
35 | * @ingroup oauth2_section_6
36 | */
37 | public function getRefreshToken($refresh_token);
38 |
39 | /**
40 | * Take the provided refresh token values and store them somewhere.
41 | *
42 | * This function should be the storage counterpart to getRefreshToken().
43 | *
44 | * If storage fails for some reason, we're not currently checking for
45 | * any sort of success/failure, so you should bail out of the script
46 | * and provide a descriptive fail message.
47 | *
48 | * Required for OAuth2::GRANT_TYPE_REFRESH_TOKEN.
49 | *
50 | * @param $refresh_token
51 | * Refresh token to be stored.
52 | * @param $client_id
53 | * Client identifier to be stored.
54 | * @param $user_id
55 | * User identifier to be stored.
56 | * @param $expires
57 | * Expiration timestamp to be stored. 0 if the token doesn't expire.
58 | * @param $scope
59 | * (optional) Scopes to be stored in space-separated string.
60 | *
61 | * @ingroup oauth2_section_6
62 | */
63 | public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null);
64 |
65 | /**
66 | * Expire a used refresh token.
67 | *
68 | * This is not explicitly required in the spec, but is almost implied.
69 | * After granting a new refresh token, the old one is no longer useful and
70 | * so should be forcibly expired in the data store so it can't be used again.
71 | *
72 | * If storage fails for some reason, we're not currently checking for
73 | * any sort of success/failure, so you should bail out of the script
74 | * and provide a descriptive fail message.
75 | *
76 | * @param $refresh_token
77 | * Refresh token to be expirse.
78 | *
79 | * @ingroup oauth2_section_6
80 | */
81 | public function unsetRefreshToken($refresh_token);
82 | }
83 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/ScopeInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | interface ScopeInterface
13 | {
14 | /**
15 | * Check if the provided scope exists.
16 | *
17 | * @param $scope
18 | * A space-separated string of scopes.
19 | *
20 | * @return
21 | * TRUE if it exists, FALSE otherwise.
22 | */
23 | public function scopeExists($scope);
24 |
25 | /**
26 | * The default scope to use in the event the client
27 | * does not request one. By returning "false", a
28 | * request_error is returned by the server to force a
29 | * scope request by the client. By returning "null",
30 | * opt out of requiring scopes
31 | *
32 | * @param $client_id
33 | * An optional client id that can be used to return customized default scopes.
34 | *
35 | * @return
36 | * string representation of default scope, null if
37 | * scopes are not defined, or false to force scope
38 | * request by the client
39 | *
40 | * ex:
41 | * 'default'
42 | * ex:
43 | * null
44 | */
45 | public function getDefaultScope($client_id = null);
46 | }
47 |
--------------------------------------------------------------------------------
/library/OAuth2/Storage/UserCredentialsInterface.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | interface UserCredentialsInterface
13 | {
14 | /**
15 | * Grant access tokens for basic user credentials.
16 | *
17 | * Check the supplied username and password for validity.
18 | *
19 | * You can also use the $client_id param to do any checks required based
20 | * on a client, if you need that.
21 | *
22 | * Required for OAuth2::GRANT_TYPE_USER_CREDENTIALS.
23 | *
24 | * @param $username
25 | * Username to be check with.
26 | * @param $password
27 | * Password to be check with.
28 | *
29 | * @return
30 | * TRUE if the username and password are valid, and FALSE if it isn't.
31 | * Moreover, if the username and password are valid, and you want to
32 | *
33 | * @see http://tools.ietf.org/html/rfc6749#section-4.3
34 | *
35 | * @ingroup oauth2_section_4
36 | */
37 | public function checkUserCredentials($username, $password);
38 |
39 | /**
40 | * @return
41 | * ARRAY the associated "user_id" and optional "scope" values
42 | * This function MUST return FALSE if the requested user does not exist or is
43 | * invalid. "scope" is a space-separated list of restricted scopes.
44 | * @code
45 | * return array(
46 | * "user_id" => USER_ID, // REQUIRED user_id to be stored with the authorization code or access token
47 | * "scope" => SCOPE // OPTIONAL space-separated list of restricted scopes
48 | * );
49 | * @endcode
50 | */
51 | public function getUserDetails($username);
52 | }
53 |
--------------------------------------------------------------------------------
/library/OAuth2/TokenType/Bearer.php:
--------------------------------------------------------------------------------
1 | config = array_merge(array(
18 | 'token_param_name' => 'access_token',
19 | 'token_bearer_header_name' => 'Bearer',
20 | ), $config);
21 | }
22 |
23 | public function getTokenType()
24 | {
25 | return 'Bearer';
26 | }
27 |
28 | /**
29 | * Check if the request has supplied token
30 | *
31 | * @see https://github.com/bshaffer/oauth2-server-php/issues/349#issuecomment-37993588
32 | */
33 | public function requestHasToken(RequestInterface $request)
34 | {
35 | $headers = $request->headers('AUTHORIZATION');
36 |
37 | // check the header, then the querystring, then the request body
38 | return !empty($headers) || (bool) ($request->request($this->config['token_param_name'])) || (bool) ($request->query($this->config['token_param_name']));
39 | }
40 |
41 | /**
42 | * This is a convenience function that can be used to get the token, which can then
43 | * be passed to getAccessTokenData(). The constraints specified by the draft are
44 | * attempted to be adheared to in this method.
45 | *
46 | * As per the Bearer spec (draft 8, section 2) - there are three ways for a client
47 | * to specify the bearer token, in order of preference: Authorization Header,
48 | * POST and GET.
49 | *
50 | * NB: Resource servers MUST accept tokens via the Authorization scheme
51 | * (http://tools.ietf.org/html/rfc6750#section-2).
52 | *
53 | * @todo Should we enforce TLS/SSL in this function?
54 | *
55 | * @see http://tools.ietf.org/html/rfc6750#section-2.1
56 | * @see http://tools.ietf.org/html/rfc6750#section-2.2
57 | * @see http://tools.ietf.org/html/rfc6750#section-2.3
58 | *
59 | * Old Android version bug (at least with version 2.2)
60 | * @see http://code.google.com/p/android/issues/detail?id=6684
61 | *
62 | */
63 | public function getAccessTokenParameter(RequestInterface $request, ResponseInterface $response)
64 | {
65 | $headers = $request->headers('AUTHORIZATION');
66 |
67 | /**
68 | * Ensure more than one method is not used for including an
69 | * access token
70 | *
71 | * @see http://tools.ietf.org/html/rfc6750#section-3.1
72 | */
73 | $methodsUsed = !empty($headers) + (bool) ($request->query($this->config['token_param_name'])) + (bool) ($request->request($this->config['token_param_name']));
74 | if ($methodsUsed > 1) {
75 | $response->setError(400, 'invalid_request', 'Only one method may be used to authenticate at a time (Auth header, GET or POST)');
76 |
77 | return null;
78 | }
79 |
80 | /**
81 | * If no authentication is provided, set the status code
82 | * to 401 and return no other error information
83 | *
84 | * @see http://tools.ietf.org/html/rfc6750#section-3.1
85 | */
86 | if ($methodsUsed == 0) {
87 | $response->setStatusCode(401);
88 |
89 | return null;
90 | }
91 |
92 | // HEADER: Get the access token from the header
93 | if (!empty($headers)) {
94 | if (!preg_match('/' . $this->config['token_bearer_header_name'] . '\s(\S+)/i', $headers, $matches)) {
95 | $response->setError(400, 'invalid_request', 'Malformed auth header');
96 |
97 | return null;
98 | }
99 |
100 | return $matches[1];
101 | }
102 |
103 | if ($request->request($this->config['token_param_name'])) {
104 | // // POST: Get the token from POST data
105 | if (!in_array(strtolower($request->server('REQUEST_METHOD')), array('post', 'put'))) {
106 | $response->setError(400, 'invalid_request', 'When putting the token in the body, the method must be POST or PUT', '#section-2.2');
107 |
108 | return null;
109 | }
110 |
111 | $contentType = $request->server('CONTENT_TYPE');
112 | if (false !== $pos = strpos($contentType, ';')) {
113 | $contentType = substr($contentType, 0, $pos);
114 | }
115 |
116 | if ($contentType !== null && $contentType != 'application/x-www-form-urlencoded') {
117 | // IETF specifies content-type. NB: Not all webservers populate this _SERVER variable
118 | // @see http://tools.ietf.org/html/rfc6750#section-2.2
119 | $response->setError(400, 'invalid_request', 'The content type for POST requests must be "application/x-www-form-urlencoded"');
120 |
121 | return null;
122 | }
123 |
124 | return $request->request($this->config['token_param_name']);
125 | }
126 |
127 | // GET method
128 | return $request->query($this->config['token_param_name']);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/library/OAuth2/TokenType/Mac.php:
--------------------------------------------------------------------------------
1 | _login();
14 | $this->_post_slugs();
15 | $this->_run_callbacks();
16 | add_filter('wo_endpoints', array($this, '_update_endpoint'), 2);
17 | }
18 |
19 | /**
20 | * _update_endpoint function.
21 | *
22 | * Create sample endpoint to update user display name from iOS app
23 | *
24 | * @access public
25 | * @param mixed $methods
26 | * @return void
27 | */
28 | public function _update_endpoint($methods)
29 | {
30 | $methods['update-me'] = array(
31 | 'func'=> array($this, 'run_update_method'), // Function name to run
32 | 'public' => false // True to be public
33 | );
34 |
35 | return $methods;
36 | }
37 |
38 | /**
39 | * run_update_method function.
40 | *
41 | * method used to update and retrieve display name
42 | *
43 | * @access public
44 | * @param mixed $token (default: null)
45 | * @return void
46 | */
47 | public function run_update_method($token = null)
48 | {
49 | $response = new OAuth2\Response();
50 |
51 | if (!isset($token['user_id']) || $token['user_id'] == 0) {
52 |
53 | $response->setError(400, 'invalid_request', 'Missing or invalid access token');
54 | $response->send();
55 | exit;
56 | }
57 |
58 | $user_id = &$token['user_id'];
59 |
60 | if( !current_user_can('edit_user', $user_id) ){
61 | $response->setError(400, 'invalid_request', 'You are not allowed to edit this user');
62 | $response->send();
63 | exit;
64 | }
65 |
66 | $user_id = wp_update_user(
67 | array(
68 | 'ID' => $user_id,
69 | 'display_name' => sanitize_text_field($_POST['name'])
70 | )
71 | );
72 |
73 | if ( is_wp_error( $user_id ) ) {
74 | // There was an error, probably that user doesn't exist.
75 | $response->setError(400, 'invalid_request', 'There was an error updating me');
76 | $response->send();
77 | exit;
78 |
79 | } else {
80 | $return = array('success'=>'updated-me');
81 | $response = new OAuth2\Response($return);
82 | $response->send();
83 | exit();
84 | }
85 | }
86 |
87 | /**
88 | * _get_tokens function.
89 | *
90 | * Get tokens for standard callback
91 | *
92 | * @access private
93 | * @param mixed $client
94 | * @return void
95 | */
96 | private function _get_tokens($client){
97 | if(empty($client)){ return; }
98 |
99 | $response = wp_remote_post( home_url('oauth/token'),
100 | array(
101 | 'timeout' => 45,
102 | 'body' => array(
103 | 'code' => sanitize_text_field($_GET['code']),
104 | 'redirect_uri' => $client['redirect_uri'],
105 | 'grant_type' => 'authorization_code',
106 | 'client_id' => $client['client_id'],
107 | 'client_secret' => $client['client_secret']
108 | ),
109 | )
110 | );
111 |
112 | if ( is_wp_error( $response ) ) {
113 | return json_encode(array('error' => 'get_tokens'));
114 | } else {
115 | $body = wp_remote_retrieve_body($response);
116 | return $body;
117 | }
118 | }
119 |
120 | /**
121 | * _run_callbacks function.
122 | *
123 | * run standard callbacks or place custom callbacks in an action
124 | *
125 | * @access public
126 | * @return void
127 | */
128 | public function _run_callbacks(){
129 |
130 | if(!empty($_GET['_wpoauth_callback']))
131 | {
132 | $headers = getallheaders();
133 | $cookie = explode('=',$headers['Cookie']);
134 |
135 | $callback = sanitize_text_field( $_GET['_wpoauth_callback'] );
136 | $clients = $this->_get_clients();
137 | $slug = get_transient( 'wpoauth_slug_'.$cookie[0] );
138 |
139 | $action = array_filter($clients, function($v) use($slug){
140 | return($v['slug'] == $slug);
141 | });
142 |
143 | delete_transient( 'wpoauth_slug_'.$cookie[0] );
144 |
145 | if(empty($action)){ return; }
146 |
147 | $response = $this->_get_tokens($action[0]);
148 |
149 | if($callback == 'standard'){
150 | ob_start();
151 | echo $response;
152 | header('Content-type: application/json');
153 | ob_end_flush();
154 | exit();
155 | }else{
156 | do_action('wpoauth_finish', array(
157 | 'callback' => $callback,
158 | 'response' => $response
159 | ));
160 | }
161 | }
162 | }
163 |
164 | /**
165 | * _login function.
166 | *
167 | * Login method
168 | * @access public
169 | * @return void
170 | */
171 | public function _login()
172 | {
173 | if( isset($_POST['wpoauth_login']) ){ //!Encrypt Password
174 | $this->_user_data($_POST);
175 | exit();
176 | }
177 | }
178 |
179 | /**
180 | * _user_data function.
181 | *
182 | * Login user and and get user data
183 | *
184 | * @access public
185 | * @param mixed $data
186 | * @return void
187 | */
188 | public function _user_data($data)
189 | {
190 | $data = array_map('sanitize_text_field', $data);
191 | $message = array();
192 |
193 | //wp_signon sanitizes login input
194 | $user = wp_signon( array(
195 | 'user_login' => $data['user_login'],
196 | 'user_password' => $data['user_password'],
197 | 'remember' => false
198 | ), true );
199 |
200 | if ( is_wp_error($user) )
201 | {
202 | //Return error messages
203 | if(isset($user->errors['invalid_username'])){
204 | $message['error'] = "Invalid User Name";
205 | } elseif(isset($user->errors['incorrect_password'])){
206 | $message['error'] = "Incorrect Password";
207 | }
208 |
209 |
210 | ob_start();
211 | echo json_encode($message);
212 | header('Content-type: application/json');
213 | ob_end_flush();
214 |
215 | exit();
216 | }
217 | else
218 | {
219 | /*
220 | * Don't return anymore information than needed.
221 | * In this case we only need the user ID
222 | * But add more as needed
223 | */
224 |
225 | $id = array(
226 | 'ID' => $user->ID
227 | );
228 |
229 | ob_start();
230 | echo json_encode($id);
231 | header('Content-type: application/json');
232 | ob_end_flush();
233 | exit();
234 | }
235 | }
236 |
237 | /**
238 | * _get_clients function.
239 | *
240 | * Get oauth clients
241 | *
242 | * @access private
243 | * @return void
244 | */
245 | private function _get_clients(){
246 | global $wpdb;
247 |
248 | $clients = $wpdb->get_results("
249 | SELECT *
250 | FROM {$wpdb->prefix}oauth_clients"
251 | , ARRAY_A);
252 |
253 | return $clients;
254 | }
255 |
256 | /**
257 | * _refresh function.
258 | *
259 | * Get Refresh token
260 | *
261 | * @access public
262 | * @param mixed $client
263 | * @return void
264 | */
265 | public function _refresh($client){
266 |
267 | $result = array();
268 |
269 | $refresh = wp_remote_post( home_url('oauth/token'),
270 | array(
271 | 'body' => array(
272 | 'grant_type' => 'refresh_token',
273 | 'refresh_token' => sanitize_text_field($_POST['refresh_token']),
274 | 'client_id' => $client['client_id'],
275 | 'client_secret' => $client['client_secret'],
276 | ),
277 | ));
278 |
279 | if ( is_wp_error( $refresh ) ) {
280 | $result = json_encode($refresh->get_error_message());
281 | } else {
282 |
283 | if( !empty($refresh['body']) ){
284 | $result = $refresh['body'];
285 | }
286 | }
287 |
288 | return $result;
289 | }
290 |
291 | /**
292 | * _oauth_types function.
293 | *
294 | * Handle oauth type requests
295 | *
296 | * @access public
297 | * @param mixed $slug
298 | * @param mixed $request
299 | * @param mixed $client
300 | * @return void
301 | */
302 | public function _oauth_types($slug,$request,$client){
303 | if(empty($slug) || empty($request) || empty($client)){ return; }
304 |
305 | $response_type = sanitize_text_field($request['response_type']);
306 |
307 | switch($request[$slug])
308 | {
309 | case 2:
310 | $go = $this->_refresh($client);
311 | ob_start();
312 | echo $go;
313 | header('Content-type: application/json');
314 | ob_end_flush();
315 | exit();
316 | break;
317 |
318 | case 1:
319 |
320 | if( !is_user_logged_in() ){ return; }
321 |
322 | $headers = getallheaders();
323 | $cookie = explode('=',$headers['Cookie']);
324 | set_transient( 'wpoauth_slug_'.$cookie[0], $client['slug'], HOUR_IN_SECONDS );
325 |
326 | $url = home_url('oauth/authorize?response_type='.$response_type.'&client_id='.$client['client_id'].'&redirect_uri='.urlencode($client['redirect_uri']));
327 |
328 | header('Location: ' .$url);
329 | die('Redirect');
330 |
331 | break;
332 | }
333 | }
334 |
335 | /**
336 | * _post_slugs function.
337 | *
338 | * Filter through clients
339 | *
340 | * @access public
341 | * @return void
342 | */
343 | public function _post_slugs(){
344 | $options = get_option($this->option_name);
345 | $clients = $this->_get_clients();
346 |
347 | if(!empty($clients)){
348 |
349 | foreach($clients as $k => $client)
350 | {
351 | if(!$client['use_auth']){return;}
352 |
353 | if(!empty($_POST[$client['slug']])){
354 | $this->_oauth_types($client['slug'],$_POST,$client);
355 | }
356 | }
357 | }
358 | }
359 | }
360 |
361 | add_action('after_setup_theme', array($standard_auth = new StandardAuthentication,'_init') );
362 |
363 | add_action('wpoauth_finish', function($data){
364 |
365 | exit();
366 | });
--------------------------------------------------------------------------------
/library/authentication/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/library/class-wo-api.php:
--------------------------------------------------------------------------------
1 | 'temporarily_unavailable'));
19 | $response->send();
20 | exit;
21 | }
22 |
23 | global $wp_query;
24 | $method = $wp_query->get( 'oauth' );
25 | $well_known = $wp_query->get( 'well-known' );
26 | $storage = new OAuth2\Storage\Wordpressdb();
27 | $config = array(
28 | 'use_crypto_tokens' => false,
29 | 'store_encrypted_token_string' => false,
30 | 'use_openid_connect' => $o['use_openid_connect'] == '' ? false : $o['use_openid_connect'],
31 | 'issuer' => site_url( null, 'https' ), // Must be HTTPS
32 | 'id_lifetime' => $o['id_token_lifetime'] == '' ? 3600 : $o['id_token_lifetime'],
33 | 'access_lifetime' => $o['access_token_lifetime'] == '' ? 3600 : $o['access_token_lifetime'],
34 | 'refresh_token_lifetime' => $o['refresh_token_lifetime'] == '' ? 86400 : $o['refresh_token_lifetime'],
35 | 'www_realm' => 'Service',
36 | 'token_param_name' => 'access_token',
37 | 'token_bearer_header_name' => 'Bearer',
38 | 'enforce_state' => $o['enforce_state'] == '1' ? true : false,
39 | 'require_exact_redirect_uri' => $o['require_exact_redirect_uri'] == '1' ? true : false,
40 | 'allow_implicit' => $o['implicit_enabled'] == '1' ? true : false,
41 | 'allow_credentials_in_request_body' => true, // Must be set to true for openID to work in most cases
42 | 'allow_public_clients' => false,
43 | 'always_issue_new_refresh_token' => true,
44 | 'redirect_status_code' => 302
45 | );
46 | $server = new OAuth2\Server( $storage, $config );
47 |
48 | /*
49 | |--------------------------------------------------------------------------
50 | | SUPPORTED GRANT TYPES
51 | |--------------------------------------------------------------------------
52 | |
53 | | Authorization Code will always be on. This may be a bug or a f@#$ up on
54 | | my end. None the less, these are controlled in the server settings page.
55 | |
56 | */
57 | $support_grant_types = array();
58 | if ( '1' == $o['auth_code_enabled'] ) {
59 | $server->addGrantType( new OAuth2\GrantType\AuthorizationCode( $storage ) );
60 | }
61 | if ( '1' == $o['client_creds_enabled'] ) {
62 | $server->addGrantType(new OAuth2\GrantType\ClientCredentials( $storage ) );
63 | }
64 | if ( '1' == $o['user_creds_enabled'] ) {
65 | $server->addGrantType(new OAuth2\GrantType\UserCredentials( $storage ) );
66 | }
67 | if ( '1' == $o['refresh_tokens_enabled'] ) {
68 | $server->addGrantType( new OAuth2\GrantType\RefreshToken( $storage, $config ) );
69 | }
70 | if ( '1' == $o['use_openid_connect'] ) {
71 | $server->addGrantType( new OAuth2\OpenID\GrantType\AuthorizationCode( $storage, $config ) );
72 | }
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | DEFAULT SCOPES
77 | |--------------------------------------------------------------------------
78 | |
79 | | Supported scopes can be added to the plugin by modifying the wo_scopes.
80 | | Until further notice, the default scope is 'basic'. Plans are in place to
81 | | allow this scope to be adjusted.
82 | |
83 | */
84 | $defaultScope = 'basic';
85 | $supportedScopes = apply_filters( 'wo_scopes', null, 20 );
86 |
87 | $memory = new OAuth2\Storage\Memory( array(
88 | 'default_scope' => $defaultScope,
89 | 'supported_scopes' => $supportedScopes,
90 | ) );
91 | $scopeUtil = new OAuth2\Scope( $memory );
92 | $server->setScopeUtil( $scopeUtil );
93 |
94 | /*
95 | |--------------------------------------------------------------------------
96 | | TOKEN CATCH
97 | |--------------------------------------------------------------------------
98 | |
99 | | The following code is ran when a request is made to the server using the
100 | | Authorization Code (implicit) Grant Type as well as request tokens
101 | |
102 | */
103 | if ( $method == 'token' ) {
104 | do_action( 'wo_before_token_method', array( $_REQUEST ) );
105 | $server->handleTokenRequest( OAuth2\Request::createFromGlobals() )->send();
106 | exit;
107 | }
108 |
109 | /*
110 | |--------------------------------------------------------------------------
111 | | AUTHORIZATION CODE CATCH
112 | |--------------------------------------------------------------------------
113 | |
114 | | The following code is ran when a request is made to the server using the
115 | | Authorization Code (not implicit) Grant Type.
116 | |
117 | | 1. Check if the user is logged in (redirect if not)
118 | | 2. Validate the request (client_id, redirect_uri)
119 | | 3. Create the authorization request using the authentication user's user_id
120 | |
121 | */
122 | if ( $method == 'authorize' ) {
123 | do_action( 'wo_before_authorize_method', array( $_REQUEST ) );
124 | $request = OAuth2\Request::createFromGlobals();
125 | $response = new OAuth2\Response();
126 | if (! $server->validateAuthorizeRequest( $request, $response ) ) {
127 | $response->send();
128 | exit;
129 | }
130 |
131 | if (! is_user_logged_in() ) {
132 | wp_redirect( wp_login_url( $_SERVER['REQUEST_URI'] ) );
133 | exit;
134 | }
135 |
136 | $server->handleAuthorizeRequest($request, $response, true, get_current_user_id());
137 | $response->send();
138 | exit;
139 | }
140 |
141 | /*
142 | |--------------------------------------------------------------------------
143 | | PUBLIC KEY
144 | |--------------------------------------------------------------------------
145 | |
146 | | Presents the generic public key for signing.
147 | | @since 3.0.5
148 | */
149 | if ( $well_known == 'keys' ) {
150 | $keys = apply_filters( 'wo_server_keys', null);
151 | $publicKey = openssl_pkey_get_public( file_get_contents( $keys['public'] ) );
152 | $publicKey = openssl_pkey_get_details( $publicKey );
153 | $response = new OAuth2\Response( array(
154 | 'keys' => array(
155 | array(
156 | 'kty' => 'RSA',
157 | 'alg' => 'RS256',
158 | 'use' => 'sig',
159 | 'n' => base64_encode( $publicKey['rsa']['n'] ),
160 | 'e' => base64_encode( $publicKey['rsa']['e'] )
161 | )
162 | )
163 | ));
164 | $response->send();
165 | exit;
166 | }
167 |
168 | /*
169 | |--------------------------------------------------------------------------
170 | | OpenID Discovery
171 | |--------------------------------------------------------------------------
172 | |
173 | */
174 | if ( $well_known == 'openid-configuration' ) {
175 | $openid_configuration = array(
176 | 'issuer' => site_url( null, 'https' ),
177 | 'authorization_endpoint' => site_url( '/oauth/authorize' ),
178 | 'token_endpoint' => site_url( 'oauth/token' ),
179 | 'userinfo_endpoint' => site_url( '/oauth/me' ),
180 | 'jwks_uri' => site_url( '/.well-known/keys' ),
181 | 'response_types_supported' => array( 'code', 'id_token', 'token id_token', 'code id_token' ),
182 | 'subject_types_supported' => array( 'public' ),
183 | 'id_token_signing_alg_values_supported' => array( 'RS256' )
184 | );
185 | $response = new OAuth2\Response( $openid_configuration );
186 | $response->send();
187 | exit;
188 | }
189 |
190 | /*
191 | |--------------------------------------------------------------------------
192 | | EXTENDABLE RESOURCE SERVER METHODS
193 | |--------------------------------------------------------------------------
194 | |
195 | | Below this line is part of the developer API. Do not edit directly.
196 | | Refer to the developer documentation for extending the WordPress OAuth
197 | | Server plugin core functionality.
198 | |
199 | | @todo Document and tighten up error messages. All error messages will soon be
200 | | controlled through apply_filters so start planning for a filter error list to
201 | | allow for developers to customize error messages.
202 | |
203 | */
204 | $ext_methods = apply_filters( "wo_endpoints", null );
205 |
206 | // Check to see if the method exists in the filter
207 | if ( array_key_exists( $method, $ext_methods ) ) {
208 |
209 | // If the method is is set to public, lets just run the method without
210 | if( isset( $ext_methods[$method]['public'] ) && $ext_methods[$method]['public'] ){
211 | call_user_func_array($ext_methods[$method]['func'], $_REQUEST);
212 | exit;
213 | }
214 |
215 | $response = new OAuth2\Response();
216 | if ( ! $server->verifyResourceRequest( OAuth2\Request::createFromGlobals() ) ) {
217 | $response->setError(400, 'invalid_request', 'Missing or invalid parameter(s)');
218 | $response->send();
219 | exit;
220 | }
221 | $token = $server->getAccessTokenData( OAuth2\Request::createFromGlobals() );
222 | if ( is_null( $token ) ) {
223 | $server->getResponse()->send();
224 | exit;
225 | }
226 |
227 | do_action('wo_endpoint_user_authenticated', array( $token ) );
228 | call_user_func_array( $ext_methods[$method]['func'], array( $token ) );
229 |
230 | exit;
231 | }
232 |
233 | /**
234 | * Server error response. End of line
235 | * @since 3.1.0
236 | */
237 | $response = new OAuth2\Response();
238 | $response->setError(400, 'invalid_request', 'Unknown request');
239 | $response->send();
240 | exit;
--------------------------------------------------------------------------------
/library/class-wo-table.php:
--------------------------------------------------------------------------------
1 | 'wp_list_text_link', //Singular label
14 | 'plural' => 'wp_list_test_links', //plural label, also this well be one of the table css class
15 | 'ajax' => false //We won't support Ajax for this table
16 | ) );
17 | }
18 |
19 | /**
20 | * Add extra markup in the toolbars before or after the list
21 | * @param string $which, helps you decide if you add the markup after (bottom) or before (top) the list
22 | */
23 | function extra_tablenav( $which ) {
24 | if ( $which == "top" ){
25 | return false;
26 | }
27 | if ( $which == "bottom" ){
28 | return false;
29 | }
30 | }
31 |
32 | /**
33 | * Overide default functionality to remove _nonce field
34 | * @return [type] [description]
35 | */
36 | function display_tablenav ( $which ) {
37 | ?>
38 |
39 |
40 | bulk_actions( $which ); ?>
41 |
42 | extra_tablenav( $which );
44 | $this->pagination( $which );
45 | ?>
46 |
47 |
48 | __('Name'),
58 | 'description' => __('Description'),
59 | //'user_id' => __('User ID'),
60 | 'client_id' => __('Client ID'), //,
61 | //'redirect_uri' => __('Redirect URI')
62 | 'slug' => __('Authentication')
63 | );
64 | }
65 |
66 | /**
67 | * Decide which columns to activate the sorting functionality on
68 | * @return array $sortable, the array of columns that can be sorted by the user
69 | */
70 | public function get_sortable_columns() {
71 | return $sortable = array(
72 | //'name' => array('name'),
73 | //'user_id'=>array('user_id')
74 | );
75 | }
76 |
77 | /**
78 | * Prepare the table with different parameters, pagination, columns and table elements
79 | */
80 | function prepare_items() {
81 | global $wpdb, $_wp_column_headers;
82 | $screen = get_current_screen();
83 |
84 | $query = "SELECT * FROM {$wpdb->prefix}oauth_clients";
85 |
86 | $orderby = ! empty( $_GET['orderby'] ) ? mysql_real_escape_string( $_GET['orderby'] ) : 'ASC';
87 | $order = ! empty( $_GET['order'] ) ? mysql_real_escape_string( $_GET['order'] ) : '';
88 |
89 | if( ! empty( $orderby ) & ! empty( $order ) ) {
90 | $query .= ' ORDER BY ' . $orderby . ' ' . $order;
91 | }
92 |
93 | $totalitems = $wpdb->query( $query );
94 | $perpage = 5;
95 | $paged = ! empty( $_GET['paged'] ) ? mysql_real_escape_string( $_GET['paged'] ) : '';
96 |
97 | if( empty( $paged ) || ! is_numeric( $paged ) || $paged <= 0 ) {
98 | $paged = 1;
99 | }
100 |
101 | $totalpages = ceil( $totalitems / $perpage );
102 |
103 | if( ! empty( $paged ) && ! empty( $perpage ) ) {
104 | $offset = ( $paged - 1 ) * $perpage;
105 | $query.= ' LIMIT ' . (int) $offset . ',' . (int) $perpage;
106 | }
107 |
108 | $this->set_pagination_args( array(
109 | 'total_items' => $totalitems,
110 | 'total_pages' => $totalpages,
111 | 'per_page' => $perpage,
112 | ) );
113 |
114 | $columns = $this->get_columns();
115 | $hidden = array();
116 | $sortable = $this->get_sortable_columns();
117 | $this->_column_headers = array( $columns, $hidden, $sortable );
118 | $this->items = $wpdb->get_results($query);
119 | }
120 |
121 | /**
122 | * Display the rows of records in the table
123 | * @return string, echo the markup of the rows
124 | */
125 | function display_rows() {
126 | //Get the records registered in the prepare_items method
127 | $records = $this->items;
128 |
129 | //Get the columns registered in the get_columns and get_sortable_columns methods
130 | list( $columns, $hidden ) = $this->get_column_info();
131 |
132 | //Loop for each record
133 | if(!empty($records)){foreach($records as $rec){
134 |
135 | //Open the line
136 | echo '';
137 | foreach ( $columns as $column_name => $column_display_name ) {
138 |
139 | //Style attributes for each col
140 | $class = "class='$column_name column-$column_name'";
141 | $style = "";
142 | if ( in_array( $column_name, $hidden ) ) $style = ' style="display:none;"';
143 | $attributes = $class . $style;
144 |
145 | //edit link
146 | //$editlink = '/wp-admin/link.php?action=edit&link_id='.(int)$rec->client_id;
147 |
148 | //Display the cell
149 | switch ( $column_name ) {
150 | case "name":
151 | $edit_link = site_url() . '?wpoauthincludes=edit&_wp_nonce=' . wp_create_nonce( 'wpo-edit-client' ) . '&client_id='.$rec->client_id.'&TB_iframe=true&width=600&height=420';
152 | echo '
153 |
154 | '.stripslashes($rec->name).'
155 | '; break;
157 |
158 | case "description": echo ' '.stripslashes($rec->description).' '; break;
159 | //case "user_id": echo ''.stripslashes($rec->user_id).' '; break;
160 | case "client_id": echo ''.$rec->client_id.' '; break;
161 | // case "redirect_uri": echo ''.$rec->redirect_uri.' '; break;
162 | case "col_link_visible": echo ''.$rec->link_visible.' '; break;
163 | case "slug":
164 | echo '
165 |
166 |
169 |
170 | You will be required to use your own authentication method if this is unchecked
171 | ';
172 | break;
173 | }
174 | }
175 |
176 | //Close the line
177 | echo' ';
178 | }}
179 |
180 | ?>
181 |
209 |
210 |
--------------------------------------------------------------------------------
/library/content/create-new-client.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * @todo Implant proper error handling
7 | */
8 |
9 | /** Block direct and or unauthorized access */
10 | if(!current_user_can('manage_options') || ! wp_verify_nonce($_GET['_wpnonce'], 'wpo-create-client') )
11 | exit('Unauthorized Access');
12 |
13 | /** listen for post back */
14 | if(isset($_POST['_wpnonce']) && wp_verify_nonce( $_POST['_wpnonce'], 'add-new-client')){
15 | if( wo_create_client( $_POST ) ){
16 | print 'Reloading...';
17 | exit;
18 | }
19 | print 'There was an issue creating a new client in the server';
20 | }
21 |
22 | $options = get_option('wo_options');
23 | ?>
24 |
99 |
100 |
101 |
102 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/library/content/edit-client.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * @todo Although this file works is it is very import that we do it right. Now that we have it working and it
7 | * is secure, we need to start tweaking the file to be more WP compliant. Quality Matters!
8 | *
9 | * @todo Add an additional check to ensure that the form is being loaded by WordPress.
10 | * @todo Load WP core JS and styles for the plugin. It will be more cleaner and not rely on external JS libs.
11 | */
12 |
13 | /** should stop 99% exploits */
14 | if(! current_user_can('manage_options') || !isset($_REQUEST['client_id']) || ! wp_verify_nonce($_REQUEST['_wp_nonce'], 'wpo-edit-client') )
15 | wp_die('');
16 |
17 | global $wpdb;
18 | $client_info = $wpdb->get_row($wpdb->prepare("
19 | SELECT *
20 | FROM {$wpdb->prefix}oauth_clients
21 | WHERE client_id='%s'", array($_GET['client_id']))
22 | );
23 |
24 | /** simple check if the client exists */
25 | if(!$client_info)
26 | exit('Unauthorized Access');
27 |
28 | /** listen for post back */
29 | if(isset($_POST['_wpnonce']) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-client')){
30 | global $wpdb;
31 |
32 | $slug = empty($_POST['client-name']) ? 'No Name '.rand() : $_POST['client-name'];
33 | $slug = str_replace('-','_',sanitize_title($slug));
34 |
35 | $update_client = $wpdb->update("{$wpdb->prefix}oauth_clients",
36 | array(
37 | 'redirect_uri' => $_POST['client-redirect-uri'],
38 | 'name' => $_POST['client-name'],
39 | 'slug' => $slug,
40 | 'description' => $_POST['client-description']
41 | ),
42 | array(
43 | 'client_id' => $_GET['client_id']
44 | ));
45 |
46 | print 'Reloading...';
47 | exit;
48 | }
49 | ?>
50 |
125 |
126 |
127 |
128 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/library/keys/.htaccess:
--------------------------------------------------------------------------------
1 | # VERY IMPORTANT - DO NOT REMOVE THIS HTACCESS FILE
2 | deny from all
--------------------------------------------------------------------------------
/library/keys/index.php:
--------------------------------------------------------------------------------
1 | add_query_var( 'oauth' );
69 | $wp->add_query_var( 'well-known' );
70 | $wp->add_query_var( 'wpoauthincludes' );
71 | }
72 | add_action( 'init', '_wo_server_register_query_vars' );
73 |
74 | /**
75 | * Registers rewrites for OAuth2 Server
76 | *
77 | * - authorize
78 | * - token
79 | * - .well-known
80 | * - wpoauthincludes
81 | *
82 | * @return void
83 | */
84 | function _wo_server_register_rewrites() {
85 | add_rewrite_rule( '^oauth/(.+)','index.php?oauth=$matches[1]','top' );
86 | add_rewrite_rule( '^.well-known/(.+)','index.php?well-known=$matches[1]','top' );
87 | add_rewrite_rule( '^wpoauthincludes/(.+)','index.php?wpoauthincludes=$matches[1]','top' );
88 | }
89 |
90 | /**
91 | * [template_redirect_intercept description]
92 | * @return [type] [description]
93 | */
94 | function _wo_server_template_redirect_intercept( $template ) {
95 | global $wp_query;
96 |
97 | if ( $wp_query->get( 'oauth' ) || $wp_query->get( 'well-known' ) ) {
98 | require_once dirname( __FILE__ ) . '/library/class-wo-api.php';
99 | exit;
100 | }
101 |
102 | if ( $wp_query->get( 'wpoauthincludes' ) ) {
103 | $allowed_includes = array(
104 | 'create' => dirname( WPOAUTH_FILE ) . '/library/content/create-new-client.php',
105 | 'edit' => dirname( WPOAUTH_FILE ) . '/library/content/edit-client.php'
106 | );
107 | if( array_key_exists( $wp_query->get( 'wpoauthincludes' ), $allowed_includes ) && current_user_can( 'manage_options' ) ) {
108 | require_once $allowed_includes[$wp_query->get( 'wpoauthincludes' )];
109 | }
110 | }
111 |
112 | return $template;
113 | }
114 | add_filter( 'template_include', '_wo_server_template_redirect_intercept', 100);
115 |
116 | /**
117 | * OAuth2 Server Activation
118 | * @param [type] $network_wide [description]
119 | * @return [type] [description]
120 | */
121 | function _wo_server_activation( $network_wide ) {
122 | if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
123 | $mu_blogs = wp_get_sites();
124 | foreach ( $mu_blogs as $mu_blog ) {
125 | switch_to_blog( $mu_blog['blog_id'] );
126 | _wo_server_register_rewrites();
127 | flush_rewrite_rules();
128 | }
129 | restore_current_blog();
130 | } else {
131 | _wo_server_register_rewrites();
132 | flush_rewrite_rules();
133 | }
134 | }
135 | register_activation_hook( __FILE__, '_wo_server_activation' );
136 |
137 | /**
138 | * OAuth Server Deactivation
139 | * @param [type] $network_wide [description]
140 | * @return [type] [description]
141 | */
142 | function _wo_server_deactivation( $network_wide ) {
143 | if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
144 | $mu_blogs = wp_get_sites();
145 | foreach ( $mu_blogs as $mu_blog ) {
146 | switch_to_blog( $mu_blog['blog_id'] );
147 | flush_rewrite_rules();
148 | }
149 | restore_current_blog();
150 | } else {
151 | flush_rewrite_rules();
152 | }
153 | }
154 | register_deactivation_hook( __FILE__, '_wo_server_deactivation' );
155 |
156 | /**
157 | * @todo Move setup and upgrade inside the function wo_plugin_activate()
158 | */
159 | register_activation_hook(__FILE__, array(new WO_Server, 'setup'));
160 | register_activation_hook(__FILE__, array(new WO_Server, 'upgrade'));
161 |
162 | include_once( dirname( __FILE__ ) . '/library/authentication/StandardAuthentication.php' );
163 |
--------------------------------------------------------------------------------