context = $context; $this->headers = $this->getHeaders(); $this->endpoint_repository = $endpoint_repository; $this->token_service = $token_service; } /** * @param \Illuminate\Http\Request $request * @param Closure $next * @return OAuth2WWWAuthenticateErrorResponse */ public function handle($request, Closure $next) { $url = $request->getRequestUri(); $method = $request->getMethod(); $realm = $request->getHost(); try { $route = RequestUtils::getCurrentRoutePath($request); if (!$route) { throw new OAuth2ResourceServerException( 400, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, sprintf('API endpoint does not exists! (%s:%s)', $url, $method) ); } Log::debug($request->headers->__toString()); // http://tools.ietf.org/id/draft-abarth-origin-03.html $origin = $request->headers->has('Origin') ? $request->headers->get('Origin') : null; if (!empty($origin)) { $nm = new Normalizer($origin); $origin = $nm->normalize(); } //check first http basic auth header $auth_header = isset($this->headers['authorization']) ? $this->headers['authorization'] : null; if (!is_null($auth_header) && !empty($auth_header)) { $access_token_value = BearerAccessTokenAuthorizationHeaderParser::getInstance()->parse($auth_header); } else { // http://tools.ietf.org/html/rfc6750#section-2- 2 // if access token is not on authorization header check on POST/GET params $access_token_value = Request::input(OAuth2Protocol::OAuth2Protocol_AccessToken, ''); } if (is_null($access_token_value) || empty($access_token_value)) { //if access token value is not set, then error throw new OAuth2ResourceServerException( 400, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, 'missing access token' ); } $endpoint = $this->endpoint_repository->getApiEndpointByUrlAndMethod($route, $method); //api endpoint must be registered on db and active if (is_null($endpoint) || !$endpoint->isActive()) { throw new OAuth2ResourceServerException( 400, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, sprintf('API endpoint does not exits! (%s:%s)', $route, $method) ); } $token_info = $this->token_service->get($access_token_value); if(!is_null($token_info)) Log::debug(sprintf("token lifetime %s", $token_info->getLifetime())); //check lifetime if (is_null($token_info)) { throw new InvalidGrantTypeException(OAuth2Protocol::OAuth2Protocol_Error_InvalidToken); } //check token audience Log::debug('checking token audience ...'); $audience = explode(' ', $token_info->getAudience()); if ((!in_array($realm, $audience))) { throw new InvalidGrantTypeException(OAuth2Protocol::OAuth2Protocol_Error_InvalidToken); } if ( $token_info->getApplicationType() === 'JS_CLIENT' && (is_null($origin) || empty($origin)|| str_contains($token_info->getAllowedOrigins(), $origin) === false ) ) { //check origins throw new OAuth2ResourceServerException( 403, OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient, sprintf('invalid origin %s - allowed ones (%s)', $origin, $token_info->getAllowedOrigins()) ); } //check scopes Log::debug('checking token scopes ...'); $endpoint_scopes = $endpoint->getScopesNames(); Log::debug(sprintf("endpoint scopes %s", implode(' ',$endpoint_scopes))); Log::debug(sprintf("token scopes %s", $token_info->getScope())); $token_scopes = explode(' ', $token_info->getScope()); //check token available scopes vs. endpoint scopes if (count(array_intersect($endpoint_scopes, $token_scopes)) == 0) { Log::warning( sprintf( 'access token scopes (%s) does not allow to access to api url %s , needed scopes %s', $token_info->getScope(), $url, implode(' OR ', $endpoint_scopes) ) ); throw new OAuth2ResourceServerException( 403, OAuth2Protocol::OAuth2Protocol_Error_InsufficientScope, 'the request requires higher privileges than provided by the access token', implode(' ', $endpoint_scopes) ); } Log::debug('setting resource server context ...'); //set context for api and continue processing $context = [ 'access_token' => $access_token_value, 'expires_in' => $token_info->getLifetime(), 'client_id' => $token_info->getClientId(), 'scope' => $token_info->getScope(), 'application_type' => $token_info->getApplicationType(), 'allowed_origins' => $token_info->getAllowedOrigins(), 'allowed_return_uris' => $token_info->getAllowedReturnUris() ]; if (!is_null($token_info->getUserId())) { Log::debug(sprintf("OAuth2BearerAccessTokenRequestValidator::handle user id is not null (%s)", $token_info->getUserId())); $context['user_id'] = $token_info->getUserId(); $context['user_external_id'] = $token_info->getUserExternalId(); $context['user_identifier'] = $token_info->getUserIdentifier(); $context['user_email'] = $token_info->getUserEmail(); $context['user_first_name'] = $token_info->getUserFirstName(); $context['user_last_name'] = $token_info->getUserLastName(); $context['user_groups'] = $token_info->getUserGroups(); } $this->context->setAuthorizationContext($context); } catch (OAuth2ResourceServerException $ex1) { Log::warning($ex1); $response = new OAuth2WWWAuthenticateErrorResponse( $realm, $ex1->getError(), $ex1->getErrorDescription(), $ex1->getScope(), $ex1->getHttpCode() ); $http_response = Response::json($response->getContent(), $response->getHttpCode()); $http_response->header('WWW-Authenticate', $response->getWWWAuthenticateHeaderValue()); return $http_response; } catch (InvalidGrantTypeException $ex2) { Log::warning($ex2); $response = new OAuth2WWWAuthenticateErrorResponse( $realm, OAuth2Protocol::OAuth2Protocol_Error_InvalidToken, 'the access token provided is expired, revoked, malformed, or invalid for other reasons.', null, 401 ); $http_response = Response::json($response->getContent(), $response->getHttpCode()); $http_response->header('WWW-Authenticate', $response->getWWWAuthenticateHeaderValue()); return $http_response; } catch (\Exception $ex) { Log::error($ex); $response = new OAuth2WWWAuthenticateErrorResponse( $realm, OAuth2Protocol::OAuth2Protocol_Error_InvalidRequest, 'invalid request', null, 400 ); $http_response = Response::json($response->getContent(), $response->getHttpCode()); $http_response->header('WWW-Authenticate', $response->getWWWAuthenticateHeaderValue()); return $http_response; } $response = $next($request); return $response; } /** * @return array */ protected function getHeaders() { $headers = []; if (function_exists('getallheaders')) { foreach (getallheaders() as $name => $value) { $headers[strtolower($name)] = $value; } } if(count($headers) == 0 ) { foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); $headers[strtolower($name)] = $value; } } foreach (Request::header() as $name => $value) { if (!array_key_exists($name, $headers)) { $headers[strtolower($name)] = $value[0]; } } } return $headers; } }