diff --git a/Core/composer.json b/Core/composer.json index 4adace683843..71bec88788ed 100644 --- a/Core/composer.json +++ b/Core/composer.json @@ -6,7 +6,7 @@ "require": { "php": ">=5.5", "rize/uri-template": "~0.3", - "google/auth": "^1.5.1", + "google/auth": "^1.6", "guzzlehttp/guzzle": "^5.3|^6.0", "guzzlehttp/promises": "^1.3", "guzzlehttp/psr7": "^1.2", diff --git a/PubSub/src/Subscription.php b/PubSub/src/Subscription.php index b1864b2cb2cb..c4e1de118f77 100644 --- a/PubSub/src/Subscription.php +++ b/PubSub/src/Subscription.php @@ -56,6 +56,25 @@ * 'my-new-topic' * ); * ``` + * + * ``` + * // Consuming messages received via push with JWT Authentication. + * use Google\Auth\AccessToken; + * + * // Remove the `Bearer ` prefix from the token. + * // If using another request manager such as Symfony HttpFoundation, + * // use `Authorization` as the header name, e.g. `$request->headers->get('Authorization')`. + * $jwt = explode(' ', $_SERVER['HTTP_AUTHORIZATION'])[1]; + * + * // Using the Access Token utility requires installation of the `phpseclib/phpseclib` dependency at version 2. + * $accessTokenUtility = new AccessToken(); + * $payload = $accessTokenUtility->verify($jwt); + * if (!$payload) { + * throw new \RuntimeException('Could not verify token!'); + * } + * + * echo 'Authenticated using ' . $payload['email']; + * ``` */ class Subscription { @@ -185,15 +204,25 @@ public function name() * @param array $options [optional] { * Configuration Options * - * For information regarding the push configuration settings, see - * [PushConfig](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions#PushConfig). - * * @type string $pushConfig.pushEndpoint A URL locating the endpoint to which * messages should be pushed. For example, a Webhook endpoint * might use "https://example.com/push". * @type array $pushConfig.attributes Endpoint configuration attributes. - * @type array $pushConfig.oidcToken Contains information needed for generating - * an OpenIDConnect token. + * @type array $pushConfig.oidcToken If specified, Pub/Sub will generate + * and attach an OIDC JWT token as an `Authorization` header in + * the HTTP request for every pushed message. + * @type string $pushConfig.oidcToken.serviceAccountEmail Service + * account email to be used for generating the OIDC token. The + * caller (for `subscriptions.create`, `UpdateSubscription`, and + * `subscriptions.modifyPushConfig` RPCs) must have the + * `iam.serviceAccounts.actAs` permission for the service account. + * @type string $pushConfig.oidcToken.audience Audience to be used when + * generating OIDC token. The audience claim identifies the + * recipients that the JWT is intended for. The audience value is + * a single case-sensitive string. Having multiple values (array) + * for the audience field is not supported. More info about the + * OIDC JWT token audience here: https://tools.ietf.org/html/rfc7519#section-4.1.3 + * Note: if not specified, the Push endpoint URL will be used. * @type int $ackDeadlineSeconds The maximum time after a subscriber * receives a message before the subscriber should acknowledge the * message. @@ -276,16 +305,30 @@ public function create(array $options = []) * ]); * ``` * + * @see https://cloud.google.com/pubsub/docs/reference/rest/v1/UpdateSubscriptionRequest UpdateSubscriptionRequest + * * @param array $subscription { * The Subscription data. * - * For information regarding the push configuration settings, see - * [PushConfig](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions#PushConfig). - * * @type string $pushConfig.pushEndpoint A URL locating the endpoint to which * messages should be pushed. For example, a Webhook endpoint * might use "https://example.com/push". * @type array $pushConfig.attributes Endpoint configuration attributes. + * @type array $pushConfig.oidcToken If specified, Pub/Sub will generate + * and attach an OIDC JWT token as an `Authorization` header in + * the HTTP request for every pushed message. + * @type string $pushConfig.oidcToken.serviceAccountEmail Service + * account email to be used for generating the OIDC token. The + * caller (for `subscriptions.create`, `UpdateSubscription`, and + * `subscriptions.modifyPushConfig` RPCs) must have the + * `iam.serviceAccounts.actAs` permission for the service account. + * @type string $pushConfig.oidcToken.audience Audience to be used when + * generating OIDC token. The audience claim identifies the + * recipients that the JWT is intended for. The audience value is + * a single case-sensitive string. Having multiple values (array) + * for the audience field is not supported. More info about the + * OIDC JWT token audience here: https://tools.ietf.org/html/rfc7519#section-4.1.3 + * Note: if not specified, the Push endpoint URL will be used. * @type int $ackDeadlineSeconds The maximum time after a subscriber * receives a message before the subscriber should acknowledge the * message. diff --git a/PubSub/tests/Snippet/SubscriptionTest.php b/PubSub/tests/Snippet/SubscriptionTest.php index e7da11dc5404..bff3b32c8cdc 100644 --- a/PubSub/tests/Snippet/SubscriptionTest.php +++ b/PubSub/tests/Snippet/SubscriptionTest.php @@ -17,6 +17,7 @@ namespace Google\Cloud\PubSub\Tests\Snippet; +use Google\Auth\AccessToken; use Google\Cloud\Core\Iam\Iam; use Google\Cloud\Core\Testing\Snippet\SnippetTestCase; use Google\Cloud\Core\Testing\TestHelpers; @@ -71,6 +72,45 @@ public function testClassThroughPubSubClient() $this->assertEquals(self::SUBSCRIPTION, $res->returnVal()->name()); } + public function testAuthenticatedPush() + { + $authToken = 'foobar'; + $email = 'foo@bar.com'; + $token = $this->prophesize(AccessToken::class); + $token->verify($authToken) + ->shouldBeCalled() + ->willReturn([ + 'email' => $email + ]); + + $snippet = $this->snippetFromClass(Subscription::class, 2); + $snippet->replace('$accessTokenUtility = new AccessToken();', ''); + $snippet->addLocal('accessTokenUtility', $token->reveal()); + $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $authToken; + + $res = $snippet->invoke(); + $this->assertEquals('Authenticated using ' . $email, $res->output()); + } + + /** + * @expectedException \RuntimeException + */ + public function testAuthenticatedPushFails() + { + $authToken = 'foobar'; + $token = $this->prophesize(AccessToken::class); + $token->verify($authToken) + ->shouldBeCalled() + ->willReturn(false); + + $snippet = $this->snippetFromClass(Subscription::class, 2); + $snippet->replace('$accessTokenUtility = new AccessToken();', ''); + $snippet->addLocal('accessTokenUtility', $token->reveal()); + $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $authToken; + + $res = $snippet->invoke(); + } + public function testName() { $snippet = $this->snippetFromMethod(Subscription::class, 'name'); diff --git a/composer.json b/composer.json index 4fb9706b5b5e..62fc5ad5e03a 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "ramsey/uuid": "~3", "google/gax": "^1.1", "google/common-protos": "^1.0", - "google/auth": "^1.5.1", + "google/auth": "^1.6", "google/crc32": "^0.1.0" }, "require-dev": {