├── 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 |
8 |

Server Status

9 |

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 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 |
Plugin Build: 17 | version, '-') ? _WO()->version . " You are using a development version of the plugin." : _WO()->version;?> 18 |
PHP Version (): 24 | = 0 ? " OK" : " Failed - Please upgrade PHP to 5.4 or greater.";?> 25 |
Apache Version: 31 | apache_get_version() not enabled.'; ?> 32 |
Running CGI: 38 | OK" : " Notice - Header 'Authorization Basic' may not work as expected.";?> 39 |
Certificates Generated: 45 | No Certificates Found" : "Certificates Found"?> 46 |
License: 52 | Standard" : "Licensed"?> 53 |
57 |
58 | false 8 | ); 9 | 10 | /** loop though all the ajax events and add then as needed */ 11 | foreach ( $ajax_events as $ajax_event => $nopriv ) { 12 | add_action( 'wp_ajax_wo_' . $ajax_event, 'wo_ajax_'.$ajax_event ); 13 | if ( $nopriv ) 14 | add_action( 'wp_ajax_nopriv_wo_' . $ajax_event, 'wo_ajax_'.$ajax_event ); 15 | } 16 | 17 | function wo_ajax_remove_client () { 18 | global $wpdb; 19 | $action = $wpdb->delete( "{$wpdb->prefix}oauth_clients", array( 'client_id' => $_POST['data']) ); 20 | if($action){ 21 | print "1"; 22 | }else{ 23 | print "System Error: Could not remove the client from the server."; 24 | } 25 | exit; 26 | } -------------------------------------------------------------------------------- /includes/filters.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | 8 | /** 9 | * WordPress OAuth Server Error Filter 10 | * @deprecated Schedule for removal. The PHP server handles all these now. 11 | */ 12 | add_filter("WO_API_Errors", "wo_api_error_setup", 1); 13 | function wo_api_error_setup($errors) { 14 | $errors["invalid_access_token"] = "The access token is invalid or has expired"; 15 | $errors["invalid_refresh_token"] = "The refresh token is invalid or has expired"; 16 | $errors["invalid_credentials"] = "Invalid user credentials"; 17 | 18 | return $errors; 19 | } 20 | 21 | /** 22 | * Server Key Locations Filter 23 | * @since 3.0.5 24 | */ 25 | add_filter('wo_server_keys', 'wo_server_key_location', 1); 26 | function wo_server_key_location($keys) { 27 | $keys['public'] = WOABSPATH . '/library/keys/public_key.pem'; 28 | $keys['private'] = WOABSPATH . '/library/keys/private_key.pem'; 29 | return $keys; 30 | } 31 | 32 | /** 33 | * Tabs filter 34 | * Allows developers to extend the options page 35 | * @since 3.0.3 36 | * 37 | * @todo The tabs filter will work for adding additional tabs but we need to look into handling the save 38 | * method since it is not tied into the core and the core is hard coded. Maybe we need to add all tabs into 39 | * the filter and find a way to save the values. 40 | */ 41 | add_filter('wo_tabs', 'wo_extend_tabs'); 42 | function wo_extend_tabs ( $tabs ) { 43 | return $tabs; 44 | } 45 | 46 | /** 47 | * Default Method Filter for the resource server API calls 48 | * 49 | * @since 3.1.8 Endpoints now can accept public methods that bypass the token authorization 50 | */ 51 | add_filter('wo_endpoints', 'wo_default_endpoints', 1); 52 | function wo_default_endpoints () { 53 | $endpoints = array( 54 | 'me' => array( 55 | 'func' => '_wo_method_me', 56 | 'public' => false 57 | ), 58 | 'destroy' => array( 59 | 'func' => '_wo_method_destroy', 60 | 'public' => false 61 | ) 62 | ); 63 | return $endpoints; 64 | } 65 | 66 | /** 67 | * Default Support Scopes 68 | * 69 | * Keep in mind that "basic" is automatically supported for the time being 70 | */ 71 | add_filter('wo_scopes', 'wo_default_scopes'); 72 | function wo_default_scopes () { 73 | $scopes = array( 74 | 'openid', 75 | 'profile', 76 | 'email' 77 | ); 78 | return $scopes; 79 | } 80 | 81 | /** 82 | * DEFAULT DESTROY METHOD 83 | * This method has been added to help secure installs that want to manually destroy sessions (valid access tokens). 84 | * @since 3.1.5 85 | */ 86 | function _wo_method_destroy ( $token = null ) { 87 | $access_token = &$token['access_token']; 88 | 89 | global $wpdb; 90 | $stmt = $wpdb->delete("{$wpdb->prefix}oauth_access_tokens", array( 'access_token' => $access_token ) ); 91 | 92 | /** If there is a refresh token we need to remove it as well. */ 93 | if( ! empty( $_REQUEST[ 'refresh_token' ] ) ) 94 | $stmt = $wpdb->delete("{$wpdb->prefix}oauth_refresh_tokens", array( 'refresh_token' => $_REQUEST['refresh_token'] ) ); 95 | 96 | /** Prepare the return */ 97 | $response = new OAuth2\Response( array( 98 | 'status' => true, 99 | 'description' => 'Session destroyed successfully' 100 | ) ); 101 | $response->send(); 102 | exit; 103 | } 104 | 105 | /** 106 | * DEFAULT ME METHOD - DO NOT REMOVE DIRECTLY 107 | * This is the default resource call "/oauth/me". Do not edit or remove. 108 | */ 109 | function _wo_method_me ( $token = null ) { 110 | if ( ! isset( $token['user_id'] ) || $token['user_id'] == 0 ) { 111 | $response = new OAuth2\Response(); 112 | $response->setError( 400, 'invalid_request', 'Missing or invalid access token' ); 113 | $response->send(); 114 | exit; 115 | } 116 | 117 | $user = get_user_by( 'id', $token['user_id'] ); 118 | $me_data = (array) $user->data; 119 | 120 | unset( $me_data['user_pass'] ); 121 | unset( $me_data['user_activation_key'] ); 122 | unset( $me_data['user_url'] ); 123 | 124 | /** 125 | * @since 3.0.5 126 | * OpenID Connect looks for the field "email". 127 | * Sooooo. We shall provide it. (at least for Moodle) 128 | */ 129 | $me_data['email'] = $me_data['user_email']; 130 | 131 | $response = new OAuth2\Response( $me_data ); 132 | $response->send(); 133 | exit; 134 | } -------------------------------------------------------------------------------- /includes/functions.php: -------------------------------------------------------------------------------- 1 | 6 | * @package WordPress OAuth Server 7 | * 8 | * @todo Convert entire file into library ( class ) 9 | */ 10 | 11 | // Hook into core filters 12 | require_once dirname(__FILE__) . '/filters.php' ; 13 | 14 | // Hook into core actions 15 | require_once( dirname(__FILE__) . '/actions.php' ); 16 | 17 | /** 18 | * Generates a 40 Character key is generated by default but should be adjustable in the admin 19 | * @return [type] [description] 20 | * 21 | * @todo Allow more characters to be added to the character list to provide complex keys 22 | */ 23 | function wo_gen_key( $length = 40 ) { 24 | 25 | // Gather the settings 26 | $options = get_option("wo_options"); 27 | $user_defined_length = (int) $options["client_id_length"]; 28 | 29 | // If user setting is larger than 0, then define it 30 | if ( $user_defined_length > 0 ) { 31 | $length = $user_defined_length; 32 | } 33 | 34 | $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 35 | $randomString = ''; 36 | 37 | for ($i = 0; $i < $length; $i++) { 38 | $randomString .= $characters[rand(0, strlen($characters) - 1)]; 39 | } 40 | 41 | return $randomString; 42 | } 43 | 44 | /** 45 | * Blowfish Encryptions 46 | * @param [type] $input [description] 47 | * @param integer $rounds [description] 48 | * @return [type] [description] 49 | * 50 | * REQUIRES ATLEAST 5.3.x 51 | */ 52 | function wo_crypt($input, $rounds = 7) { 53 | $salt = ""; 54 | $salt_chars = array_merge(range('A', 'Z'), range('a', 'z'), range(0, 9)); 55 | for ($i = 0; $i < 22; $i++) { 56 | $salt .= $salt_chars[array_rand($salt_chars)]; 57 | } 58 | return crypt($input, sprintf('$2a$%02d$', $rounds) . $salt); 59 | } 60 | 61 | /** 62 | * Check if there is more than one client in the system 63 | * 64 | * @return boolean [description] 65 | */ 66 | function has_a_client (){ 67 | global $wpdb; 68 | $count = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}oauth_clients"); 69 | //print_r($count); 70 | if (count($count) >= 1) 71 | return true; 72 | } 73 | 74 | /** 75 | * Get the client IP multiple ways since REMOTE_ADDR is not always the best way to do so 76 | * @return [type] [description] 77 | */ 78 | function client_ip(){ 79 | $ipaddress = ''; 80 | if (getenv('HTTP_CLIENT_IP')) 81 | $ipaddress = getenv('HTTP_CLIENT_IP'); 82 | else if(getenv('HTTP_X_FORWARDED_FOR')) 83 | $ipaddress = getenv('HTTP_X_FORWARDED_FOR'); 84 | else if(getenv('HTTP_X_FORWARDED')) 85 | $ipaddress = getenv('HTTP_X_FORWARDED'); 86 | else if(getenv('HTTP_FORWARDED_FOR')) 87 | $ipaddress = getenv('HTTP_FORWARDED_FOR'); 88 | else if(getenv('HTTP_FORWARDED')) 89 | $ipaddress = getenv('HTTP_FORWARDED'); 90 | else if(getenv('REMOTE_ADDR')) 91 | $ipaddress = getenv('REMOTE_ADDR'); 92 | else 93 | $ipaddress = 'UNKNOWN'; 94 | 95 | return $ipaddress; 96 | } 97 | 98 | /** 99 | * Valid the license 100 | * @param [type] $l [description] 101 | * @return [type] [description] 102 | */ 103 | /* 104 | function _vl() { 105 | $options = get_option('wo_options'); 106 | return @$options['license_status'] == 'valid' ? true : false; 107 | } 108 | */ 109 | 110 | /* 111 | function license_status (){ 112 | $options = get_option('wo_options'); 113 | $status = isset($options['license_status']) ? $options['license_status'] : ''; 114 | switch($status){ 115 | case 'invalid': 116 | echo 'Invalid. Activate your license now.'; 117 | break; 118 | case 'valid': 119 | echo 'Valid'; 120 | break; 121 | } 122 | 123 | } 124 | */ 125 | 126 | /** 127 | * Cron Tasks That the plugin should run daily 128 | * @return [type] [description] 129 | */ 130 | /* 131 | add_action( 'wo_daily_tasks_hook', 'wo_daily_tasks' ); 132 | function wo_daily_tasks () { 133 | $options = get_option( 'wo_options' ); 134 | if( @$options['license_status'] == 'valid' ){ 135 | $api_params = array( 136 | 'edd_action'=> 'activate_license', 137 | 'license' => $options['license'], 138 | 'item_name' => urlencode('WP OAuth License'), 139 | 'url' => home_url() 140 | ); 141 | $response = wp_remote_get( add_query_arg( $api_params, 'https://wp-oauth.com' ) ); 142 | if ( !is_wp_error( $response ) ){ 143 | $license_data = json_decode( wp_remote_retrieve_body( $response ) ); 144 | if(@$options['license_status'] == 'valid' && $license_data->license != 'valid') { 145 | wp_mail( get_option('admin_email'), 'Issues found with WP OAuth Server', 'Recent checks show that your license key status for WordPress OAuth Server has been changed.'); 146 | $options['license'] = ''; 147 | $options['license_status'] = ''; 148 | } 149 | update_option('wo_options', $options); 150 | } 151 | } 152 | } 153 | */ 154 | 155 | /** 156 | * WordPress OAuth Server Firewall 157 | * Called if and license is valid and the firewall is enabled 158 | */ 159 | add_action('wo_before_api', 'wordpress_oauth_firewall_init', 10); 160 | function wordpress_oauth_firewall_init() { 161 | $options = get_option('wo_options'); 162 | 163 | if(isset($options['firewall_block_all_incomming']) && $options['firewall_block_all_incomming']){ 164 | $remote_addr = client_ip(); 165 | $whitelist = str_replace(' ', '',$options['firewall_ip_whitelist']); // remove all whitespace 166 | $whitelist_array = explode(',', $whitelist); 167 | if(in_array($remote_addr, $whitelist_array)) 168 | return; 169 | 170 | header('Content-Type: application/json'); 171 | $response = array( 172 | 'error' => 'Unauthorized' 173 | ); 174 | print json_encode($response); 175 | exit; 176 | } 177 | } 178 | 179 | /** 180 | * Return the private key for signing 181 | * @since 3.0.5 182 | * @return [type] [description] 183 | */ 184 | function get_private_server_key () { 185 | $keys = apply_filters('wo_server_keys', null); 186 | return file_get_contents( $keys['private'] ); 187 | } 188 | 189 | /** 190 | * Returns the public key 191 | * @return [type] [description] 192 | * @since 3.1.0 193 | */ 194 | function get_public_server_key () { 195 | $keys = apply_filters('wo_server_keys', null); 196 | return file_get_contents( $keys['public'] ); 197 | } 198 | 199 | /** 200 | * Returns the set ALGO that is to be used for the server to encode 201 | * 202 | * @todo Possibly set this to be adjusted somewhere. The it_token calls for it to be set by each 203 | * client as a pref but we need to keep this simple. 204 | * 205 | * @since 3.1.93 206 | * @return String Type of algorithm used for encoding and decoding. 207 | */ 208 | function wo_get_algorithm (){ 209 | return 'RS256'; 210 | } 211 | 212 | /** 213 | * Check to see if there is certificates that have been generated 214 | * @return boolean [description] 215 | */ 216 | function wo_has_certificates (){ 217 | return file_exists( dirname(WPOAUTH_FILE) . '/library/keys/public_key.pem' ) 218 | && file_exists( dirname(WPOAUTH_FILE) . '/library/keys/private_key.pem' ); 219 | } 220 | 221 | /** 222 | * [wo_create_client description] 223 | * @param [type] $user [description] 224 | * @return [type] [description] 225 | * 226 | * @todo Add role and permissions check 227 | */ 228 | 229 | add_action('wp_ajax_use-auth', function(){ 230 | 231 | check_ajax_referer('wp_oauth', 'wpOauthSec'); 232 | //dbug($_POST); 233 | 234 | global $wpdb; 235 | 236 | $update_client = $wpdb->update("{$wpdb->prefix}oauth_clients", 237 | array( 238 | 'use_auth' => (int)$_POST['use_auth'] 239 | ), 240 | array( 241 | 'client_id' => sanitize_text_field( $_POST['client_id'] ) 242 | )); 243 | 244 | ob_start(); 245 | echo json_encode(array('status'=>$update_client)); 246 | header('Content-type: application/json'); 247 | ob_end_flush(); 248 | 249 | exit(); 250 | }); 251 | 252 | function wo_create_client( $user=null ){ 253 | 254 | do_action('wo_before_create_client', array( $user ) ); 255 | if(! current_user_can( 'manage_options' ) ) 256 | return false; 257 | 258 | $new_client_id = wo_gen_key(); 259 | $new_client_secret = wo_gen_key(); 260 | 261 | $slug = empty($user['client-name']) ? 'No Name '.rand() : $user['client-name']; 262 | 263 | // Insert the user into the system 264 | global $wpdb; 265 | return $wpdb->insert("{$wpdb->prefix}oauth_clients", 266 | array( 267 | 'client_id' => $new_client_id, 268 | 'client_secret' => $new_client_secret, 269 | 'redirect_uri' => empty($user['client-redirect-uri']) ? '' : $user['client-redirect-uri'], 270 | 'name' => empty($user['client-name']) ? 'No Name' : $user['client-name'], 271 | 'slug' => str_replace('-','_',sanitize_title($slug)), 272 | 'description' => empty($user['client-description']) ? '' : $user['client-description'], 273 | 'use_auth' => 1 274 | )); 275 | } -------------------------------------------------------------------------------- /library/OAuth2/Autoloader.php: -------------------------------------------------------------------------------- 1 | 9 | * @license MIT License 10 | */ 11 | class Autoloader 12 | { 13 | private $dir; 14 | 15 | public function __construct($dir = null) 16 | { 17 | if (is_null($dir)) { 18 | $dir = dirname(__FILE__).'/..'; 19 | } 20 | $this->dir = $dir; 21 | } 22 | /** 23 | * Registers OAuth2\Autoloader as an SPL autoloader. 24 | */ 25 | public static function register($dir = null) 26 | { 27 | ini_set('unserialize_callback_func', 'spl_autoload_call'); 28 | spl_autoload_register(array(new self($dir), 'autoload')); 29 | } 30 | 31 | /** 32 | * Handles autoloading of classes. 33 | * 34 | * @param string $class A class name. 35 | * 36 | * @return boolean Returns true if the class has been loaded 37 | */ 38 | public function autoload($class) 39 | { 40 | if (0 !== strpos($class, 'OAuth2')) { 41 | return; 42 | } 43 | 44 | if (file_exists($file = $this->dir.'/'.str_replace('\\', '/', $class).'.php')) { 45 | require $file; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /library/OAuth2/ClientAssertionType/ClientAssertionTypeInterface.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class HttpBasic implements ClientAssertionTypeInterface 15 | { 16 | private $clientData; 17 | 18 | protected $storage; 19 | protected $config; 20 | 21 | /** 22 | * @param OAuth2\Storage\ClientCredentialsInterface $clientStorage REQUIRED Storage class for retrieving client credentials information 23 | * @param array $config OPTIONAL Configuration options for the server 24 | * 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 |
156 | Edit | client_id).'\');" href="#">Delete | Show Secret | Show Client Slug
'; 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 |
103 |

Create Client

104 |

105 | Create a new client using the form below: 106 |

107 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 118 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 133 | 134 |
112 | 113 |

114 |
119 | 120 |

121 |
129 | 130 | 131 | 132 |
135 |
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 |
129 |

Edit Client

130 |

131 | Edit the client information below: 132 |

133 | 134 | 135 | 136 | 137 | 141 | 142 | 143 | 144 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 159 | 160 |
138 | 139 |

140 |
145 | 146 |

147 |
155 | 156 | 157 | 158 |
161 |
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 | --------------------------------------------------------------------------------