授予商家账号访问权限

以下示例可用于授权:

Java

package shopping.common;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.DataStoreFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Set;

/**
 * Class that contains all the authentication logic, including choosing between service account
 * credentials and OAuth2 client credentials.
 */
public class Authenticator {

  private Set<String> scopes;
  private Config config;
  private HttpTransport httpTransport;
  private JsonFactory jsonFactory;
  private DataStoreFactory dataStoreFactory;

  public Authenticator(
      HttpTransport httpTransport, JsonFactory jsonFactory, Set<String> scopes, Config config)
      throws IOException {
    this.scopes = scopes;
    this.httpTransport = httpTransport;
    this.jsonFactory = jsonFactory;
    this.config = config;
    this.dataStoreFactory = new ConfigDataStoreFactory(config);
  }

  public Credential authenticate() throws IOException {
    try {
      Credential credential = GoogleCredential.getApplicationDefault().createScoped(scopes);
      System.out.println("Loaded the Application Default Credentials.");
      return credential;
    } catch (IOException e) {
      // No need to do anything, we'll fall back on other credentials.
    }
    if (config.getPath() == null) {
      throw new IllegalArgumentException(
          "Must use Application Default Credentials with no configuration directory.");
    }
    File serviceAccountFile = new File(config.getPath(), "service-account.json");
    if (serviceAccountFile.exists()) {
      System.out.println("Loading service account credentials.");
      try (InputStream inputStream = new FileInputStream(serviceAccountFile)) {
        GoogleCredential credential =
            GoogleCredential.fromStream(inputStream, httpTransport, jsonFactory)
                .createScoped(scopes);
        System.out.printf(
            "Loaded service account credentials for %s%n", credential.getServiceAccountId());
        // GoogleCredential.fromStream does NOT refresh, since scopes must be added after.
        if (!credential.refreshToken()) {
          System.out.println("The service account access token could not be refreshed.");
          System.out.println("The service account key may have been deleted in the API Console.");
          throw new IOException("Failed to refresh service account token");
        }
        return credential;
      } catch (IOException e) {
        throw new IOException(
            "Could not retrieve service account credentials from the file "
                + serviceAccountFile.getCanonicalPath(), e);
      }
    }
    File clientSecretsFile = new File(config.getPath(), "client-secrets.json");
    if (clientSecretsFile.exists()) {
      System.out.println("Loading OAuth2 client credentials.");
      try (InputStream inputStream = new FileInputStream(clientSecretsFile)) {
        GoogleClientSecrets clientSecrets =
            GoogleClientSecrets.load(jsonFactory, new InputStreamReader(inputStream));
        // set up authorization code flow
        GoogleAuthorizationCodeFlow flow =
            new GoogleAuthorizationCodeFlow.Builder(
                    httpTransport, jsonFactory, clientSecrets, scopes)
                .setDataStoreFactory(dataStoreFactory)
                .build();
        // authorize
        String userID = ConfigDataStoreFactory.UNUSED_ID;
        Credential storedCredential = flow.loadCredential(userID);
        if (storedCredential != null) {
          System.out.printf("Retrieved stored credential for %s from cache.%n", userID);
          return storedCredential;
        }
        LocalServerReceiver receiver =
            new LocalServerReceiver.Builder().setHost("localhost").setPort(9999).build();
        Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize(userID);
        System.out.printf("Retrieved credential for %s from web.%n", userID);
        return credential;
      } catch (IOException e) {
        throw new IOException(
            "Could not retrieve OAuth2 client credentials from the file "
                + clientSecretsFile.getCanonicalPath());
      }
    }
    throw new IOException(
        "No authentication credentials found. Checked the Google Application"
            + "Default Credentials and the paths "
            + serviceAccountFile.getCanonicalPath()
            + " and "
            + clientSecretsFile.getCanonicalPath()
            + ". Please read the accompanying README.");
  }
}

PHP

<?php

/**
 * Copyright 2023 Google LLC
 *
 * 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
 *
 *     https://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.
 */


require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/Config.php';

use Google\Auth\CredentialsLoader;
use Google\Auth\OAuth2;
use Google\Auth\Credentials\UserRefreshCredentials;
use Psr\Http\Message\ServerRequestInterface;
use React\EventLoop\Loop;
use React\Http\HttpServer;
use React\Http\Message\Response;
use React\Socket\SocketServer;

/*
 * A class that provides authentication credentials to use the merchant API.
 */
class Authentication
{
    private const SCOPE = 'https://www.googleapis.com/auth/content';
    private const AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/v2/auth';
    private const OAUTH2_CALLBACK_IP_ADDRESS = '127.0.0.1';

    /**
     * This method is called on each sample request, using either the service
     * account or stored token.json file to get valid authentication
     * credentials.
     */
    public static function useServiceAccountOrTokenFile()
    {
        $config = Config::generateConfig();

        print "Attempting to load service account information." . PHP_EOL;
        if (file_exists($config['serviceAccountFile'])) {
            print 'Service account file exists, will use service account '
                . 'to authenticate.'
                . PHP_EOL;
            return $config['serviceAccountFile'];
        } else {
            print 'Service account file does not exist, attempting to load '
                . 'token file.'
                . PHP_EOL;

            // Check if the token file exists, and if so, return the contents of the file. Ensure
            // you are running this code from the root directory of the PHP samples.
            if (file_exists($config['tokenFile'])) {
                // Read in the object of `refresh_token`, `client_secret` and
                // `client_id` and cast it to an array.
                $tokenJSON = (array) json_decode(
                    file_get_contents($config['tokenFile'])
                );

                // Create OAuth credentials to be used in the merchant api
                // client requests.
                $credentials = new UserRefreshCredentials(
                    scope: null,
                    jsonKey: $tokenJSON
                );
                print 'Token file exists, will use token file to authenticate.'
                    . PHP_EOL;
                return $credentials;
            } else {
                print 'Token file does not exist, attempting to see if client '
                    . 'secrets file exists.'
                    . PHP_EOL;

                if (file_exists($config['clientSecretsFile'])) {
                    throw new Exception(
                        'Client secrets file exists, please run the '
                            . '`GenerateUserCredentials example to use your client '
                            . 'secrets to get OAuth2 refresh token. Then re-run your '
                            . 'sample.'
                            . PHP_EOL
                    );
                } else {
                    throw new Exception(
                        'Service account file, token file, and client secrets '
                            . 'file do not exist. Please follow the instructions in '
                            . 'the top level ReadMe to create a service account or '
                            . 'client secrets file.'
                            . PHP_EOL
                    );
                }
            }
        }
    }

    /**
     * This function goes through the OAuth Flow with your client secrets
     * to generate and store a token.json file to use for authentication.
     */
    public static function generateUserCredentials(): void
    {
        $config = Config::generateConfig();

        print 'Token file does not exist, attempting to load client secrets '
            . 'file.'
            . PHP_EOL;

        if (file_exists($config['clientSecretsFile'])) {
            print 'Client secrets file exists, will use client secrets to get '
                . 'an OAuth2 refresh token.'
                . PHP_EOL;


            if (!class_exists(HttpServer::class)) {
                print 'Please install "react/http" package to be able to run '
                    . 'this example';
                    throw new Exception('Please install "react/http" package');
            }

            // Creates a socket for localhost with random port. Port 0 is used
            // to tell the SocketServer to create a server with a random port.
            $socket = new SocketServer(self::OAUTH2_CALLBACK_IP_ADDRESS . ':0');

            // Ensure you've created a client secrets file in the appropriate
            // location by following the instructions in the top level ReadMe.
            // Remember that if you are using a web application, you need to add
            // the following to its "Authorized redirect URIs": http://127.0.0.1
            // in your GCP console (https://console.cloud.google.com/).

            // Read the Client Secrets JSON file.
            $json = file_get_contents($config['clientSecretsFile']);

            // Decode the JSON file.
            $json_data = json_decode($json, true);

            $redirectUrl = str_replace('tcp:', 'http:', $socket->getAddress());
            $oauth2 = new OAuth2(
                [
                    'clientId' => $json_data['web']['client_id'],
                    'clientSecret' => $json_data['web']['client_secret'],
                    'authorizationUri' => $json_data['web']['auth_uri'],
                    'redirectUri' => $redirectUrl,
                    'tokenCredentialUri' => $json_data['web']['token_uri'],
                    'scope' => self::SCOPE,
                    // Create a 'state' token to prevent request forgery. See
                    // https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken
                    // for details.
                    'state' => sha1(openssl_random_pseudo_bytes(1024))
                ]
            );

            $authToken = null;

            $server = new HttpServer(
                function (ServerRequestInterface $request) use ($oauth2, &$authToken, $json_data) {
                    // Stops the server after tokens are retrieved.
                    if (!is_null($authToken)) {
                        Loop::stop();
                    }

                    // Check if the requested path is the one set as the
                    // redirect URI. We add '/' here so the parse_url method
                    // can function correctly, since it cannot detect the URI
                    // without '/' at the end, which is the case for the value
                    // of getRedirectUri().
                    if (
                        $request->getUri()->getPath()
                        !== parse_url($oauth2->getRedirectUri() . '/', PHP_URL_PATH)
                    ) {
                        return new Response(
                            404,
                            ['Content-Type' => 'text/plain'],
                            'Page not found'
                        );
                    }

                    // Exit if the state is invalid to prevent request forgery.
                    $state = $request->getQueryParams()['state'];
                    if (empty($state) || ($state !== $oauth2->getState())) {
                        throw new UnexpectedValueException(
                            "The state is empty or doesn't match the expected one."
                                . PHP_EOL
                        );
                    };

                    // Set the authorization code and fetch refresh and access
                    // tokens.
                    $code = $request->getQueryParams()['code'];
                    $oauth2->setCode($code);
                    $authToken = $oauth2->fetchAuthToken();
                    $refreshToken = $authToken['refresh_token'];
                    print 'Your refresh token is: ' . $refreshToken . PHP_EOL;
                    print 'You can now run any example to automatically use '
                         . 'your new refresh token to generate an access token and '
                         . 'succesfully authenticate your request.'
                         . PHP_EOL;

                    $token_file_credentials = [
                        'client_id' => $json_data['web']['client_id'],
                        'client_secret' => $json_data['web']['client_secret'],
                        'refresh_token' => $refreshToken
                    ];

                    file_put_contents(
                        "token.json",
                        json_encode($token_file_credentials)
                    );

                    return new Response(
                        200,
                        ['Content-Type' => 'text/plain'],
                        'Your refresh token has been fetched. Check the '
                            . 'console output for further instructions.'
                    );
                }
            );

            $server->listen($socket);
            printf(
                'Log into the Google account you use for Google Ads and visit '
                    . 'the following URL in your web browser: %1$s%2$s%1$s%1$s',
                PHP_EOL,
                implode(
                    [$oauth2->buildFullAuthorizationUri(['access_type' => 'offline']), '&prompt=consent']
                )
            );
        } else {
            print 'Client secrets file does not exist. Please follow the '
                . 'instructions in the top level ReadMe to create a client secrets '
                . 'file.'
                . PHP_EOL;
        }
    }
}

Python

# -*- coding: utf-8 -*-
# Copyright 2024 Google LLC
#
# 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.

"""This example will create credentials to use the Merchant API.

This file authenticates either via a provided service-account.json file, a
stored OAuth2 refresh token, or creates credentials and stores a refresh token
via a provided client-secrets.json file.

This example works with web OAuth client ID types.

https://console.cloud.google.com

IMPORTANT: For web app clients types, you must add "http://127.0.0.1" to the
"Authorized redirect URIs" list in your Google Cloud Console project before
running this example.
"""

import hashlib
import os
import re
import socket
import sys
import urllib.parse

from examples.authentication import configuration
from examples.authentication import token_storage
from google.oauth2 import service_account

# If using Web flow, the redirect URL must match exactly what’s configured in
# GCP for the OAuth client.
from google_auth_oauthlib.flow import Flow


_SCOPE = "https://www.googleapis.com/auth/content"
_SERVER = "127.0.0.1"
_PORT = 8080

_REDIRECT_URI = f"http://{_SERVER}:{_PORT}"


def main():
  """Generates OAuth2 credentials."""
  # Gets the configuration object that has the paths on the local machine to
  # the `service-account.json`, `token.json`, and `client-secrets.json` files.
  config = configuration.Configuration().get_config()
  service_account_path = config["service_account_path"]
  print("Attempting to use service account credentials from "
        f"{service_account_path}.")
  if os.path.isfile(service_account_path):
    print("Service account credentials found. Attempting to authenticate.")
    credentials = service_account.Credentials.from_service_account_file(
        service_account_path,
        scopes=[_SCOPE])
    return credentials
  else:
    print("Service account credentials not found.")
    full_token_path = os.path.join(os.getcwd(), config["token_path"])
    print(f"Attempting to use stored token data from {full_token_path}")
    if os.path.isfile(config["token_path"]):
      print("Token file found.")
      print("Attempting to use token file to authenticate")
      return get_credentials_from_token(config)
    else:
      print("Token file not found.")
      client_secrets_path = config["client_secrets_path"]
      print(f"Attempting to use client secrets from {client_secrets_path}.")
      if os.path.isfile(client_secrets_path):
        print("Client secrets file found.")
        print("Attempting to use client secrets to authenticate")
        return get_credentials_from_client_secrets(config)
      else:
        print("Service account file, token file, and client secrets "
              "file do not exist. Please follow the instructions in "
              "the top level ReadMe to create a service account or "
              "client secrets file.")
        exit(1)


def get_credentials_from_token(config):
  """Generates OAuth2 refresh token from stored local token file."""
  credentials = token_storage.Storage(config, _SCOPE).get()
  return credentials


def get_credentials_from_client_secrets(config):
  """Generates OAuth2 refresh token using the Web application flow.

  To retrieve the necessary client_secrets JSON file, first
  generate OAuth 2.0 credentials of type Web application in the
  Google Cloud Console (https://console.cloud.google.com).
  Make sure "http://_SERVER:_PORT" is included the list of
  "Authorized redirect URIs" for this client ID."

  Starts a basic server and initializes an auth request.

  Args:
    config: an instance of the Configuration object.

  Returns:
    Credentials used to authenticate with the Merchant API.
  """
  # A list of API scopes to include in the auth request, see:
  # https://developers.google.com/identity/protocols/oauth2/scopes
  scopes = [_SCOPE]

  # A path to where the client secrets JSON file is located
  # on the machine running this example.
  client_secrets_path = config["client_secrets_path"]

  flow = Flow.from_client_secrets_file(client_secrets_path, scopes=scopes)
  flow.redirect_uri = _REDIRECT_URI

  # Create an anti-forgery state token as described here:
  # https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken
  passthrough_val = hashlib.sha256(os.urandom(1024)).hexdigest()

  authorization_url, state = flow.authorization_url(
      access_type="offline",
      state=passthrough_val,
      prompt="consent",
      include_granted_scopes="true",
  )

  print(f"Your state token is: {state}\n")

  # Prints the authorization URL so you can paste into your browser. In a
  # typical web application you would redirect the user to this URL, and they
  # would be redirected back to "redirect_url" provided earlier after
  # granting permission.
  print("Paste this URL into your browser: ")
  print(authorization_url)
  print(f"\nWaiting for authorization and callback to: {_REDIRECT_URI}")

  # Retrieves an authorization code by opening a socket to receive the
  # redirect request and parsing the query parameters set in the URL.
  code = urllib.parse.unquote(get_authorization_code(passthrough_val))

  # Passes the code back into the OAuth module to get a refresh token.
  flow.fetch_token(code=code)
  refresh_token = flow.credentials.refresh_token

  print(f"\nYour refresh token is: {refresh_token}\n")

  # Stores the provided credentials into the appropriate file.
  storage = token_storage.Storage(config, scopes)
  storage.put(flow.credentials)

  return flow.credentials


def get_authorization_code(passthrough_val):
  """Opens a socket to handle a single HTTP request containing auth tokens.

  Args:
    passthrough_val: an anti-forgery token used to verify the request
      received by the socket.

  Returns:
    a str access token from the Google Auth service.
  """
  # Opens a socket at _SERVER:_PORT and listen for a request.
  sock = socket.socket()
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  sock.bind((_SERVER, _PORT))
  sock.listen(1)
  connection, address = sock.accept()
  print(f"Socket address: {address}")
  data = connection.recv(1024)
  # Parses the raw request to retrieve the URL query parameters.
  params = parse_raw_query_params(data)

  try:
    if not params.get("code"):
      # If no code is present in the query params then there will be an
      # error message with more details.
      error = params.get("error")
      message = f"Failed to retrieve authorization code. Error: {error}"
      raise ValueError(message)
    elif params.get("state") != passthrough_val:
      message = "State token does not match the expected state."
      raise ValueError(message)
    else:
      message = "Authorization code was successfully retrieved."
  except ValueError as error:
    print(error)
    sys.exit(1)
  finally:
    response = (
        "HTTP/1.1 200 OK\n"
        "Content-Type: text/html\n\n"
        f"<b>{message}</b>"
        "<p>Please check the console output.</p>\n"
    )

    connection.sendall(response.encode())
    connection.close()

  return params.get("code")


def parse_raw_query_params(data):
  """Parses a raw HTTP request to extract its query params as a dict.

  Note that this logic is likely irrelevant if you're building OAuth logic
  into a complete web application, where response parsing is handled by a
  framework.

  Args:
    data: raw request data as bytes.

  Returns:
    a dict of query parameter key value pairs.
  """
  # Decodes the request into a utf-8 encoded string.
  decoded = data.decode("utf-8")
  # Uses a regular expression to extract the URL query parameters string.
  params = re.search(r"GET\s\/\?(.*) ", decoded).group(1)

  # Splits the parameters to isolate the key/value pairs.
  pairs = [pair.split("=") for pair in params.split("&")]
  # Converts pairs to a dict to make it easy to access the values.
  return {key: val for key, val in pairs}