diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6049696 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +vendor/ +.DS_Store +composer.lock +phpdoc.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d12a553 --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "authlete/authlete-laravel", + "type": "library", + "license": "Apache-2.0", + "description": "Authlete Library for Laravel", + "keywords": [ + "Authlete", "Laravel", + "OAuth", "OAuth 2.0", + "OpenID", "OpenID Connect", "OIDC", + "API", "Security", "SSO" + ], + "homepage": "https://github.com/authlete/authlete-php-laravel", + "require": { + "authlete/authlete": ">=1.0.0", + "laravel/framework": ">=5" + }, + "autoload": { + "psr-4": { + "Authlete\\Laravel\\": "src/" + } + }, + "require-dev": { + "phpunit/phpunit": "^6" + }, + "extra": { + "laravel": { + "providers": [ + "Authlete\\Laravel\\Provider\\AuthleteServiceProvider" + ] + } + } +} diff --git a/config/authlete.php b/config/authlete.php new file mode 100644 index 0000000..bbaa73c --- /dev/null +++ b/config/authlete.php @@ -0,0 +1,9 @@ + 'https://api.authlete.com', + 'service_owner.api_key' => '', + 'service_owner.api_secret' => '', + 'service.api_key' => '', + 'service.api_secret' => '' +]; +?> diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml new file mode 100644 index 0000000..d5cd5ac --- /dev/null +++ b/phpdoc.dist.xml @@ -0,0 +1,14 @@ + + + + docs + public,protected + + + docs + + + src + + + diff --git a/src/Laravel/Conf/AuthleteLaravelConfiguration.php b/src/Laravel/Conf/AuthleteLaravelConfiguration.php new file mode 100644 index 0000000..f54d7c0 --- /dev/null +++ b/src/Laravel/Conf/AuthleteLaravelConfiguration.php @@ -0,0 +1,97 @@ + 'https://api.authlete.com', + * 'service_owner.api_key' => '', + * 'service_owner.api_secret' => '', + * 'service.api_key' => '', + * 'service.api_secret' => '' + * ]; + * ?> + * ``` + * + * Of course, values of the configuration parameters need to + * replaced with your own values. + */ +class AuthleteLaravelConfiguration implements AuthleteConfiguration +{ + use AuthleteConfigurationTrait; + + + private static $CONFIGURATION_BASE_NAME = 'authlete'; + private static $KEY_BASE_URL = 'base_url'; + private static $KEY_SERVICE_OWNER_API_KEY = 'service_owner.api_key'; + private static $KEY_SERVICE_OWNER_API_SECRET = 'service_owner.api_secret'; + private static $KEY_SERVICE_API_KEY = 'service.api_key'; + private static $KEY_SERVICE_API_SECRET = 'service.api_secret'; + private static $DEFAULT_BASE_URL = 'https://api.authlete.com'; + + + /** + * Constructor which refers to 'config/authlete.php' and sets up + * the corresponding properties. + */ + public function __construct() + { + $this->baseUrl = get($KEY_BASE_URL); + $this->serviceOwnerApiKey = get($KEY_SERVICE_OWNER_API_KEY); + $this->serviceOwnerApiSecret = get($KEY_SERVICE_OWNER_API_SECRET); + $this->serviceApiKey = get($KEY_SERVICE_API_KEY); + $this->serviceApiSecret = get($KEY_SERVICE_API_SECRET); + + // If the value of 'base_url' is not available. + if (is_null($this->baseUrl) || empty($this->baseUrl)) + { + // Use the default value for 'base_url'. + $this->baseUrl = $DEFAULT_BASE_URL; + } + } + + + /** + * Get the value of the configuration parameter which is identified by the key. + */ + private function get($key) + { + return config("${CONFIGURATION_BASE_NAME}.${key}"); + } +} +?> diff --git a/src/Laravel/Controller/ConfigurationController.php b/src/Laravel/Controller/ConfigurationController.php new file mode 100644 index 0000000..792af0c --- /dev/null +++ b/src/Laravel/Controller/ConfigurationController.php @@ -0,0 +1,72 @@ +handle(); + } +} +?> \ No newline at end of file diff --git a/src/Laravel/Handler/BaseRequestHandler.php b/src/Laravel/Handler/BaseRequestHandler.php new file mode 100644 index 0000000..ef19c52 --- /dev/null +++ b/src/Laravel/Handler/BaseRequestHandler.php @@ -0,0 +1,94 @@ +api = $api; + } + + + /** + * Get the implementation of the AuthleteApi interface. + * + * The value returned from this method is the instance that was given to + * the constructor. + * + * @return AuthleteApi + * An implementation of the `AuthleteApi` interface. + */ + public function getApi() + { + return $this->api; + } + + + /** + * A utility method to generate a Response instance with + * "500 Internal Server Error" and an error message in JSON. + * + * This method is expected to be used when the value of the `action` + * parameter in a response from an Authlete API holds an unexpected + * value. + * + * @param string $apiPath + * The path of an Authlete API. + * + * @return Response + * A Response instahce which represents a server error. + */ + protected function unknownAction($apiPath) + { + $content = "{{\"error\":\"Authlete's '" . $apiPath . "' API returned an unknown action.\"}}"; + + return ResponseUtility::internalServerError($content); + } +} +?> diff --git a/src/Laravel/Handler/ConfigurationRequestHandler.php b/src/Laravel/Handler/ConfigurationRequestHandler.php new file mode 100644 index 0000000..f85e350 --- /dev/null +++ b/src/Laravel/Handler/ConfigurationRequestHandler.php @@ -0,0 +1,94 @@ +getApi()->getServiceConfiguration($pretty); + + // 200 OK, application/json;charset=UTF-8 + return ResponseUtility::okJson($json); + } +} +?> diff --git a/src/Laravel/Provider/AuthleteServiceProvider.php b/src/Laravel/Provider/AuthleteServiceProvider.php new file mode 100644 index 0000000..179a4c3 --- /dev/null +++ b/src/Laravel/Provider/AuthleteServiceProvider.php @@ -0,0 +1,103 @@ + [ + * // Other Service Providers + * + * Authlete\Laravel\Provider\AuthleteServiceProvider::class, + * ], + * ``` + * + * The singleton instance refers to `config/authlete.php` on its creation. + * The `boot()` method of this service provider publishes 'authlete.php'. + * Parameters in the configuration file can be found in the description of + * the `AuthleteLaravelConfiguration` class. + * + * If the version of Laravel is 5.5 or higher, this service provider will + * be automatically detected thanks to the feature of 'auto-discovery'. + * Therefore, you don't have to modify `config/app.php` to add this service + * provider manually. + */ +class AuthleteServiceProvider extends ServiceProvider +{ + /** + * Register a singleton instance for the AuthleteApi interface. + */ + public function register() + { + // Register an instance that implements the AuthleteApi interface. + $this->app->singleton( + \Authlete\Api\AuthleteApi::class, + function () { return instantiateAuthleteApi(); } + ); + } + + + /** + * Create an instance which implements the AuthleteApi interface. + */ + private function instantiateAuthleteApi() + { + // Create an instance of AuthleteApiImp which implements AuthleteApi. + // AuthleteLaravelConfiguration refers to 'config/authlete.php' to + // find parameters which are necessary to access Authlete APIs. + return new AuthleteApiImpl( + new AuthleteLaravelConfiguration() + ); + } + + + /** + * Publish 'authlete.php' when 'php artisan vendor:publish' is executed. + */ + public function boot() + { + // 'authlete.php', a configuration file which holds parameters to + // access Authlete APIs. AuthleteLaravelConfiguration class refers + // to the configuration file. + $this->publishes([ + __DIR__ . '/config/authlete.php' => config_path('authlete.php') + ]); + } +} +?> diff --git a/src/Laravel/Web/ResponseUtility.php b/src/Laravel/Web/ResponseUtility.php new file mode 100644 index 0000000..095400f --- /dev/null +++ b/src/Laravel/Web/ResponseUtility.php @@ -0,0 +1,351 @@ +headers->set('Location', $location); + + return $response; + } + + + /** + * Create a response having the given content formatted in + * "application/json;charset=UTF-8" with the HTTP status code + * "400 Bad Request". + * + * @param string $content + * The content formatted in `application/json;charset=UTF-8`. + * + * @return Response + * An HTTP response with "400 Bad Request" and JSON. + */ + public static function badRequest($content) + { + // 400 Bad Request, application/json;charset=UTF-8 + return self::buildResponseJson(Response::HTTP_BAD_REQUEST, $content); + } + + + /** + * Create a response with the HTTP status code "401 Unauthorized" + * and optionally with JSON. + * + * @param string $challenge + * The value of the `WWW-Authenticate` header. + * + * @param $content + * The content formatted in `application/json;charset=UTF-8'. + * This parameter is optional. + * + * @return Response + * An HTTP response with "401 Unauthorized" and optionally with JSON. + */ + public static function unauthorized($challenge, $content = null) + { + $response = null; + + if (is_null($content)) + { + // 401 Unauthorized + $response = self::buildResponseBase(Response::HTTP_UNAUTHORIZED); + } + else + { + // 401 Unauthorized with JSON + $response = self::buildResponseJson(Response::HTTP_UNAUTHORIZED, $content); + } + + // WWW-Authenticate: $challenge + $response->headers->set('WWW-Authenticate', $challenge); + + return $response; + } + + + /** + * Create a response having the given content formatted in + * "application/json;charset=UTF-8" with the HTTP status code + * "403 Forbidden". + * + * @param string $content + * The content formatted in `application/json;charset=UTF-8`. + * + * @return Response + * An HTTP response with "403 Forbidden" and JSON. + */ + public static function forbidden($content) + { + // 403 Forbidden, application/json;charset=UTF-8 + return self::buildResponseJson(Response::HTTP_FORBIDDEN, $content); + } + + + /** + * Create a response having the given content formatted in + * "application/json;charset=UTF-8" with the HTTP status code + * "404 Not Found". + * + * @param string $content + * The content formatted in `application/json;charset=UTF-8`. + * + * @return Response + * An HTTP response with "404 Not Found" and JSON. + */ + public static function notFound($content) + { + // 404 Not Found, application/json;charset=UTF-8 + return self::buildResponseJson(Response::HTTP_NOT_FOUND, $content); + } + + + /** + * Create a response having the given content formatted in + * "application/json;charset=UTF-8" with the HTTP status code + * "500 Internal Server Error". + * + * @param string $content + * The content formatted in `application/json;charset=UTF-8`. + * + * @return Response + * An HTTP response with "500 Internal Server Error" and JSON. + */ + public static function internalServerError($content) + { + // 500 Internal Server Error, application/json;charset=UTF-8 + return self::buildResponseJson(Response::HTTP_INTERNAL_SERVER_ERROR, $content); + } + + + /** + * Build a response which has the specified HTTP status code with + * "Cache-Control: no-store" header and "Pragma: no-cache" header. + * + * @param integer $statusCode + * HTTP status code. + * + * @param string $content + * Response body. + * + * @param string $contentType + * The value of the `Content-Type` header. + * + * @param string $charset + * The value of the character set of the response. + * + * @return Response + */ + private static function buildResponseBase( + $statusCode, $content = null, $contentType = null, $charset = null) + { + $response = new Response(); + + // HTTP status code. + $response->setStatusCode($statusCode); + + // Cache-Control: no-store + $response->headers->set('Cache-Control', 'no-store'); + + // Pragma: no-cache + $response->headers->set('Pragma', 'no-cache'); + + // Content + if (!is_null($content)) + { + $response->setContent($content); + } + + // Content-Type: $contentType + if (!is_null($contentType)) + { + $response->headers->set('Content-Type', $contentType); + } + + // Charset + if (!is_null($charset)) + { + $response->setCharset($charset); + } + + return $response; + } + + + /** + * Build a response whose Content-Type is 'application/json'. + */ + private static function buildResponseJson($statusCode, $content, $charset = 'UTF-8') + { + return self::buildResponseBase( + $statusCode, $content, 'application/json', $charset); + } + + + /** + * Build a response whose Content-Type is 'application/javascript'. + */ + private static function buildResponseJavaScript($statusCode, $content, $charset = 'UTF-8') + { + return self::buildResponseBase( + $statusCode, $content, 'application/javascript', $charset); + + } + + + /** + * Build a response whose Content-Type is 'application/jwt'. + */ + private static function buildResponseJwt($statusCode, $content, $charset = null) + { + return self::buildResponseBase( + $statusCode, $content, 'application/jwt', $charset); + + } + + + /** + * Build a response whose Content-Type is 'text/html'. + */ + private static function buildResponseHtml($statusCode, $content, $charset = 'UTF-8') + { + return self::buildResponseBase( + $statusCode, $content, 'text/html', $charset); + } +} +?>