diff options
author | François Kooman <fkooman@tuxed.net> | 2013-07-05 15:23:53 +0400 |
---|---|---|
committer | François Kooman <fkooman@tuxed.net> | 2013-07-05 15:23:53 +0400 |
commit | 5a5d9072c5b00d5d62f30090feec97e87746916a (patch) | |
tree | 3947bb2d19f54376a7a82d8b4c04e7103e5af5ae /user_oauth | |
parent | ee3c7760a9571041e250dc8e4ec49f8b59b33e34 (diff) |
update user_oauth to support token introspection (draft-richer-oauth-introspection) and use new php-oauth-lib-rs + API
Diffstat (limited to 'user_oauth')
23 files changed, 2303 insertions, 523 deletions
diff --git a/user_oauth/3rdparty/fetch_3rdparty_libs.sh b/user_oauth/3rdparty/fetch_3rdparty_libs.sh index 743755a4e..5b97c8033 100644 --- a/user_oauth/3rdparty/fetch_3rdparty_libs.sh +++ b/user_oauth/3rdparty/fetch_3rdparty_libs.sh @@ -1,4 +1,4 @@ #!/bin/sh -rm -rf php-lib-remote-rs/ -git clone https://github.com/fkooman/php-lib-remote-rs.git -rm -rf php-lib-remote-rs/.git +rm -rf php-oauth-lib-rs/ +git clone https://github.com/fkooman/php-oauth-lib-rs.git +rm -rf php-oauth-lib-rs/.git diff --git a/user_oauth/3rdparty/php-lib-remote-rs/README.md b/user_oauth/3rdparty/php-lib-remote-rs/README.md deleted file mode 100644 index 0e9d042e4..000000000 --- a/user_oauth/3rdparty/php-lib-remote-rs/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Introduction -This is a library to implement an OAuth 2.0 resource server (RS). The library -can be used by any service that wants to accept OAuth 2.0 bearer tokens. - -It is compatible with and was tested with -[php-oauth](https://github.com/fkooman/php-oauth), and should work with Google. - -# License -Licensed under the Apache License, Version 2.0; - - http://www.apache.org/licenses/LICENSE-2.0 - -# API -Using the library is straightforward: - - <?php - require_once 'extlib/php-lib-remote-rs/lib/OAuth/RemoteResourceServer.php'; - - use \OAuth\RemoteResourceServer as RemoteResourceServer; - - $config = array( - "tokenInfoEndpoint" => "http://localhost/php-oauth/tokeninfo.php", - "resourceServerRealm" => "My Demo Service", - "throwException" => FALSE - ); - - $rs = new RemoteResourceServer($config); - $rs->verifyRequest(); - -Onlt the `tokenInfoEndpoint` configuration parameter is required, the others -are optional: - -* `tokenInfoEndpoint` - specify the location at which to verify the OAuth token; -* `resourceServerRealm` - specify the "realm" of the RS that is used when - returning errors to the client using the `WWW-Authenticate` header; -* `throwException` - throw a `RemoteResourceServerException` instead of handling - the failure in the library by sending a response back to the client. This is - useful if you want to integrate the library in your own framework, you can - use the information from the exception to craft your own response. - -After the `verifyRequest()` some methods are available to retrieve information -about the resource owner and client. - -* `getResourceOwnerId()` (the unique resource owner identifier) -* `getAttributes()` (additional attributes associated with the resource owner) -* `getScope()` (the scope granted to the client accessing this resource) -* `getEntitlement()` (the entitlement the resource owner has when accessing this - resource) diff --git a/user_oauth/3rdparty/php-lib-remote-rs/lib/OAuth/RemoteResourceServer.php b/user_oauth/3rdparty/php-lib-remote-rs/lib/OAuth/RemoteResourceServer.php deleted file mode 100644 index f33d5ad9d..000000000 --- a/user_oauth/3rdparty/php-lib-remote-rs/lib/OAuth/RemoteResourceServer.php +++ /dev/null @@ -1,390 +0,0 @@ -<?php - -/** - * Copyright 2012 François Kooman <fkooman@tuxed.net> - * - * 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. - */ - -namespace OAuth; - -class RemoteResourceServer -{ - private $_config; - - private $_grantedScope; - private $_resourceOwnerId; - private $_resourceOwnerAttributes; - private $_isVerified; - - public function __construct(array $c) - { - $this->_config = $c; - - $this->_resourceOwnerId = NULL; - $this->_grantedScope = NULL; - $this->_resourceOwnerAttributes = NULL; - $this->_isVerified = FALSE; - } - - /** - * Verify the Authorization Bearer token. - * - * Note: this only works on Apache as the PHP function - * "apache_request_headers" is used. On other web servers, or when using - * a framework, please use the verifyAuthorizationHeader function instead - * where you can directly specify the contents of the Authorization header. - */ - public function verifyRequest() - { - $apacheHeaders = apache_request_headers(); - $headerKeys = array_keys($apacheHeaders); - $keyPositionInArray = array_search(strtolower("Authorization"), array_map('strtolower', $headerKeys)); - $authorizationHeader = (FALSE !== $keyPositionInArray) ? $apacheHeaders[$headerKeys[$keyPositionInArray]] : NULL; - $this->verifyAuthorization($authorizationHeader, $_GET); - } - - public function verifyAuthorization($authorizationHeader = NULL, array $queryParameters = NULL) - { - // FIXME: only one authorization mechanism should be allowed - if (NULL !== $authorizationHeader) { - $this->verifyAuthorizationHeader($authorizationHeader); - - return; - } - if (array_key_exists('access_token', $queryParameters)) { - $this->verifyQueryParameter($queryParameters); - - return; - } - $this->_handleException("no_token", "no access token provided"); - } - - public function verifyQueryParameter(array $queryParameters) - { - if (!array_key_exists('access_token', $queryParameters)) { - $this->_handleException("no_token", "no access token in query parameter"); - } - $this->verifyBearerToken($queryParameters['access_token']); - } - - /** - * Verify the Authorization Bearer token. - * - * @param $authorizationHeader The actual content of the Authorization - * header, e.g.: "Bearer abcdef" - */ - public function verifyAuthorizationHeader($authorizationHeader) - { - if (NULL === $authorizationHeader) { - $this->_handleException("no_token", "no authorization header"); - } - // b64token = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" - $b64TokenRegExp = '(?:[[:alpha:][:digit:]-._~+/]+=*)'; - $result = preg_match('|^Bearer (?P<value>' . $b64TokenRegExp . ')$|', $authorizationHeader, $matches); - if ($result === FALSE || $result === 0) { - $this->_handleException("invalid_token", "the access token is malformed"); - } - $accessToken = $matches['value']; - $this->verifyBearerToken($accessToken); - } - - public function verifyBearerToken($accessToken) - { - $getParameters = array(); - $getParameters["access_token"] = $accessToken; - - $curlChannel = curl_init(); - - $tokenInfoUrl = $this->_getRequiredConfigParameter("tokenInfoEndpoint"); - if (0 !== strpos($tokenInfoUrl, "file://")) { - $separator = (FALSE === strpos($tokenInfoUrl, "?")) ? "?" : "&"; - $tokenInfoUrl .= $separator . http_build_query($getParameters); - } else { - // file cannot have query parameter, use accesstoken as file instead - $tokenInfoUrl .= $accessToken . ".json"; - } - curl_setopt_array($curlChannel, array ( - CURLOPT_URL => $tokenInfoUrl, - //CURLOPT_FOLLOWLOCATION => 1, - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_SSL_VERIFYPEER => 1, - CURLOPT_SSL_VERIFYHOST => 2, - )); - - $output = curl_exec($curlChannel); - - if (FALSE === $output) { - $error = curl_error($curlChannel); - $this->_handleException("internal_server_error", "cURL error while talking to tokenInfoEndpoint: $error"); - } - - $httpCode = curl_getinfo($curlChannel, CURLINFO_HTTP_CODE); - curl_close($curlChannel); - - if (0 !== strpos($tokenInfoUrl, "file://")) { - // not a file - if (200 !== $httpCode) { - $this->_handleException("invalid_token", "the access token is not valid"); - } - } - - $token = json_decode($output, TRUE); - if (NULL === $token) { - $this->_handleException("internal_server_error", "unable to decode the token response from authorization server"); - } - - if ($token['expires_in'] < 0) { - $this->_handleException("invalid_token", "the access token expired"); - } - - $this->_resourceOwnerId = $token['user_id']; - $this->_grantedScope = $token['scope']; - $this->_resourceOwnerAttributes = $token['attributes']; - - $this->_isVerified = TRUE; - } - - public function getResourceOwnerId() - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - - return $this->_resourceOwnerId; - } - - public function getScope() - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - if (NULL === $this->_grantedScope) { - return array(); - } - - return explode(" ", $this->_grantedScope); - } - - public function getEntitlement() - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - if (!array_key_exists('eduPersonEntitlement', $this->_resourceOwnerAttributes)) { - return array(); - } - - return $this->_resourceOwnerAttributes['eduPersonEntitlement']; - } - - public function hasScope($scope) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - if (NULL === $this->_grantedScope) { - return FALSE; - } - $grantedScope = explode(" ", $this->_grantedScope); - if (in_array($scope, $grantedScope)) { - return TRUE; - } - - return FALSE; - } - - public function requireScope($scope) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - if (FALSE === $this->hasScope($scope)) { - $this->_handleException("insufficient_scope", "no permission for this call with granted scope"); - } - } - - /** - * At least one of the scopes should be granted - * - * @param array $scope the list of scopes of which one - * should be granted - * @throws RemoteResourceServerException if not at least one of the provided - * scopes was granted - */ - public function requireAnyScope(array $scope) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - foreach ($scope as $s) { - if (TRUE === $this->hasScope($s)) { - return; - } - } - $this->_handleException("insufficient_scope", "no permission for this call with granted scope"); - } - - public function hasEntitlement($entitlement) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - if (!array_key_exists('eduPersonEntitlement', $this->_resourceOwnerAttributes)) { - return FALSE; - } - - return in_array($entitlement, $this->_resourceOwnerAttributes['eduPersonEntitlement']); - } - - public function requireEntitlement($entitlement) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - - if (FALSE === $this->hasEntitlement($entitlement)) { - $this->_handleException("insufficient_entitlement", "no permission for this call with granted entitlement"); - } - } - - public function getAttributes() - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - - return $this->_resourceOwnerAttributes; - } - - public function getAttribute($key) - { - if (!$this->_isVerified) { - $this->_handleException("internal_server_error", "verify method needs to be requested first"); - } - $attributes = $this->getAttributes(); - - return array_key_exists($key, $attributes) ? $attributes[$key] : NULL; - } - - private function _getRequiredConfigParameter($key) - { - if (!array_key_exists($key, $this->_config)) { - $this->_handleException("internal_server_error", "no config parameter '$key'"); - } - - return $this->_config[$key]; - } - - private function _handleException($message, $description) - { - switch ($message) { - case "no_token": - case "invalid_token": - $responseCode = 401; - break; - case "insufficient_scope": - case "insufficient_entitlement": - $responseCode = 403; - break; - case "internal_server_error": - $responseCode = 500; - break; - case "invalid_request": - default: - $responseCode = 400; - break; - } - - $resourceServerRealm = array_key_exists("resourceServerRealm", $this->_config) ? $this->_config["resourceServerRealm"] : "Resource Server"; - - $content = json_encode(array("error" => $message, "error_description" => $description)); - $authenticateHeader = NULL; - - if (500 !== $responseCode) { - if ("no_token" === $message) { - // no authorization header is a special case, the client did not know - // authentication was required, so tell it now without giving error message - $authenticateHeader = 'Bearer realm="' . $resourceServerRealm . '"'; - } else { - $authenticateHeader = sprintf('Bearer realm="' . $resourceServerRealm . '",error="%s",error_description="%s"', $message, $description); - } - } - - // we can either throw an exception in case a framework is used, or just - // directly handle the response to the client ourselves... - if (array_key_exists("throwException", $this->_config) && $this->_config["throwException"]) { - $e = new RemoteResourceServerException($message, $description); - $e->setResponseCode($responseCode); - $e->setAuthenticateHeader($authenticateHeader); - $e->setContent($content); - throw $e; - } else { - header("HTTP/1.1 " . $responseCode); - header("WWW-Authenticate: " . $authenticateHeader); - header("Content-Type: application/json"); - die($content); - } - } - -} - -class RemoteResourceServerException extends \Exception -{ - private $_description; - private $_responseCode; - private $_authenticateHeader; - private $_content; - - public function __construct($message, $description, $code = 0, Exception $previous = null) - { - $this->_description = $description; - parent::__construct($message, $code, $previous); - } - - public function getDescription() - { - return $this->_description; - } - - public function setResponseCode($responseCode) - { - $this->_responseCode = $responseCode; - } - - public function getResponseCode() - { - return $this->_responseCode; - } - - public function setAuthenticateHeader($authenticateHeader) - { - $this->_authenticateHeader = $authenticateHeader; - } - - public function getAuthenticateHeader() - { - return $this->_authenticateHeader; - } - - public function setContent($content) - { - $this->_content = $content; - } - - public function getContent() - { - return $this->_content; - } - -} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/README.md b/user_oauth/3rdparty/php-oauth-lib-rs/README.md new file mode 100644 index 000000000..584255ac1 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/README.md @@ -0,0 +1,126 @@ +# Introduction +This is a library to implement an OAuth 2.0 resource server (RS). The library +can be used by any service that wants to accept OAuth 2.0 bearer tokens. + +It is compatible with and was tested with +[php-oauth](https://github.com/fkooman/php-oauth). + +# License +Licensed under the Apache License, Version 2.0; + + http://www.apache.org/licenses/LICENSE-2.0 + +# API +Using the library is straightforward: + + <?php + require_once 'lib/OAuth/RemoteResourceServer.php'; + + use \OAuth\RemoteResourceServer as RemoteResourceServer; + + $config = array( + "introspectionEndpoint" => "http://localhost/php-oauth/introspect.php", + ); + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyAndHandleRequest(); + + header("Content-Type: text/plain"); + echo $introspection->getSub(); // resourceOwnerId + +Only the `introspectionEndpoint` configuration parameter needs to be set. + +* `introspectionEndpoint` (REQUIRED) specify the location at which to verify + the OAuth token; +* `realm` (OPTIONAL) specify the "realm" of the RS that is used when + returning errors to the client using the `WWW-Authenticate` header. Default + value is `Resource Server`; + +## Verify Tokens +If you write a simple piece of software that does not use any framework for +handling HTTP requests and responses you can use the following method to handle +all communication with the client by itself: + + verifyAndHandleRequest() + +This will extract the Bearer Authorization header or `access_token` query +parameter, verify it at the introspection endpoint and inform the client +directly about any problems. + +If you use a HTTP framework an other method is available to help you verify +tokens: + + verifyRequest(array $requestHeaders, array $queryParameters) + +You can specify an array with request headers and an array with query +parameters. You can just feed the library all request headers and query +parameters as only the relevant `Authorization` and/or `access_token` +query parameter are evaluated. If the client provides both an `Authorization` +header and an `access_token` query parameter an error will be returned as it +is not allowed to specify both methods simultaneously. + +## Retrieve Resource Owner Information +After the `verifyRequest()`, or any of the other verify functions, some methods +may be available to retrieve information about the resource owner and client +assuming the verification was successful and the token introspection endpoint +provides this information to the resource server. + +* `getResourceOwnerId()` (the unique resource owner identifier) +* `getScope()` (the scope granted to the client accessing this resource) +* `getEntitlement()` (the entitlement the resource owner has when accessing this + resource) +* `getExt()` (get additional data set by the authentication plugin. For instance + the SspResourceOwner class uses this to store the SAML attributes obtained from + the identity provider) + +Note that the `getEntitlement()` and `getExt()` methods are not supported by all +authorization servers and is a proprietary extension to +[https://github.com/fkooman/php-oauth](php-oauth). There are more methods that +can help with implementing a resource server more efficiently, see the +`TokenIntrospection` class documentation. + +## Exceptions +The library will return exceptions when using the `verifyRequest` method, you +can catch these exceptions and send the appropriate response to the client +using your own (HTTP) framework. + +The exception provides some helper methods to help with constructing a response +for the client: + +* `getResponseCode()` +* `getAuthenticateHeader()` +* `setRealm($realm)` +* `getContent()` + +The `getResponseCode()` method will get you the (integer) HTTP response code +to send to the client. The method `setRealm($realm)` allows you to set the +"realm" that will be part of the `WWW-Authenticate` header you can retrieve +with the `getAuthenticateHeader()` method. The `getContent()` method gives you +a `JSON` formatted response you can send back to the client, this is OPTIONAL. + +Here is an example on how to use this library with your own exception handling: + + <?php + require_once 'lib/OAuth/RemoteResourceServer.php'; + + use \OAuth\RemoteResourceServer as RemoteResourceServer; + use \OAuth\RemoteResourceServerException as RemoteResourceServerException; + + $config = array( + "introspectionEndpoint" => "http://localhost/php-oauth/introspect.php", + ); + + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(apache_request_headers(), $_GET); + header("Content-Type: text/plain"); + echo $introspection->getSub(); + } catch (RemoteResourceServerException $e) { + $e->setRealm("Foo"); + header("HTTP/1.1 " . $e->getResponseCode()); + if (NULL !== $e->getAuthenticateHeader()) { + // for "internal_server_error" responses no WWW-Authenticate header is set + header("WWW-Authenticate: " . $e->getAuthenticateHeader()); + } + header("Content-Type: application/json"); + die($e->getContent()); + } diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/draft-richer-oauth-introspection-03.txt b/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/draft-richer-oauth-introspection-03.txt new file mode 100644 index 000000000..8cad751e9 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/draft-richer-oauth-introspection-03.txt @@ -0,0 +1,336 @@ + + + +OAuth Working Group J. Richer, Ed. +Internet-Draft The MITRE Corporation +Intended status: Standards Track February 21, 2013 +Expires: August 25, 2013 + + + OAuth Token Introspection + draft-richer-oauth-introspection-03 + +Abstract + + This specification defines a method for a client or protected + resource to query an OAuth authorization server to determine meta- + information about an OAuth token. + +Requirements Language + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + +Status of this Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at http://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on August 25, 2013. + +Copyright Notice + + Copyright (c) 2013 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + + + +Richer Expires August 25, 2013 [Page 1] + +Internet-Draft oauth-introspection February 2013 + + + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Introspection Endpoint . . . . . . . . . . . . . . . . . . . . 3 + 2.1. Introspection Request . . . . . . . . . . . . . . . . . . . 3 + 2.2. Introspection Response . . . . . . . . . . . . . . . . . . 4 + 2.3. Non-normative Example . . . . . . . . . . . . . . . . . . . 4 + 3. IANA Considerations . . . . . . . . . . . . . . . . . . . . . . 6 + 4. Security Considerations . . . . . . . . . . . . . . . . . . . . 6 + 5. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 6 + 6. Normative References . . . . . . . . . . . . . . . . . . . . . 6 + Author's Address . . . . . . . . . . . . . . . . . . . . . . . . . 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Richer Expires August 25, 2013 [Page 2] + +Internet-Draft oauth-introspection February 2013 + + +1. Introduction + + In OAuth, the contents of tokens are opaque to clients. This means + that the client does not need to know anything about the content or + structure of the token itself, if there is any. However, there is + still a large amount of metadata that may be attached to a token, + such as its current validity, approved scopes, and extra information + about the authentication context in which the token was issued. + These pieces of information are often vital to Protected Resources + making authorization decisions based on the tokens being presented. + Since OAuth2 defines no direct relationship between the Authorization + Server and the Protected Resource, only that they must have an + agreement on the tokens themselves, there have been many different + approaches to bridging this gap. + + This specification defines an Introspection Endpoint that allows the + holder of a token to query the Authorization Server to discover the + set of metadata for a token. A Protected Resource may use the + mechanism described in this draft to query the Introspection Endpoint + in a particular authorization decision context and ascertain the + relevant metadata about the token in order to make this authorization + decision appropriately. + + +2. Introspection Endpoint + + The Introspection Endpoint is an OAuth 2 Endpoint that responds to + HTTP GET and HTTP POST requests from token holders. The endpoint + takes a single parameter representing the token (and optionally + further authentication) and returns a JSON document representing the + meta information surrounding the token. + +2.1. Introspection Request + + token REQUIRED. The string value of the token. + + resource_id OPTIONAL. A service-specific string identifying the + resource that the client doing the introspection is asking about. + + The endpoint MAY allow other parameters to provide context to the + query. For instance, an authorization service may need to know the + IP address of the Client in order to determine the appropriateness of + the token being presented. + + The endpoint SHOULD also require some form of authentication to + access this endpoint, such as the Client Authentication as described + in OAuth 2 Core Specification [RFC6749] or a separate OAuth2 Access + Token. The methods of managing and validating these authentication + + + +Richer Expires August 25, 2013 [Page 3] + +Internet-Draft oauth-introspection February 2013 + + + credentials are out of scope of this specification. + +2.2. Introspection Response + + The server responds with a JSON object [RFC4627] in + "application/json" format with the following top-level members. + Specific implementations MAY extend this structure with their own + service-specific pieces of information. + + active REQUIRED. Boolean indicator of whether or not the presented + token is currently active. + + expires_at OPTIONAL. Integer timestamp, measured in the number of + seconds since January 1 1970 UTC, indicating when this token will + expire. + + issued_at OPTIONAL. Integer timestamp, measured in the number of + seconds since January 1 1970 UTC, indicating when this token was + originally issued. + + scope OPTIONAL. A space-separated list of strings representing the + scopes associated with this token, in the format described in + Section 3.3 of OAuth 2.0 [RFC6749]. + + client_id OPTIONAL. Client Identifier for the OAuth Client that + requested this token. + + sub OPTIONAL. Local identifier of the Resource Owner who authorized + this token. + + aud OPTIONAL. Service-specific string identifier or list of string + identifiers representing the intended audience for this token. + +2.3. Non-normative Example + + For example, a Protected Resource accepts a request from a Client + carrying an OAuth2 Bearer Token. In order to know how and whether to + serve the request, the Protected Resource then makes the following + request to the Introspection Endpoint of the Authorization Server. + The Protected Resource is here authenticating with its own Client ID + and Client Secret as per OAuth2 [RFC6749] Section 2.3.1. + + + + + + + + + + +Richer Expires August 25, 2013 [Page 4] + +Internet-Draft oauth-introspection February 2013 + + + Following is a non-normative example request (with line wraps for + display purposes only): + POST /register HTTP/1.1 + Accept: application/x-www-form-urlencoded + Host: server.example.com + Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3 + + token=X3241Affw.4233-99JXJ + + The Authorization Server validates the client credentials and looks + up the information in the token. If the token is currently active, + it returns the following JSON document. + + Following is a non-normative example active token response (with line + wraps for display purposes only): + HTTP/1.1 200 OK + Content-Type: application/json + Cache-Control: no-store + + { + "active": true, + "client_id":"s6BhdRkqt3", + "scope": "read write dolphin", + "sub": "2309fj32kl", + "aud": "http://example.org/protected-resource/*" + } + + If the token presented is not currently active (but the + authentication presented is valid), it returns the following JSON + document. + + Following is a non-normative example response to an inactive or + invalid token (with line wraps for display purposes only): + HTTP/1.1 200 OK + Content-Type: application/json + Cache-Control: no-store + + { + "active": false + } + + If the client credentials are invalid or there is another error, the + Authorization Server responds with an HTTP 400 (Bad Request) as + described in OAuth 2.0 section 5.2 [RFC6749]. + + + + + + + +Richer Expires August 25, 2013 [Page 5] + +Internet-Draft oauth-introspection February 2013 + + +3. IANA Considerations + + This document makes no request of IANA. + + +4. Security Considerations + + If left unprotected and un-throttled, the Introspection Endpoint + could present a means for an attacker to poll a series of possible + token values, fishing for a valid token. Therefore, the + Authorization Server SHOULD issue special client credentials to any + protected resources or clients that need to access the introspection + endpoint. These credentials may be used directly at the endpoint, or + they may be exchanged for an OAuth2 Access token scoped specifically + for the Introspection Endpoint. + + +5. Acknowledgements + + Thanks to the OAuth Working Group and the UMA Working Group for + feedback. + + +6. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC4627] Crockford, D., "The application/json Media Type for + JavaScript Object Notation (JSON)", RFC 4627, July 2006. + + [RFC6749] Hardt, D., "The OAuth 2.0 Authorization Framework", + RFC 6749, October 2012. + + [RFC6750] Jones, M. and D. Hardt, "The OAuth 2.0 Authorization + Framework: Bearer Token Usage", RFC 6750, October 2012. + + +Author's Address + + Justin Richer (editor) + The MITRE Corporation + + Email: jricher@mitre.org + + + + + + + +Richer Expires August 25, 2013 [Page 6] + diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/rfc6750.txt b/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/rfc6750.txt new file mode 100644 index 000000000..b433c72a2 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/docs/specifications/rfc6750.txt @@ -0,0 +1,1011 @@ + + + + + + +Internet Engineering Task Force (IETF) M. Jones +Request for Comments: 6750 Microsoft +Category: Standards Track D. Hardt +ISSN: 2070-1721 Independent + October 2012 + + + The OAuth 2.0 Authorization Framework: Bearer Token Usage + +Abstract + + This specification describes how to use bearer tokens in HTTP + requests to access OAuth 2.0 protected resources. Any party in + possession of a bearer token (a "bearer") can use it to get access to + the associated resources (without demonstrating possession of a + cryptographic key). To prevent misuse, bearer tokens need to be + protected from disclosure in storage and in transport. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6750. + +Copyright Notice + + Copyright (c) 2012 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + +Jones & Hardt Standards Track [Page 1] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +Table of Contents + + 1. Introduction ....................................................2 + 1.1. Notational Conventions .....................................3 + 1.2. Terminology ................................................3 + 1.3. Overview ...................................................3 + 2. Authenticated Requests ..........................................4 + 2.1. Authorization Request Header Field .........................5 + 2.2. Form-Encoded Body Parameter ................................5 + 2.3. URI Query Parameter ........................................6 + 3. The WWW-Authenticate Response Header Field ......................7 + 3.1. Error Codes ................................................9 + 4. Example Access Token Response ..................................10 + 5. Security Considerations ........................................10 + 5.1. Security Threats ..........................................10 + 5.2. Threat Mitigation .........................................11 + 5.3. Summary of Recommendations ................................13 + 6. IANA Considerations ............................................14 + 6.1. OAuth Access Token Type Registration ......................14 + 6.1.1. The "Bearer" OAuth Access Token Type ...............14 + 6.2. OAuth Extensions Error Registration .......................14 + 6.2.1. The "invalid_request" Error Value ..................14 + 6.2.2. The "invalid_token" Error Value ....................15 + 6.2.3. The "insufficient_scope" Error Value ...............15 + 7. References .....................................................15 + 7.1. Normative References ......................................15 + 7.2. Informative References ....................................17 + Appendix A. Acknowledgements ......................................18 + +1. Introduction + + OAuth enables clients to access protected resources by obtaining an + access token, which is defined in "The OAuth 2.0 Authorization + Framework" [RFC6749] as "a string representing an access + authorization issued to the client", rather than using the resource + owner's credentials directly. + + Tokens are issued to clients by an authorization server with the + approval of the resource owner. The client uses the access token to + access the protected resources hosted by the resource server. This + specification describes how to make protected resource requests when + the OAuth access token is a bearer token. + + This specification defines the use of bearer tokens over HTTP/1.1 + [RFC2616] using Transport Layer Security (TLS) [RFC5246] to access + protected resources. TLS is mandatory to implement and use with this + specification; other specifications may extend this specification for + use with other protocols. While designed for use with access tokens + + + +Jones & Hardt Standards Track [Page 2] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + resulting from OAuth 2.0 authorization [RFC6749] flows to access + OAuth protected resources, this specification actually defines a + general HTTP authorization method that can be used with bearer tokens + from any source to access any resources protected by those bearer + tokens. The Bearer authentication scheme is intended primarily for + server authentication using the WWW-Authenticate and Authorization + HTTP headers but does not preclude its use for proxy authentication. + +1.1. Notational Conventions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in "Key words for use in + RFCs to Indicate Requirement Levels" [RFC2119]. + + This document uses the Augmented Backus-Naur Form (ABNF) notation of + [RFC5234]. Additionally, the following rules are included from + HTTP/1.1 [RFC2617]: auth-param and auth-scheme; and from "Uniform + Resource Identifier (URI): Generic Syntax" [RFC3986]: URI-reference. + + Unless otherwise noted, all the protocol parameter names and values + are case sensitive. + +1.2. Terminology + + Bearer Token + A security token with the property that any party in possession of + the token (a "bearer") can use the token in any way that any other + party in possession of it can. Using a bearer token does not + require a bearer to prove possession of cryptographic key material + (proof-of-possession). + + All other terms are as defined in "The OAuth 2.0 Authorization + Framework" [RFC6749]. + +1.3. Overview + + OAuth provides a method for clients to access a protected resource on + behalf of a resource owner. In the general case, before a client can + access a protected resource, it must first obtain an authorization + grant from the resource owner and then exchange the authorization + grant for an access token. The access token represents the grant's + scope, duration, and other attributes granted by the authorization + grant. The client accesses the protected resource by presenting the + access token to the resource server. In some cases, a client can + directly present its own credentials to an authorization server to + obtain an access token without having to first obtain an + authorization grant from a resource owner. + + + +Jones & Hardt Standards Track [Page 3] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + The access token provides an abstraction, replacing different + authorization constructs (e.g., username and password, assertion) for + a single token understood by the resource server. This abstraction + enables issuing access tokens valid for a short time period, as well + as removing the resource server's need to understand a wide range of + authentication schemes. + + +--------+ +---------------+ + | |--(A)- Authorization Request ->| Resource | + | | | Owner | + | |<-(B)-- Authorization Grant ---| | + | | +---------------+ + | | + | | +---------------+ + | |--(C)-- Authorization Grant -->| Authorization | + | Client | | Server | + | |<-(D)----- Access Token -------| | + | | +---------------+ + | | + | | +---------------+ + | |--(E)----- Access Token ------>| Resource | + | | | Server | + | |<-(F)--- Protected Resource ---| | + +--------+ +---------------+ + + Figure 1: Abstract Protocol Flow + + The abstract OAuth 2.0 flow illustrated in Figure 1 describes the + interaction between the client, resource owner, authorization server, + and resource server (described in [RFC6749]). The following two + steps are specified within this document: + + (E) The client requests the protected resource from the resource + server and authenticates by presenting the access token. + + (F) The resource server validates the access token, and if valid, + serves the request. + + This document also imposes semantic requirements upon the access + token returned in step (D). + +2. Authenticated Requests + + This section defines three methods of sending bearer access tokens in + resource requests to resource servers. Clients MUST NOT use more + than one method to transmit the token in each request. + + + + + +Jones & Hardt Standards Track [Page 4] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +2.1. Authorization Request Header Field + + When sending the access token in the "Authorization" request header + field defined by HTTP/1.1 [RFC2617], the client uses the "Bearer" + authentication scheme to transmit the access token. + + For example: + + GET /resource HTTP/1.1 + Host: server.example.com + Authorization: Bearer mF_9.B5f-4.1JqM + + The syntax of the "Authorization" header field for this scheme + follows the usage of the Basic scheme defined in Section 2 of + [RFC2617]. Note that, as with Basic, it does not conform to the + generic syntax defined in Section 1.2 of [RFC2617] but is compatible + with the general authentication framework being developed for + HTTP 1.1 [HTTP-AUTH], although it does not follow the preferred + practice outlined therein in order to reflect existing deployments. + The syntax for Bearer credentials is as follows: + + b64token = 1*( ALPHA / DIGIT / + "-" / "." / "_" / "~" / "+" / "/" ) *"=" + credentials = "Bearer" 1*SP b64token + + Clients SHOULD make authenticated requests with a bearer token using + the "Authorization" request header field with the "Bearer" HTTP + authorization scheme. Resource servers MUST support this method. + +2.2. Form-Encoded Body Parameter + + When sending the access token in the HTTP request entity-body, the + client adds the access token to the request-body using the + "access_token" parameter. The client MUST NOT use this method unless + all of the following conditions are met: + + o The HTTP request entity-header includes the "Content-Type" header + field set to "application/x-www-form-urlencoded". + + o The entity-body follows the encoding requirements of the + "application/x-www-form-urlencoded" content-type as defined by + HTML 4.01 [W3C.REC-html401-19991224]. + + o The HTTP request entity-body is single-part. + + + + + + + +Jones & Hardt Standards Track [Page 5] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + o The content to be encoded in the entity-body MUST consist entirely + of ASCII [USASCII] characters. + + o The HTTP request method is one for which the request-body has + defined semantics. In particular, this means that the "GET" + method MUST NOT be used. + + The entity-body MAY include other request-specific parameters, in + which case the "access_token" parameter MUST be properly separated + from the request-specific parameters using "&" character(s) (ASCII + code 38). + + For example, the client makes the following HTTP request using + transport-layer security: + + POST /resource HTTP/1.1 + Host: server.example.com + Content-Type: application/x-www-form-urlencoded + + access_token=mF_9.B5f-4.1JqM + + The "application/x-www-form-urlencoded" method SHOULD NOT be used + except in application contexts where participating browsers do not + have access to the "Authorization" request header field. Resource + servers MAY support this method. + +2.3. URI Query Parameter + + When sending the access token in the HTTP request URI, the client + adds the access token to the request URI query component as defined + by "Uniform Resource Identifier (URI): Generic Syntax" [RFC3986], + using the "access_token" parameter. + + For example, the client makes the following HTTP request using + transport-layer security: + + GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1 + Host: server.example.com + + The HTTP request URI query can include other request-specific + parameters, in which case the "access_token" parameter MUST be + properly separated from the request-specific parameters using "&" + character(s) (ASCII code 38). + + + + + + + + +Jones & Hardt Standards Track [Page 6] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + For example: + + https://server.example.com/resource?access_token=mF_9.B5f-4.1JqM&p=q + + Clients using the URI Query Parameter method SHOULD also send a + Cache-Control header containing the "no-store" option. Server + success (2XX status) responses to these requests SHOULD contain a + Cache-Control header with the "private" option. + + Because of the security weaknesses associated with the URI method + (see Section 5), including the high likelihood that the URL + containing the access token will be logged, it SHOULD NOT be used + unless it is impossible to transport the access token in the + "Authorization" request header field or the HTTP request entity-body. + Resource servers MAY support this method. + + This method is included to document current use; its use is not + recommended, due to its security deficiencies (see Section 5) and + also because it uses a reserved query parameter name, which is + counter to URI namespace best practices, per "Architecture of the + World Wide Web, Volume One" [W3C.REC-webarch-20041215]. + +3. The WWW-Authenticate Response Header Field + + If the protected resource request does not include authentication + credentials or does not contain an access token that enables access + to the protected resource, the resource server MUST include the HTTP + "WWW-Authenticate" response header field; it MAY include it in + response to other conditions as well. The "WWW-Authenticate" header + field uses the framework defined by HTTP/1.1 [RFC2617]. + + All challenges defined by this specification MUST use the auth-scheme + value "Bearer". This scheme MUST be followed by one or more + auth-param values. The auth-param attributes used or defined by this + specification are as follows. Other auth-param attributes MAY be + used as well. + + A "realm" attribute MAY be included to indicate the scope of + protection in the manner described in HTTP/1.1 [RFC2617]. The + "realm" attribute MUST NOT appear more than once. + + The "scope" attribute is defined in Section 3.3 of [RFC6749]. The + "scope" attribute is a space-delimited list of case-sensitive scope + values indicating the required scope of the access token for + accessing the requested resource. "scope" values are implementation + defined; there is no centralized registry for them; allowed values + are defined by the authorization server. The order of "scope" values + is not significant. In some cases, the "scope" value will be used + + + +Jones & Hardt Standards Track [Page 7] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + when requesting a new access token with sufficient scope of access to + utilize the protected resource. Use of the "scope" attribute is + OPTIONAL. The "scope" attribute MUST NOT appear more than once. The + "scope" value is intended for programmatic use and is not meant to be + displayed to end-users. + + Two example scope values follow; these are taken from the OpenID + Connect [OpenID.Messages] and the Open Authentication Technology + Committee (OATC) Online Multimedia Authorization Protocol [OMAP] + OAuth 2.0 use cases, respectively: + + scope="openid profile email" + scope="urn:example:channel=HBO&urn:example:rating=G,PG-13" + + If the protected resource request included an access token and failed + authentication, the resource server SHOULD include the "error" + attribute to provide the client with the reason why the access + request was declined. The parameter value is described in + Section 3.1. In addition, the resource server MAY include the + "error_description" attribute to provide developers a human-readable + explanation that is not meant to be displayed to end-users. It also + MAY include the "error_uri" attribute with an absolute URI + identifying a human-readable web page explaining the error. The + "error", "error_description", and "error_uri" attributes MUST NOT + appear more than once. + + Values for the "scope" attribute (specified in Appendix A.4 of + [RFC6749]) MUST NOT include characters outside the set %x21 / %x23-5B + / %x5D-7E for representing scope values and %x20 for delimiters + between scope values. Values for the "error" and "error_description" + attributes (specified in Appendixes A.7 and A.8 of [RFC6749]) MUST + NOT include characters outside the set %x20-21 / %x23-5B / %x5D-7E. + Values for the "error_uri" attribute (specified in Appendix A.9 of + [RFC6749]) MUST conform to the URI-reference syntax and thus MUST NOT + include characters outside the set %x21 / %x23-5B / %x5D-7E. + + For example, in response to a protected resource request without + authentication: + + HTTP/1.1 401 Unauthorized + WWW-Authenticate: Bearer realm="example" + + + + + + + + + + +Jones & Hardt Standards Track [Page 8] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + And in response to a protected resource request with an + authentication attempt using an expired access token: + + HTTP/1.1 401 Unauthorized + WWW-Authenticate: Bearer realm="example", + error="invalid_token", + error_description="The access token expired" + +3.1. Error Codes + + When a request fails, the resource server responds using the + appropriate HTTP status code (typically, 400, 401, 403, or 405) and + includes one of the following error codes in the response: + + invalid_request + The request is missing a required parameter, includes an + unsupported parameter or parameter value, repeats the same + parameter, uses more than one method for including an access + token, or is otherwise malformed. The resource server SHOULD + respond with the HTTP 400 (Bad Request) status code. + + invalid_token + The access token provided is expired, revoked, malformed, or + invalid for other reasons. The resource SHOULD respond with + the HTTP 401 (Unauthorized) status code. The client MAY + request a new access token and retry the protected resource + request. + + insufficient_scope + The request requires higher privileges than provided by the + access token. The resource server SHOULD respond with the HTTP + 403 (Forbidden) status code and MAY include the "scope" + attribute with the scope necessary to access the protected + resource. + + If the request lacks any authentication information (e.g., the client + was unaware that authentication is necessary or attempted using an + unsupported authentication method), the resource server SHOULD NOT + include an error code or other error information. + + For example: + + HTTP/1.1 401 Unauthorized + WWW-Authenticate: Bearer realm="example" + + + + + + + +Jones & Hardt Standards Track [Page 9] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +4. Example Access Token Response + + Typically, a bearer token is returned to the client as part of an + OAuth 2.0 [RFC6749] access token response. An example of such a + response is: + + HTTP/1.1 200 OK + Content-Type: application/json;charset=UTF-8 + Cache-Control: no-store + Pragma: no-cache + + { + "access_token":"mF_9.B5f-4.1JqM", + "token_type":"Bearer", + "expires_in":3600, + "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA" + } + +5. Security Considerations + + This section describes the relevant security threats regarding token + handling when using bearer tokens and describes how to mitigate these + threats. + +5.1. Security Threats + + The following list presents several common threats against protocols + utilizing some form of tokens. This list of threats is based on NIST + Special Publication 800-63 [NIST800-63]. Since this document builds + on the OAuth 2.0 Authorization specification [RFC6749], we exclude a + discussion of threats that are described there or in related + documents. + + Token manufacture/modification: An attacker may generate a bogus + token or modify the token contents (such as the authentication or + attribute statements) of an existing token, causing the resource + server to grant inappropriate access to the client. For example, + an attacker may modify the token to extend the validity period; a + malicious client may modify the assertion to gain access to + information that they should not be able to view. + + Token disclosure: Tokens may contain authentication and attribute + statements that include sensitive information. + + + + + + + + +Jones & Hardt Standards Track [Page 10] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + Token redirect: An attacker uses a token generated for consumption + by one resource server to gain access to a different resource + server that mistakenly believes the token to be for it. + + Token replay: An attacker attempts to use a token that has already + been used with that resource server in the past. + +5.2. Threat Mitigation + + A large range of threats can be mitigated by protecting the contents + of the token by using a digital signature or a Message Authentication + Code (MAC). Alternatively, a bearer token can contain a reference to + authorization information, rather than encoding the information + directly. Such references MUST be infeasible for an attacker to + guess; using a reference may require an extra interaction between a + server and the token issuer to resolve the reference to the + authorization information. The mechanics of such an interaction are + not defined by this specification. + + This document does not specify the encoding or the contents of the + token; hence, detailed recommendations about the means of + guaranteeing token integrity protection are outside the scope of this + document. The token integrity protection MUST be sufficient to + prevent the token from being modified. + + To deal with token redirect, it is important for the authorization + server to include the identity of the intended recipients (the + audience), typically a single resource server (or a list of resource + servers), in the token. Restricting the use of the token to a + specific scope is also RECOMMENDED. + + The authorization server MUST implement TLS. Which version(s) ought + to be implemented will vary over time and will depend on the + widespread deployment and known security vulnerabilities at the time + of implementation. At the time of this writing, TLS version 1.2 + [RFC5246] is the most recent version, but it has very limited actual + deployment and might not be readily available in implementation + toolkits. TLS version 1.0 [RFC2246] is the most widely deployed + version and will give the broadest interoperability. + + To protect against token disclosure, confidentiality protection MUST + be applied using TLS [RFC5246] with a ciphersuite that provides + confidentiality and integrity protection. This requires that the + communication interaction between the client and the authorization + server, as well as the interaction between the client and the + resource server, utilize confidentiality and integrity protection. + Since TLS is mandatory to implement and to use with this + specification, it is the preferred approach for preventing token + + + +Jones & Hardt Standards Track [Page 11] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + disclosure via the communication channel. For those cases where the + client is prevented from observing the contents of the token, token + encryption MUST be applied in addition to the usage of TLS + protection. As a further defense against token disclosure, the + client MUST validate the TLS certificate chain when making requests + to protected resources, including checking the Certificate Revocation + List (CRL) [RFC5280]. + + Cookies are typically transmitted in the clear. Thus, any + information contained in them is at risk of disclosure. Therefore, + bearer tokens MUST NOT be stored in cookies that can be sent in the + clear. See "HTTP State Management Mechanism" [RFC6265] for security + considerations about cookies. + + In some deployments, including those utilizing load balancers, the + TLS connection to the resource server terminates prior to the actual + server that provides the resource. This could leave the token + unprotected between the front-end server where the TLS connection + terminates and the back-end server that provides the resource. In + such deployments, sufficient measures MUST be employed to ensure + confidentiality of the token between the front-end and back-end + servers; encryption of the token is one such possible measure. + + To deal with token capture and replay, the following recommendations + are made: First, the lifetime of the token MUST be limited; one means + of achieving this is by putting a validity time field inside the + protected part of the token. Note that using short-lived (one hour + or less) tokens reduces the impact of them being leaked. Second, + confidentiality protection of the exchanges between the client and + the authorization server and between the client and the resource + server MUST be applied. As a consequence, no eavesdropper along the + communication path is able to observe the token exchange. + Consequently, such an on-path adversary cannot replay the token. + Furthermore, when presenting the token to a resource server, the + client MUST verify the identity of that resource server, as per + Section 3.1 of "HTTP Over TLS" [RFC2818]. Note that the client MUST + validate the TLS certificate chain when making these requests to + protected resources. Presenting the token to an unauthenticated and + unauthorized resource server or failing to validate the certificate + chain will allow adversaries to steal the token and gain unauthorized + access to protected resources. + + + + + + + + + + +Jones & Hardt Standards Track [Page 12] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +5.3. Summary of Recommendations + + Safeguard bearer tokens: Client implementations MUST ensure that + bearer tokens are not leaked to unintended parties, as they will + be able to use them to gain access to protected resources. This + is the primary security consideration when using bearer tokens and + underlies all the more specific recommendations that follow. + + Validate TLS certificate chains: The client MUST validate the TLS + certificate chain when making requests to protected resources. + Failing to do so may enable DNS hijacking attacks to steal the + token and gain unintended access. + + Always use TLS (https): Clients MUST always use TLS [RFC5246] + (https) or equivalent transport security when making requests with + bearer tokens. Failing to do so exposes the token to numerous + attacks that could give attackers unintended access. + + Don't store bearer tokens in cookies: Implementations MUST NOT store + bearer tokens within cookies that can be sent in the clear (which + is the default transmission mode for cookies). Implementations + that do store bearer tokens in cookies MUST take precautions + against cross-site request forgery. + + Issue short-lived bearer tokens: Token servers SHOULD issue + short-lived (one hour or less) bearer tokens, particularly when + issuing tokens to clients that run within a web browser or other + environments where information leakage may occur. Using + short-lived bearer tokens can reduce the impact of them being + leaked. + + Issue scoped bearer tokens: Token servers SHOULD issue bearer tokens + that contain an audience restriction, scoping their use to the + intended relying party or set of relying parties. + + Don't pass bearer tokens in page URLs: Bearer tokens SHOULD NOT be + passed in page URLs (for example, as query string parameters). + Instead, bearer tokens SHOULD be passed in HTTP message headers or + message bodies for which confidentiality measures are taken. + Browsers, web servers, and other software may not adequately + secure URLs in the browser history, web server logs, and other + data structures. If bearer tokens are passed in page URLs, + attackers might be able to steal them from the history data, logs, + or other unsecured locations. + + + + + + + +Jones & Hardt Standards Track [Page 13] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +6. IANA Considerations + +6.1. OAuth Access Token Type Registration + + This specification registers the following access token type in the + OAuth Access Token Types registry defined in [RFC6749]. + +6.1.1. The "Bearer" OAuth Access Token Type + + Type name: + Bearer + + Additional Token Endpoint Response Parameters: + (none) + + HTTP Authentication Scheme(s): + Bearer + + Change controller: + IETF + + Specification document(s): + RFC 6750 + +6.2. OAuth Extensions Error Registration + + This specification registers the following error values in the OAuth + Extensions Error registry defined in [RFC6749]. + +6.2.1. The "invalid_request" Error Value + + Error name: + invalid_request + + Error usage location: + Resource access error response + + Related protocol extension: + Bearer access token type + + Change controller: + IETF + + Specification document(s): + RFC 6750 + + + + + + +Jones & Hardt Standards Track [Page 14] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +6.2.2. The "invalid_token" Error Value + + Error name: + invalid_token + + Error usage location: + Resource access error response + + Related protocol extension: + Bearer access token type + + Change controller: + IETF + + Specification document(s): + RFC 6750 + +6.2.3. The "insufficient_scope" Error Value + + Error name: + insufficient_scope + + Error usage location: + Resource access error response + + Related protocol extension: + Bearer access token type + + Change controller: + IETF + + Specification document(s): + RFC 6750 + +7. References + +7.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2246] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0", + RFC 2246, January 1999. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + + + +Jones & Hardt Standards Track [Page 15] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + + [RFC2617] Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, + S., Leach, P., Luotonen, A., and L. Stewart, "HTTP + Authentication: Basic and Digest Access Authentication", + RFC 2617, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, January 2005. + + [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax + Specifications: ABNF", STD 68, RFC 5234, January 2008. + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer + Security (TLS) Protocol Version 1.2", RFC 5246, + August 2008. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., + Housley, R., and W. Polk, "Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation + List (CRL) Profile", RFC 5280, May 2008. + + [RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265, + April 2011. + + [RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework", + RFC 6749, October 2012. + + [USASCII] American National Standards Institute, "Coded Character + Set -- 7-bit American Standard Code for Information + Interchange", ANSI X3.4, 1986. + + [W3C.REC-html401-19991224] + Raggett, D., Le Hors, A., and I. Jacobs, "HTML 4.01 + Specification", World Wide Web Consortium + Recommendation REC-html401-19991224, December 1999, + <http://www.w3.org/TR/1999/REC-html401-19991224>. + + [W3C.REC-webarch-20041215] + Jacobs, I. and N. Walsh, "Architecture of the World Wide + Web, Volume One", World Wide Web Consortium + Recommendation REC-webarch-20041215, December 2004, + <http://www.w3.org/TR/2004/REC-webarch-20041215>. + + + + + + + +Jones & Hardt Standards Track [Page 16] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +7.2. Informative References + + [HTTP-AUTH] Fielding, R., Ed., and J. Reschke, Ed., "Hypertext + Transfer Protocol (HTTP/1.1): Authentication", Work + in Progress, October 2012. + + [NIST800-63] Burr, W., Dodson, D., Newton, E., Perlner, R., Polk, T., + Gupta, S., and E. Nabbus, "NIST Special Publication + 800-63-1, INFORMATION SECURITY", December 2011, + <http://csrc.nist.gov/publications/>. + + [OMAP] Huff, J., Schlacht, D., Nadalin, A., Simmons, J., + Rosenberg, P., Madsen, P., Ace, T., Rickelton-Abdi, C., + and B. Boyer, "Online Multimedia Authorization Protocol: + An Industry Standard for Authorized Access to Internet + Multimedia Resources", April 2012, + <http://www.oatc.us/Standards/Download.aspx>. + + [OpenID.Messages] + Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., + Mortimore, C., and E. Jay, "OpenID Connect Messages + 1.0", June 2012, <http://openid.net/specs/ + openid-connect-messages-1_0.html>. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Jones & Hardt Standards Track [Page 17] + +RFC 6750 OAuth 2.0 Bearer Token Usage October 2012 + + +Appendix A. Acknowledgements + + The following people contributed to preliminary versions of this + document: Blaine Cook (BT), Brian Eaton (Google), Yaron Y. Goland + (Microsoft), Brent Goldman (Facebook), Raffi Krikorian (Twitter), + Luke Shepard (Facebook), and Allen Tom (Yahoo!). The content and + concepts within are a product of the OAuth community, the Web + Resource Authorization Profiles (WRAP) community, and the OAuth + Working Group. David Recordon created a preliminary version of this + specification based upon an early draft of the specification that + evolved into OAuth 2.0 [RFC6749]. Michael B. Jones in turn created + the first version (00) of this specification using portions of + David's preliminary document and edited all subsequent versions. + + The OAuth Working Group has dozens of very active contributors who + proposed ideas and wording for this document, including Michael + Adams, Amanda Anganes, Andrew Arnott, Derek Atkins, Dirk Balfanz, + John Bradley, Brian Campbell, Francisco Corella, Leah Culver, Bill de + hOra, Breno de Medeiros, Brian Ellin, Stephen Farrell, Igor Faynberg, + George Fletcher, Tim Freeman, Evan Gilbert, Yaron Y. Goland, Eran + Hammer, Thomas Hardjono, Dick Hardt, Justin Hart, Phil Hunt, John + Kemp, Chasen Le Hara, Barry Leiba, Amos Jeffries, Michael B. Jones, + Torsten Lodderstedt, Paul Madsen, Eve Maler, James Manger, Laurence + Miao, William J. Mills, Chuck Mortimore, Anthony Nadalin, Axel + Nennker, Mark Nottingham, David Recordon, Julian Reschke, Rob + Richards, Justin Richer, Peter Saint-Andre, Nat Sakimura, Rob Sayre, + Marius Scurtescu, Naitik Shah, Justin Smith, Christian Stuebner, + Jeremy Suriel, Doug Tangren, Paul Tarjan, Hannes Tschofenig, Franklin + Tse, Sean Turner, Paul Walker, Shane Weeden, Skylar Woodward, and + Zachary Zeltsan. + +Authors' Addresses + + Michael B. Jones + Microsoft + + EMail: mbj@microsoft.com + URI: http://self-issued.info/ + + + Dick Hardt + Independent + + EMail: dick.hardt@gmail.com + URI: http://dickhardt.org/ + + + + + + +Jones & Hardt Standards Track [Page 18] + diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/lib/OAuth/RemoteResourceServer.php b/user_oauth/3rdparty/php-oauth-lib-rs/lib/OAuth/RemoteResourceServer.php new file mode 100644 index 000000000..acb8c46b5 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/lib/OAuth/RemoteResourceServer.php @@ -0,0 +1,467 @@ +<?php + +/** + * Copyright 2013 François Kooman <fkooman@tuxed.net> + * + * 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. + */ + +namespace OAuth; + +class RemoteResourceServer +{ + private $_config; + + public function __construct(array $c) + { + $this->_config = $c; + } + + public function verifyAndHandleRequest() + { + try { + $headerBearerToken = NULL; + $queryBearerToken = NULL; + + // look for headers + if (function_exists("apache_request_headers")) { + $headers = apache_request_headers(); + } elseif (isset($_SERVER)) { + $headers = $_SERVER; + } else { + $headers = array(); + } + + // look for query parameters + $query = (isset($_GET) && is_array($_GET)) ? $_GET : array(); + + return $this->verifyRequest($headers, $query); + + } catch (RemoteResourceServerException $e) { + // send response directly to client, halt execution of calling script as well + $e->setRealm($this->_getConfigParameter("realm", FALSE, "Resource Server")); + header("HTTP/1.1 " . $e->getResponseCode()); + if (NULL !== $e->getAuthenticateHeader()) { + // for "internal_server_error" responses no WWW-Authenticate header is set + header("WWW-Authenticate: " . $e->getAuthenticateHeader()); + } + header("Content-Type: application/json"); + die($e->getContent()); + } + } + + public function verifyRequest(array $headers, array $query) + { + // extract token from authorization header + $authorizationHeader = self::_getAuthorizationHeader($headers); + $ah = FALSE !== $authorizationHeader ? self::_getTokenFromHeader($authorizationHeader) : FALSE; + + // extract token from query parameters + $aq = self::_getTokenFromQuery($query); + + if (FALSE === $ah && FALSE === $aq) { + // no token at all provided + throw new RemoteResourceServerException("no_token", "missing token"); + } + if (FALSE !== $ah && FALSE !== $aq) { + // two tokens provided + throw new RemoteResourceServerException("invalid_request", "more than one method for including an access token used"); + } + if (FALSE !== $ah) { + return $this->verifyBearerToken($ah); + } + if (FALSE !== $aq) { + return $this->verifyBearerToken($aq); + } + } + + private static function _getAuthorizationHeader(array $headers) + { + $headerKeys = array_keys($headers); + foreach (array("X-Authorization", "Authorization") as $h) { + $keyPositionInArray = array_search(strtolower($h), array_map('strtolower', $headerKeys)); + if (FALSE === $keyPositionInArray) { + continue; + } + + return $headers[$headerKeys[$keyPositionInArray]]; + } + + return FALSE; + } + + private static function _getTokenFromHeader($authorizationHeader) + { + if (0 !== strpos($authorizationHeader, "Bearer ")) { + return FALSE; + } + + return substr($authorizationHeader, 7); + } + + private static function _getTokenFromQuery(array $queryParameters) + { + if (!isset($queryParameters) || empty($queryParameters['access_token'])) { + return FALSE; + } + + return $queryParameters['access_token']; + } + + public function verifyBearerToken($token) + { + // b64token = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" + if ( 1 !== preg_match('|^[[:alpha:][:digit:]-._~+/]+=*$|', $token)) { + throw new RemoteResourceServerException("invalid_token", "the access token is not a valid b64token"); + } + + $introspectionEndpoint = $this->_getConfigParameter("introspectionEndpoint"); + $get = array("token" => $token); + + if (!function_exists("curl_init")) { + throw new RemoteResourceServerException("internal_server_error", "php curl module not available"); + } + + $curlChannel = curl_init(); + if (FALSE === $curlChannel) { + throw new RemoteResourceServerException("internal_server_error", "unable to initialize curl"); + } + + if (0 !== strpos($introspectionEndpoint, "file://")) { + $separator = (FALSE === strpos($introspectionEndpoint, "?")) ? "?" : "&"; + $introspectionEndpoint .= $separator . http_build_query($get); + } else { + // file cannot have query parameter, use accesstoken as JSON file instead + $introspectionEndpoint .= $token . ".json"; + } + if (FALSE === curl_setopt_array($curlChannel, array ( + CURLOPT_URL => $introspectionEndpoint, + //CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_SSL_VERIFYPEER => 1, + CURLOPT_SSL_VERIFYHOST => 2, + ))) { + throw new RemoteResourceServerException("internal_server_error", "unable to set curl options"); + } + + $output = curl_exec($curlChannel); + + if (FALSE === $output) { + $error = curl_error($curlChannel); + throw new RemoteResourceServerException("internal_server_error", "unable to contact introspection endpoint"); + } + + $httpCode = curl_getinfo($curlChannel, CURLINFO_HTTP_CODE); + curl_close($curlChannel); + + if (0 !== strpos($introspectionEndpoint, "file://")) { + // not a file + if (200 !== $httpCode) { + throw new RemoteResourceServerException("internal_server_error", "unexpected response code from introspection endpoint"); + } + } + + $data = json_decode($output, TRUE); + $jsonError = json_last_error(); + if (JSON_ERROR_NONE !== $jsonError) { + throw new RemoteResourceServerException("internal_server_error", "unable to decode response from introspection endpoint"); + } + if (!is_array($data) || !isset($data['active']) || !is_bool($data['active'])) { + throw new RemoteResourceServerException("internal_server_error", "unexpected response from introspection endpoint"); + } + + if (!$data['active']) { + throw new RemoteResourceServerException("invalid_token", "the token is not active"); + } + + return new TokenIntrospection($data); + } + + private function _getConfigParameter($key, $required = TRUE, $default = NULL) + { + if (!array_key_exists($key, $this->_config)) { + if ($required) { + throw new RemoteResourceServerException("internal_server_error", "missing required configuration parameter"); + } else { + return $default; + } + } + + return $this->_config[$key]; + } +} + +class TokenIntrospection +{ + private $_response; + + public function __construct(array $response) + { + if (!isset($response['active']) || !is_bool($response['active'])) { + throw new RemoteResourceServerException("internal_server_error", "active key should be set and its value a boolean"); + } + + if (isset($response['exp']) && (!is_int($response['exp']) || 0 > $response['exp'])) { + throw new RemoteResourceServerException("internal_server_error", "exp value must be positive integer"); + } + + if (isset($response['exp']) && (!is_int($response['iat']) || 0 > $response['iat'])) { + throw new RemoteResourceServerException("internal_server_error", "iat value must be positive integer"); + } + + if (isset($response['iat'])) { + if (time() < $response['iat']) { + throw new RemoteResourceServerException("internal_server_error", "token issued in the future"); + } + } + + if (isset($response['exp']) && isset($response['iat'])) { + if ($response['exp'] < $response['iat']) { + throw new RemoteResourceServerException("internal_server_error", "token expired before it was issued"); + } + } + + if (isset($response['exp'])) { + if (time() > $response['exp']) { + throw new RemoteResourceServerException("invalid_token", "the token expired"); + } + } + + if (isset($response['x-entitlement']) && !is_array($response['x-entitlement'])) { + throw new RemoteResourceServerException("internal_server_error", "x-entitlement value must be array"); + } + + $this->_response = $response; + } + + /** + * REQUIRED. Boolean indicator of whether or not the presented + * token is currently active. + */ + public function getActive() + { + return $this->_response['active']; + } + + /** + * OPTIONAL. Integer timestamp, measured in the number of + * seconds since January 1 1970 UTC, indicating when this token will + * expire. + */ + public function getExpiresAt() + { + return $this->_getKeyValue('exp'); + } + + /** + * OPTIONAL. Integer timestamp, measured in the number of + * seconds since January 1 1970 UTC, indicating when this token was + * originally issued. + */ + public function getIssuedAt() + { + return $this->_getKeyValue('iat'); + } + + /** + * OPTIONAL. A space-separated list of strings representing the + * scopes associated with this token, in the format described in + * Section 3.3 of OAuth 2.0 [RFC6749]. + */ + public function getScope() + { + return $this->_getKeyValue('scope'); + } + + /** + * OPTIONAL. Client Identifier for the OAuth Client that + * requested this token. + */ + public function getClientId() + { + return $this->_getKeyValue('client_id'); + } + + /** + * OPTIONAL. Local identifier of the Resource Owner who authorized + * this token. + */ + public function getSub() + { + return $this->_getKeyValue('sub'); + } + + /** + * OPTIONAL. Service-specific string identifier or list of string + * identifiers representing the intended audience for this token. + */ + public function getAud() + { + return $this->_getKeyValue('aud'); + } + + /** + * OPTIONAL. Type of the token as defined in OAuth 2.0 + * section 5.1. + */ + public function getTokenType() + { + return $this->_getKeyValue('token_type'); + } + + private function _getKeyValue($key) + { + return isset($this->_response[$key]) ? $this->_response[$key] : FALSE; + } + + /* ADDITIONAL HELPER METHODS */ + public function getResourceOwnerId() + { + return $this->getSub(); + } + + public function getScopeAsArray() + { + return FALSE !== $this->getScope() ? explode(" ", $this->getScope()) : FALSE; + } + + public function hasScope($scope) + { + return FALSE !== $this->getScopeAsArray() ? in_array($scope, $this->getScopeAsArray()) : FALSE; + } + + public function requireScope($scope) + { + if (FALSE === $this->hasScope($scope)) { + throw new RemoteResourceServerException("insufficient_scope", "no permission for this call with granted scope"); + } + } + + public function requireAnyScope(array $scope) + { + if (FALSE === $this->hasAnyScope($scope)) { + throw new RemoteResourceServerException("insufficient_scope", "no permission for this call with granted scope"); + } + } + + /** + * At least one of the scopes should be granted. + * + * @param array $scope the list of scopes of which one should be granted + * @return TRUE when at least one of the requested scopes was granted, + * FALSE when none were granted. + */ + public function hasAnyScope(array $scope) + { + foreach ($scope as $s) { + if ($this->hasScope($s)) { + return TRUE; + } + } + + return FALSE; + } + + public function getEntitlement() + { + return $this->_getKeyValue('x-entitlement'); + } + + public function hasEntitlement($entitlement) + { + return FALSE !== $this->getEntitlement() ? in_array($entitlement, $this->getEntitlement()) : FALSE; + } + + public function requireEntitlement($entitlement) + { + if (FALSE === $this->hasEntitlement($entitlement)) { + throw new RemoteResourceServerException("insufficient_entitlement", "no permission for this call with granted entitlement"); + } + } + + public function getExt() + { + return $this->_getKeyValue('x-ext'); + } + +} + +class RemoteResourceServerException extends \Exception +{ + private $description; + private $responseCode; + private $realm; + + public function __construct($message, $description, $code = 0, Exception $previous = null) + { + switch ($message) { + case "no_token": + case "invalid_token": + $this->responseCode = 401; + break; + case "insufficient_scope": + case "insufficient_entitlement": + $this->responseCode = 403; + break; + case "internal_server_error": + $this->responseCode = 500; + break; + case "invalid_request": + default: + $this->responseCode = 400; + break; + } + + $this->description = $description; + $this->realm = "Resource Server"; + + parent::__construct($message, $code, $previous); + } + + public function getDescription() + { + return $this->description; + } + + public function setRealm($resourceServerRealm) + { + $this->realm = (is_string($resourceServerRealm) && !empty($resourceServerRealm)) ? $resourceServerRealm : "Resource Server"; + } + + public function getResponseCode() + { + return $this->responseCode; + } + + public function getAuthenticateHeader() + { + $authenticateHeader = NULL; + if (500 !== $this->responseCode) { + if ("no_token" === $this->message) { + // no authorization header is a special case, the client did not know + // authentication was required, so tell it now without giving error message + $authenticateHeader = sprintf('Bearer realm="%s"', $this->realm); + } else { + $authenticateHeader = sprintf('Bearer realm="%s",error="%s",error_description="%s"', $this->realm, $this->message, $this->description); + } + } + + return $authenticateHeader; + } + + public function getContent() + { + return json_encode(array("error" => $this->message, "error_description" => $this->description)); + } + +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/RemoteResourceServerTest.php b/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/RemoteResourceServerTest.php new file mode 100644 index 000000000..b4041abaa --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/RemoteResourceServerTest.php @@ -0,0 +1,160 @@ +<?php + +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +require_once dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "OAuth" . DIRECTORY_SEPARATOR . "RemoteResourceServer.php"; + +use \OAuth\RemoteResourceServer as RemoteResourceServer; +use \OAuth\RemoteResourceServerException as RemoteResourceServerException; + +class RemoteResourceServerTest extends PHPUnit_Framework_TestCase +{ + + private $_dataPath; + + public function setUp() + { + $this->_dataPath = "file://" . dirname(__DIR__) . DIRECTORY_SEPARATOR . "data/"; + } + + public function testBasicToken() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array("Authorization" => "Bearer 001"), array()); + $this->assertEquals("fkooman", $introspection->getSub()); + $this->assertEquals("testclient", $introspection->getClientId()); + $this->assertEquals(2366377846, $introspection->getExpiresAt()); + $this->assertEquals(1366376612, $introspection->getIssuedAt()); + $this->assertEquals("foo bar", $introspection->getScope()); + $this->assertEquals(array("urn:x-foo:service:access","urn:x-bar:privilege:admin"), $introspection->getEntitlement()); + $this->assertTrue($introspection->getActive()); + } + + public function testBasicTokenNoEntitlement() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array(), array("access_token" => "002")); + $this->assertEquals("frko", $introspection->getSub()); + $this->assertEquals("testclient", $introspection->getClientId()); + $this->assertEquals(2366377846, $introspection->getExpiresAt()); + $this->assertEquals(1366376612, $introspection->getIssuedAt()); + $this->assertEquals("a b c", $introspection->getScope()); + $this->assertFalse($introspection->getEntitlement()); + $this->assertTrue($introspection->getActive()); + } + + public function testInvalidToken() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array(), array("access_token" => "003")); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + $this->assertEquals("invalid_token", $e->getMessage()); + $this->assertEquals("the token is not active", $e->getDescription()); + $this->assertEquals(401, $e->getResponseCode()); + $this->assertEquals('Bearer realm="Resource Server",error="invalid_token",error_description="the token is not active"', $e->getAuthenticateHeader()); + } + } + + public function testInvalidIntrospectionResponse() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array("Authorization" => "Bearer 100"), array()); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + $this->assertEquals("internal_server_error", $e->getMessage()); + $this->assertEquals("unexpected response from introspection endpoint", $e->getDescription()); + $this->assertEquals(500, $e->getResponseCode()); + $this->assertNull($e->getAuthenticateHeader()); + } + } + + public function testNoJsonResponse() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array("Authorization" => "Bearer 101"), array()); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + $this->assertEquals("internal_server_error", $e->getMessage()); + $this->assertEquals("unable to decode response from introspection endpoint", $e->getDescription()); + $this->assertEquals(500, $e->getResponseCode()); + $this->assertNull($e->getAuthenticateHeader()); + } + } + + public function testMultipleBearerTokens() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array("Authorization" => "Bearer 003"), array("access_token" => "003")); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + $this->assertEquals("invalid_request", $e->getMessage()); + $this->assertEquals("more than one method for including an access token used", $e->getDescription()); + $this->assertEquals(400, $e->getResponseCode()); + $this->assertEquals('Bearer realm="Resource Server",error="invalid_request",error_description="more than one method for including an access token used"', $e->getAuthenticateHeader()); + } + } + + public function testExt() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array("Authorization" => "Bearer 004"), array()); + $this->assertTrue($introspection->getActive()); + $this->assertEquals(array("uid" => array("admin"), "schacHomeOrganization" => array("localhost")), $introspection->getExt()); + } + + public function testNoBearerTokens() + { + $config = array( + "introspectionEndpoint" => $this->_dataPath, + ); + try { + $rs = new RemoteResourceServer($config); + $introspection = $rs->verifyRequest(array(), array()); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + $this->assertEquals("no_token", $e->getMessage()); + $this->assertEquals("missing token", $e->getDescription()); + $this->assertEquals(401, $e->getResponseCode()); + $this->assertEquals('Bearer realm="Resource Server"', $e->getAuthenticateHeader()); + } + } +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/TokenIntrospectionTest.php b/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/TokenIntrospectionTest.php new file mode 100644 index 000000000..e0e274eb0 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/OAuth/TokenIntrospectionTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +require_once dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "OAuth" . DIRECTORY_SEPARATOR . "RemoteResourceServer.php"; + +use \OAuth\TokenIntrospection as TokenIntrospection; +use \OAuth\RemoteResourceServerException as RemoteResourceServerException; + +class TokenIntrospectionTest extends PHPUnit_Framework_TestCase +{ + + /** + * @dataProvider validTokenProvider + */ + public function testTokenIntrospectionTest($token, $active, $expiresAt, $issuedAt, $scope, $entitlement, $clientId, $sub, $aud) + { + $i = new TokenIntrospection($token); + $this->assertEquals($active, $i->getActive()); + $this->assertEquals($expiresAt, $i->getExpiresAt()); + $this->assertEquals($issuedAt, $i->getIssuedAt()); + $this->assertEquals($scope, $i->getScope()); + if (FALSE !== $i->getScope()) { + $eScope = explode(" ", $i->getScope()); + $this->assertEquals($eScope, $i->getScopeAsArray()); + for ( $j = 0; $j < count($eScope); $j++) { + $this->assertTrue($i->hasScope($eScope[$j])); + $this->assertTrue($i->hasAnyScope(array($eScope[$j], "bogus"))); + $i->requireScope($eScope[$j]); + } + } + $e = $i->getEntitlement(); + if (FALSE !== $e) { + for ( $j = 0; $j < count($e); $j++) { + $this->assertTrue($i->hasEntitlement($e[$j])); + $i->requireEntitlement($e[$j]); + } + } + $this->assertEquals($clientId, $i->getClientId()); + $this->assertEquals($sub, $i->getResourceOwnerId()); + $this->assertEquals($sub, $i->getSub()); + $this->assertEquals($aud, $i->getAud()); + try { + $i->requireScope("bogus"); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + } + try { + $i->requireEntitlement("bogus"); + $this->assertTrue(FALSE); + } catch (RemoteResourceServerException $e) { + } + + $this->assertFalse($i->hasAnyScope(array("foo"))); + } + + public function validTokenProvider() + { + $iat = time(); + $exp = $iat + 100; + + return array( + array( + array("active" => TRUE), + TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE + ), + + array( + array("active" => TRUE, "exp" => $exp, "iat" => $iat, "scope" => "read write", "client_id" => "foo", "sub" => "fkooman", "aud" => "foobar"), + TRUE, $exp, $iat, "read write", FALSE, "foo", "fkooman", "foobar" + ), + + array( + array("active" => TRUE, "exp" => $exp, "iat" => $iat, "scope" => "read write", "x-entitlement" => array("manager", "owner", "user"), "client_id" => "foo", "sub" => "fkooman", "aud" => "foobar"), + TRUE, $exp, $iat, "read write", array("manager", "owner", "user"), "foo", "fkooman", "foobar" + ), + ); + } + +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/001.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/001.json new file mode 100644 index 000000000..f844946a5 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/001.json @@ -0,0 +1,12 @@ +{ + "active": true, + "client_id": "testclient", + "exp": 2366377846, + "iat": 1366376612, + "scope": "foo bar", + "sub": "fkooman", + "x-entitlement": [ + "urn:x-foo:service:access", + "urn:x-bar:privilege:admin" + ] +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/002.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/002.json new file mode 100644 index 000000000..a45f9ccee --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/002.json @@ -0,0 +1,8 @@ +{ + "active": true, + "client_id": "testclient", + "exp": 2366377846, + "iat": 1366376612, + "scope": "a b c", + "sub": "frko" +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/003.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/003.json new file mode 100644 index 000000000..bba306789 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/003.json @@ -0,0 +1,3 @@ +{ + "active": false +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/004.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/004.json new file mode 100644 index 000000000..a1b9abf4f --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/004.json @@ -0,0 +1,19 @@ +{ + "active": true, + "client_id": "html-manage-authorizations", + "exp": 2366844432, + "iat": 1366815632, + "scope": "authorizations", + "sub": "48a5788a56b3dc9035e981aa9c8924360c1906e4", + "x-entitlement": [ + "urn:x-oauth:entitlement:applications" + ], + "x-ext": { + "schacHomeOrganization": [ + "localhost" + ], + "uid": [ + "admin" + ] + } +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/100.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/100.json new file mode 100644 index 000000000..357d63809 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/100.json @@ -0,0 +1,3 @@ +{ + "invalid-format": false +} diff --git a/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/101.json b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/101.json new file mode 100644 index 000000000..6089c5d95 --- /dev/null +++ b/user_oauth/3rdparty/php-oauth-lib-rs/tests/data/101.json @@ -0,0 +1 @@ +NO JSON diff --git a/user_oauth/README.md b/user_oauth/README.md index 08b703d49..43b007fa4 100644 --- a/user_oauth/README.md +++ b/user_oauth/README.md @@ -1,19 +1,21 @@ # Introduction -This version implements the server side OAuth "Bearer" token verification -against an external authorization server. It aims at supporting both -the [php-oauth](https://github.com/fkooman/php-oauth) service and the -[Google](https://developers.google.com/accounts/docs/OAuth2Login#validatingtoken) service. +This app implements server side OAuth 2.0 "Bearer" token verification against +an external authorization server. It aims at supporting the +[php-oauth](https://github.com/fkooman/php-oauth) service. However, any other +OAuth 2.0 AS supporting `draft-richer-oauth-introspection` should work. # Requirements * PHP cURL extension * Apache (because we use `apache_request_headers()` at the moment) # Installation -Install this code in the directory `user_oauth` in the `apps` directory of -your Owncloud installation. +Install this code in the directory `user_oauth` in the `apps` directory of your +ownCloud installation. -This module needs an external dependency to verify the OAuth tokens at the -OAuth authorization server. A script can be used to install this dependency: +This module needs an external library to verify the OAuth tokens at the OAuth +authorization server. A script can be used to install this dependency, by +default is in included in the `3rdparty` directory. So you only need this if +you want to download the library again or update it. $ cd /path/to/owncloud/apps/user_oauth $ cd 3rdparty @@ -21,53 +23,43 @@ OAuth authorization server. A script can be used to install this dependency: You need Git installed on your server to fetch the 3rd party dependency. -You can enable the `user_oauth` app after login with the `admin` account. Go -to `Settings`, then `Apps` and finally select the `OAuth` module from the list -of modules, select it and press the `Enable` button. +You can enable the `user_oauth` app after login with the `admin` account. Go to +`Settings`, then `Apps` and finally select the `OAuth` module from the list of +modules, select it and press the `Enable` button. # Configuration -There currently is only one configuration parameter: the Token Info Endpoint. -For quick tests, one can use the playground environment, installed using -[this](https://github.com/fkooman/oauth-install-all) script located at -https://frko.surfnetlabs.nl/workshop/. +There currently is only one configuration parameter: the introspection +endpoint. For quick tests, you can use the playground environment, installed +using [this](https://github.com/fkooman/oauth-install-all) script located at +https://frko.surfnetlabs.nl/workshop/. -For the "workshop" installation the Token Info Endpoint would be +For the "workshop" installation the introspection endpoint would be: - https://frko.surfnetlabs.nl/workshop/php-oauth/tokeninfo.php - -For Google the Token Info Endpoint is: - - https://www.googleapis.com/oauth2/v1/tokeninfo + https://frko.surfnetlabs.nl/workshop/php-oauth/introspect.php You can set this endpoint by going to `Settings`, then `Admin` and then under the section head `OAuth` configure the URL. # Applications An application needs to use the OAuth service to retrieve an access token to -use this with the OAuth enabled WebDAV endpoint. The endpoint, assuming you -run the service on https://www.example.org/owncloud, note `odav` instead of +use this with the OAuth enabled WebDAV endpoint. The endpoint, assuming you run +the service on https://www.example.org/owncloud, note `odav` instead of `webdav`: https://www.example.org/owncloud/remote.php/odav/<FILE.EXT> -So, in order for an application to work it needs to obtain an access token -from the OAuth authorization server that you configured as a Token Info -Endpoint in the OAuth app configuration in Owncloud. If you used the -playground mentioned above that would mean using the following URLs: +So, in order for an application to work it needs to obtain an access token from +the OAuth authorization server that you configured as an introspection endpoint +in the OAuth app configuration in ownCloud. If you used the playground +mentioned above that would mean using the following URLs for authorization and +token endpoints: https://frko.surfnetlabs.nl/workshop/php-oauth/authorize.php https://frko.surfnetlabs.nl/workshop/php-oauth/token.php -For Google this will probably be some Google URL. You also need to register the -app at Google. In the playground environment you can also register an OAuth -client yourself. - -Template applications for both -[Android](https://github.com/OpenConextApps/android-oauth-app) and -[iOS](https://github.com/OpenConextApps/ios-oauth-app) are available that -implement OAuth 2.0 and can be used to modify the Owncloud Mobile Apps. - -So far, the Owncloud Mobile Apps have not been updated to support OAuth 2.0. +It seems the Android app of ownCloud should support OAuth at the server, but so +far we were unable to make it work. We tested version 1.4.1 of the Android app +from the F-Droid repository. # Compatibilty -The app was tested with version 5 of Owncloud. +The app was tested with version 5 of ownCloud. diff --git a/user_oauth/ajax/seturl.php b/user_oauth/ajax/seturl.php index 2f3428766..41cf361dd 100644 --- a/user_oauth/ajax/seturl.php +++ b/user_oauth/ajax/seturl.php @@ -9,6 +9,6 @@ OCP\User::checkAdminUser(); OCP\JSON::callCheck(); -OCP\Config::setSystemValue( 'tokenInfoEndpoint', $_POST['tokenInfoEndpoint'] ); +OCP\Config::setSystemValue( 'introspectionEndpoint', $_POST['introspectionEndpoint'] ); echo 'true'; diff --git a/user_oauth/appinfo/version b/user_oauth/appinfo/version index 5625e59da..7e32cd569 100644 --- a/user_oauth/appinfo/version +++ b/user_oauth/appinfo/version @@ -1 +1 @@ -1.2 +1.3 diff --git a/user_oauth/js/admin.js b/user_oauth/js/admin.js index 1456c22e7..7cb8f4fd7 100644 --- a/user_oauth/js/admin.js +++ b/user_oauth/js/admin.js @@ -2,9 +2,9 @@ $(document).ready(function(){ - $('#tokenInfoEndpoint').blur(function(event){ + $('#introspectionEndpoint').blur(function(event){ event.preventDefault(); - var post = $( "#tokenInfoEndpoint" ).serialize(); + var post = $( "#introspectionEndpoint" ).serialize(); $.post( OC.filePath('user_oauth', 'ajax', 'seturl.php') , post, function(data){ $('#user_oauth .msg').text('Finished saving: ' + data); }); diff --git a/user_oauth/oauth.php b/user_oauth/oauth.php index 0015df6bc..f5f0133d9 100644 --- a/user_oauth/oauth.php +++ b/user_oauth/oauth.php @@ -1,6 +1,6 @@ <?php -require_once '3rdparty/php-lib-remote-rs/lib/OAuth/RemoteResourceServer.php'; +require_once '3rdparty/php-oauth-lib-rs/lib/OAuth/RemoteResourceServer.php'; use \OAuth\RemoteResourceServer as RemoteResourceServer; use \OAuth\RemoteResourceServerException as RemoteResourceServerException; @@ -8,15 +8,12 @@ use \OAuth\RemoteResourceServerException as RemoteResourceServerException; class OC_Connector_Sabre_OAuth implements Sabre_DAV_Auth_IBackend { private $currentUser; - private $tokenInfoEndpoint; - private $useResourceOwnerId; - private $userIdAttributeName; + private $introspectionEndpoint; - public function __construct($tokenInfoEndpoint, $useResourceOwnerId = TRUE, $userIdAttributeName = "uid") + public function __construct($introspectionEndpoint) { - $this->tokenInfoEndpoint = $tokenInfoEndpoint; - $this->useResourceOwnerId = $useResourceOwnerId; - $this->userIdAttributeName = $userIdAttributeName; + $this->introspectionEndpoint = $introspectionEndpoint; + $this->currentUser = null; } public function getCurrentUser() @@ -27,42 +24,34 @@ class OC_Connector_Sabre_OAuth implements Sabre_DAV_Auth_IBackend public function authenticate(Sabre_DAV_Server $server, $realm) { $config = array( - "tokenInfoEndpoint" => $this->tokenInfoEndpoint, - "throwException" => TRUE, - "resourceServerRealm" => $realm, + "introspectionEndpoint" => $this->introspectionEndpoint, + "realm" => $realm ); try { $resourceServer = new RemoteResourceServer($config); - - $resourceServer->verifyRequest(); - - if ($this->useResourceOwnerId) { - // when using the user_id - $this->currentUser = $resourceServer->getResourceOwnerId(); - } else { - // when using a (SAML) attribute - $attributes = $resourceServer->getAttributes(); - $this->currentUser = $attributes[$this->userIdAttributeName][0]; - } + $tokenIntrospection = $resourceServer->verifyRequest(apache_request_headers(), $_GET); + $this->currentUser = $tokenIntrospection->getSub(); OC_User::setUserid($this->currentUser); OC_Util::setupFS($this->currentUser); return true; - } catch (RemoteResourceServerException $e) { - $server->httpResponse->setHeader('WWW-Authenticate', $e->getAuthenticateHeader()); - - // FIXME: do we need to set the status here explicitly, or does the - // Exception below take care of this? - $server->httpResponse->sendStatus($e->getResponseCode()); - if ("403" === $e->getResponseCode()) { - throw new Sabre_DAV_Exception_Forbidden($e->getDescription()); - } else { - throw new Sabre_DAV_Exception_NotAuthenticated($e->getDescription()); + switch ($e->getMessage()) { + case "insufficient_entitlement": + case "insufficient_scope": + $server->httpResponse->setHeader('WWW-Authenticate', $e->getAuthenticateHeader()); + throw new Sabre_DAV_Exception_Forbidden($e->getDescription()); + case "invalid_request": + throw new Sabre_DAV_Exception_NotAuthenticated($e->getDescription()); + case "invalid_token": + case "no_token": + $server->httpResponse->setHeader('WWW-Authenticate', $e->getAuthenticateHeader()); + throw new Sabre_DAV_Exception_NotAuthenticated($e->getDescription()); + case "internal_server_error": + throw new Sabre_DAV_Exception($e->getDescription()); } } } - } diff --git a/user_oauth/remote.php b/user_oauth/remote.php index 7b6920619..94b20937a 100644 --- a/user_oauth/remote.php +++ b/user_oauth/remote.php @@ -26,14 +26,12 @@ $RUNTIME_APPTYPES=array('filesystem','authentication'); OC_App::loadApps($RUNTIME_APPTYPES); -$tokenInfoEndpoint = \OC_Config::getValue( "tokenInfoEndpoint", "https://www.googleapis.com/oauth2/v1/tokeninfo" ); -$useResourceOwnerId = TRUE; // FIXME: take this from configuration instead -$userIdAttributeName = "uid"; // FIXME: take this from configuration instead +$introspectionEndpoint = \OC_Config::getValue( "introspectionEndpoint", "https://frko.surfnetlabs.nl/workshop/php-oauth/introspect.php" ); require_once 'oauth.php'; // Backends -$authBackend = new OC_Connector_Sabre_OAuth($tokenInfoEndpoint, $useResourceOwnerId, $userIdAttributeName); +$authBackend = new OC_Connector_Sabre_OAuth($introspectionEndpoint); $lockBackend = new OC_Connector_Sabre_Locks(); $requestBackend = new OC_Connector_Sabre_Request(); diff --git a/user_oauth/settings.php b/user_oauth/settings.php index 077caae11..04ce8a110 100644 --- a/user_oauth/settings.php +++ b/user_oauth/settings.php @@ -6,6 +6,6 @@ OCP\Util::addScript( "user_oauth", "admin" ); $tmpl = new OCP\Template( 'user_oauth', 'settings'); -$tmpl->assign('tokenInfoEndpoint', OCP\Config::getSystemValue( "tokenInfoEndpoint", 'https://www.googleapis.com/oauth2/v1/tokeninfo' )); +$tmpl->assign('introspectionEndpoint', OCP\Config::getSystemValue( "introspectionEndpoint", 'https://frko.surfnetlabs.nl/workshop/php-oauth/introspect.php' )); return $tmpl->fetchPage(); diff --git a/user_oauth/templates/settings.php b/user_oauth/templates/settings.php index 37ee75a4a..f3ff68cd7 100644 --- a/user_oauth/templates/settings.php +++ b/user_oauth/templates/settings.php @@ -1,8 +1,8 @@ <form id="user_oauth"> <fieldset class="personalblock"> <strong>OAuth</strong><br /> - <input type="text" name="tokenInfoEndpoint" id="tokenInfoEndpoint" value="<?php p($_['tokenInfoEndpoint']); ?>" placeholder="<?php p($l->t('Token Info Endpoint'));?>" /> + <input type="text" name="introspectionEndpoint" id="introspectionEndpoint" value="<?php p($_['introspectionEndpoint']); ?>" placeholder="<?php p($l->t('Introspection endpoint'));?>" /> <br /> - <span class="msg">Provide the OAuth Authorization Server Token Info Endpoint here.</span> + <span class="msg">Provide the OAuth 2.0 Authorization Server introspection endpoint here.</span> </fieldset> </form> |