Java
// Copyright 2018 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. package com.google.ads.googleads.examples.authentication; import com.beust.jcommander.Parameter; import com.google.ads.googleads.examples.utils.ArgumentNames; import com.google.ads.googleads.examples.utils.CodeSampleParams; import com.google.ads.googleads.lib.GoogleAdsClient; import com.google.ads.googleads.lib.GoogleAdsClient.Builder.ConfigPropertyKey; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpStatusCodes; import com.google.api.client.util.Key; import com.google.auth.oauth2.ClientId; import com.google.auth.oauth2.UserAuthorizer; import com.google.auth.oauth2.UserCredentials; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.math.BigInteger; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Creates an OAuth2 refresh token for the Google Ads API. This example works with both web and * desktop app OAuth client ID types. * * <p>IMPORTANT: For web app clients types, you must add {@code http://127.0.0.1} to the "Authorized * redirect URIs" list in your Google Cloud Console project before running this example. Desktop app * client types do not require the local redirect to be explicitly configured in the console. * * <p>This example will start a basic server that listens for requests at {@code * http://127.0.0.1:PORT}, where {@code PORT} is dynamically assigned. */ public class GenerateUserCredentials { private static class GenerateUserCredentialsParams extends CodeSampleParams { @Parameter( names = ArgumentNames.OAUTH_CLIENT_FILE, required = true, description = "JSON file downloaded from the Google Cloud Console that contains the OAuth client" + " details.") private String oAuthClientFile; @Parameter( names = ArgumentNames.LOGIN_EMAIL_ADDRESS_HINT, required = false, description = "If your application knows which user is trying to authenticate, you can set this to" + " the user's email address so that the Google Authentication Server will" + " automatically populate the account selection prompt with that address.") private String loginEmailAddressHint; @Parameter( names = ArgumentNames.OAUTH_SCOPES, required = false, description = "Scopes for the generated OAuth2 credentials. Set to the Google Ads API scope by" + " default, but you can add multiple scopes if you want to use the credentials for" + " other Google APIs.") private List<String> oAuthScopes = SCOPES; } // Scopes for the generated OAuth2 credentials. The list here only contains the Google Ads API // scope, but you can add multiple scopes if you want to use the credentials for other Google // APIs. private static final ImmutableList<String> SCOPES = ImmutableList.<String>builder().add("https://www.googleapis.com/auth/adwords").build(); private static final String OAUTH2_CALLBACK_BASE_URI = "http://127.0.0.1"; public static void main(String[] args) throws Exception { // To fill in the values below, generate a client ID and client secret from the Google Cloud // Console (https://console.cloud.google.com) by creating credentials for either a web or // desktop app OAuth client ID. // If using a web application, add the following to its "Authorized redirect URIs": // http://127.0.0.1 String clientId; String clientSecret; String loginEmailAddressHint; List<String> scopes = SCOPES; GenerateUserCredentialsParams params = new GenerateUserCredentialsParams(); if (!params.parseArguments(args)) { // Either pass the required parameters for this example on the command line, or insert them // into the code here. See the parameter class definition above for descriptions. clientId = "INSERT_CLIENT_ID_HERE"; clientSecret = "INSERT_CLIENT_SECRET_HERE"; // Optional: If your application knows which user is trying to authenticate, you can set this // to the user's email address so that the Google Authentication Server will automatically // populate the account selection prompt with that address. loginEmailAddressHint = null; // Ensures that the client ID and client secret are not the "INSERT_..._HERE" values. Preconditions.checkArgument( !clientId.matches("INSERT_.*_HERE"), "Client ID is invalid. Please update the example and try again."); Preconditions.checkArgument( !clientSecret.matches("INSERT_.*_HERE"), "Client secret is invalid. Please update the example and try again."); } else { // Extracts the OAuth client information from the provided file. ClientId parsedClient = ClientId.fromStream(new FileInputStream(params.oAuthClientFile)); clientId = parsedClient.getClientId(); clientSecret = parsedClient.getClientSecret(); loginEmailAddressHint = params.loginEmailAddressHint; scopes = params.oAuthScopes; } new GenerateUserCredentials().runExample(clientId, clientSecret, loginEmailAddressHint, scopes); } public void runExample( String clientId, String clientSecret, String loginEmailAddressHint, List<String> scopes) throws Exception { // Creates an anti-forgery state token as described here: // https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken String state = new BigInteger(130, new SecureRandom()).toString(32); // Creates an HTTP server that will listen for the OAuth2 callback request. URI baseUri; UserAuthorizer userAuthorizer; AuthorizationResponse authorizationResponse = null; try (SimpleCallbackServer simpleCallbackServer = new SimpleCallbackServer()) { userAuthorizer = UserAuthorizer.newBuilder() .setClientId(ClientId.of(clientId, clientSecret)) .setScopes(scopes) // Provides an empty callback URI so that no additional suffix is added to the // redirect. By default, UserAuthorizer will use "/oauth2callback" if this is either // not set or set to null. .setCallbackUri(URI.create("")) .build(); baseUri = URI.create(OAUTH2_CALLBACK_BASE_URI + ":" + simpleCallbackServer.getLocalPort()); System.out.printf( "Paste this url in your browser:%n%s%n", userAuthorizer.getAuthorizationUrl(loginEmailAddressHint, state, baseUri)); // Waits for the authorization code. simpleCallbackServer.accept(); authorizationResponse = simpleCallbackServer.authorizationResponse; } if (authorizationResponse == null || authorizationResponse.code == null) { throw new NullPointerException( "OAuth2 callback did not contain an authorization code: " + authorizationResponse); } // Confirms that the state in the response matches the state token used to generate the // authorization URL. if (!state.equals(authorizationResponse.state)) { throw new IllegalStateException("State does not match expected state"); } // Exchanges the authorization code for credentials and print the refresh token. UserCredentials userCredentials = userAuthorizer.getCredentialsFromCode(authorizationResponse.code, baseUri); System.out.printf("Your refresh token is: %s%n", userCredentials.getRefreshToken()); // Prints the configuration file contents. Properties adsProperties = new Properties(); adsProperties.put(ConfigPropertyKey.CLIENT_ID.getPropertyKey(), clientId); adsProperties.put(ConfigPropertyKey.CLIENT_SECRET.getPropertyKey(), clientSecret); adsProperties.put( ConfigPropertyKey.REFRESH_TOKEN.getPropertyKey(), userCredentials.getRefreshToken()); adsProperties.put( ConfigPropertyKey.DEVELOPER_TOKEN.getPropertyKey(), "INSERT_DEVELOPER_TOKEN_HERE"); showConfigurationFile(adsProperties); } private void showConfigurationFile(Properties adsProperties) throws IOException { System.out.printf( "Copy the text below into a file named %s in your home directory, and replace " + "INSERT_XXX_HERE with your configuration:%n", GoogleAdsClient.Builder.DEFAULT_PROPERTIES_CONFIG_FILE_NAME); System.out.println( "######################## Configuration file start ########################"); adsProperties.store(System.out, null); System.out.printf( "# Required for manager accounts only: Specify the login customer ID used to%n" + "# authenticate API calls. This will be the customer ID of the authenticated%n" + "# manager account. You can also specify this later in code if your application%n" + "# uses multiple manager account + OAuth pairs.%n" + "#%n"); System.out.println( "# " + ConfigPropertyKey.LOGIN_CUSTOMER_ID.getPropertyKey() + "=INSERT_LOGIN_CUSTOMER_ID"); System.out.println( "######################## Configuration file end ##########################"); } /** Basic server that listens for the OAuth2 callback. */ private static class SimpleCallbackServer extends ServerSocket { private AuthorizationResponse authorizationResponse; SimpleCallbackServer() throws IOException { // Passes a port # of zero so that a port will be automatically allocated. super(0); } /** * Blocks until a connection is made to this server. After this method completes, the * authorizationResponse of this server will be set, provided the request line is in the * expected format. */ @Override public Socket accept() throws IOException { Socket socket = super.accept(); try (BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) { String callbackRequest = in.readLine(); // Uses a regular expression to extract the request line from the first line of the // callback request, e.g.: // GET /?code=AUTH_CODE&state=XYZ&scope=https://www.googleapis.com/auth/adwords HTTP/1.1 Pattern pattern = Pattern.compile("GET +([^ ]+)"); Matcher matcher = pattern.matcher(Strings.nullToEmpty(callbackRequest)); if (matcher.find()) { String relativeUrl = matcher.group(1); authorizationResponse = new AuthorizationResponse(OAUTH2_CALLBACK_BASE_URI + relativeUrl); } try (Writer outputWriter = new OutputStreamWriter(socket.getOutputStream())) { outputWriter.append("HTTP/1.1 "); outputWriter.append(Integer.toString(HttpStatusCodes.STATUS_CODE_OK)); outputWriter.append(" OK\n"); outputWriter.append("Content-Type: text/html\n\n"); outputWriter.append("<b>"); if (authorizationResponse.code != null) { outputWriter.append("Authorization code was successfully retrieved."); } else { outputWriter.append("Failed to retrieve authorization code."); } outputWriter.append("</b>"); outputWriter.append("<p>Please check the console output from <code>"); outputWriter.append(GenerateUserCredentials.class.getSimpleName()); outputWriter.append("</code> for further instructions."); } } return socket; } } /** Response object with attributes corresponding to OAuth2 callback parameters. */ static class AuthorizationResponse extends GenericUrl { /** The authorization code to exchange for an access token and (optionally) a refresh token. */ @Key String code; /** Error from the request or from the processing of the request. */ @Key String error; /** State parameter from the callback request. */ @Key String state; /** * Constructs a new instance based on an absolute URL. All fields annotated with the {@link Key} * annotation will be set if they are present in the URL. * * @param encodedUrl absolute URL with query parameters. */ public AuthorizationResponse(String encodedUrl) { super(encodedUrl); } @Override public String toString() { return MoreObjects.toStringHelper(getClass()) .add("code", code) .add("error", error) .add("state", state) .toString(); } } }
C#
// Copyright 2019 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. using Google.Apis.Auth.OAuth2; using Google.Apis.Auth.OAuth2.Flows; using Google.Apis.Util.Store; using System; using System.Threading; using System.Threading.Tasks; namespace Google.Ads.GoogleAds.Examples { /// <summary> /// Entry point for the application. /// </summary> internal class Program { /// <summary> /// The Google Ads API scope. /// </summary> private const string GOOGLE_ADS_API_SCOPE = "https://www.googleapis.com/auth/adwords"; /// <summary> /// The main entry point for the application. /// </summary> public static void Main(string[] args) { Console.WriteLine("This code example creates an OAuth2 refresh token for the " + "Google Ads API .NET Client library. This example works with both web and " + "desktop app OAuth client ID types. To use this application\n" + "1) Follow the instructions on " + "https://developers.google.com/google-ads/api/docs/oauth/cloud-project " + "to generate a new client ID and secret.\n" + "2) Run this application.\n" + "3) Enter the client ID and client secret when prompted and follow the instructions.\n" + "4) Once the output is generated, copy its contents into your App.config " + "file. See https://developers.google.com/google-ads/api/docs/client-libs/dotnet/configuration " + "for other configuration options.\n\n"); Console.WriteLine("IMPORTANT: For web app clients types, you must add " + "'http://127.0.0.1/authorize' to the 'Authorized redirect URIs' list in your " + "Google Cloud Console project before running this example to avoid getting a " + "redirect_uri_mismatch error. Desktop app client types do not require the " + "local redirect to be explicitly configured in the console.\n\n"); // Accept the client ID from user. Console.Write("Enter the client ID: "); string clientId = Console.ReadLine(); // Accept the client ID from user. Console.Write("Enter the client secret: "); string clientSecret = Console.ReadLine(); // Load the JSON secrets. ClientSecrets secrets = new ClientSecrets() { ClientId = clientId, ClientSecret = clientSecret }; try { var initializer = new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = secrets, Prompt = "consent", }; // Authorize the user using desktop flow. GoogleWebAuthorizationBroker creates a // web server that listens to a random port at 127.0.0.1 and the /authorize url // as loopback url. See https://github.com/googleapis/google-api-dotnet-client/blob/main/Src/Support/Google.Apis.Auth/OAuth2/LocalServerCodeReceiver.cs // for details. Task<UserCredential> task = GoogleWebAuthorizationBroker.AuthorizeAsync( initializer, new string[] { GOOGLE_ADS_API_SCOPE }, string.Empty, CancellationToken.None, new NullDataStore() ); UserCredential credential = task.Result; Console.WriteLine("\nCopy the following content into your App.config file.\n\n" + $"<add key = 'OAuth2Mode' value = 'APPLICATION' />\n" + $"<add key = 'OAuth2ClientId' value = '{clientId}' />\n" + $"<add key = 'OAuth2ClientSecret' value = '{clientSecret}' />\n" + $"<add key = 'OAuth2RefreshToken' value = " + $"'{credential.Token.RefreshToken}' />\n"); Console.WriteLine($"Make sure you also set the values of LOGIN_CUSTOMER_ID header." + " See https://developers.google.com/google-ads/api/docs/client-libs/dotnet/configuration#google_ads_api_settings" + " to learn more.\n" + "<add key = 'LoginCustomerId' value = 'INSERT_LOGIN_CUSTOMER_ID_HERE' />"); Console.WriteLine("See https://developers.google.com/google-ads/api/docs/client-libs/dotnet/configuration " + "for alternate configuration options."); Console.WriteLine("Press <Enter> to continue..."); Console.ReadLine(); } catch (AggregateException) { Console.WriteLine("An error occured while authorizing the user."); } } } }
PHP
<?php /* * Copyright 2018 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. */ namespace Google\AdsApi\Examples\Authentication; require __DIR__ . '/../../vendor/autoload.php'; use Google\Auth\CredentialsLoader; use Google\Auth\OAuth2; use Psr\Http\Message\ServerRequestInterface; use React\EventLoop\Loop; use React\Http\HttpServer; use React\Http\Message\Response; use React\Socket\SocketServer; use UnexpectedValueException; /** * This example will create an OAuth2 refresh token for the Google Ads API. This example works with * both web and desktop app OAuth client ID types. * * We highly recommend running this example locally, since you won't need to generate refresh tokens * very often and you can avoid issue of port settings that may occur when using a Docker container. * * 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. Desktop app * client types do not require the local redirect to be explicitly configured in the console. * * <p>This example will start a basic server that listens for requests at `http://127.0.0.1:PORT`, * where `PORT` is dynamically assigned. */ class GenerateUserCredentials { /** * @var string the OAuth2 scope for the Google Ads API * @see https://developers.google.com/google-ads/api/docs/oauth/internals#scope */ private const SCOPE = 'https://www.googleapis.com/auth/adwords'; /** * @var string the Google OAuth2 authorization URI for OAuth2 requests * @see https://developers.google.com/identity/protocols/OAuth2InstalledApp#step-2-send-a-request-to-googles-oauth-20-server */ private const AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/v2/auth'; /** * @var string the OAuth2 call back IP address. */ private const OAUTH2_CALLBACK_IP_ADDRESS = '127.0.0.1'; public static function main() { if (!class_exists(HttpServer::class)) { echo 'Please install "react/http" package to be able to run this example'; exit(1); } // 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'); // To fill in the values below, generate a client ID and client secret from the Google Cloud // Console (https://console.cloud.google.com) by creating credentials for either a web or // desktop app OAuth client ID. // If using a web application, add the following to its "Authorized redirect URIs": // http://127.0.0.1 print 'Enter your OAuth2 client ID here: '; $clientId = trim(fgets(STDIN)); print 'Enter your OAuth2 client secret here: '; $clientSecret = trim(fgets(STDIN)); $redirectUrl = str_replace('tcp:', 'http:', $socket->getAddress()); $oauth2 = new OAuth2( [ 'clientId' => $clientId, 'clientSecret' => $clientSecret, 'authorizationUri' => self::AUTHORIZATION_URI, 'redirectUri' => $redirectUrl, 'tokenCredentialUri' => CredentialsLoader::TOKEN_CREDENTIAL_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) { // 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 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; $propertiesToCopy = '[GOOGLE_ADS]' . PHP_EOL; $propertiesToCopy .= 'developerToken = "INSERT_DEVELOPER_TOKEN_HERE"' . PHP_EOL; $propertiesToCopy .= <<<EOD ; Required for manager accounts only: Specify the login customer ID used to authenticate API calls. ; This will be the customer ID of the authenticated manager account. You can also specify this later ; in code if your application uses multiple manager account + OAuth pairs. ; loginCustomerId = "INSERT_LOGIN_CUSTOMER_ID_HERE" EOD; $propertiesToCopy .= PHP_EOL . '[OAUTH2]' . PHP_EOL; $propertiesToCopy .= "clientId = \"{$oauth2->getClientId()}\"" . PHP_EOL; $propertiesToCopy .= "clientSecret = \"{$oauth2->getClientSecret()}\"" . PHP_EOL; $propertiesToCopy .= "refreshToken = \"$refreshToken\"" . PHP_EOL; print 'Copy the text below into a file named "google_ads_php.ini" in your home ' . 'directory, and replace "INSERT_DEVELOPER_TOKEN_HERE" with your developer ' . 'token:' . PHP_EOL; print PHP_EOL . $propertiesToCopy; 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, $oauth2->buildFullAuthorizationUri(['access_type' => 'offline']) ); } } GenerateUserCredentials::main();
Python
#!/usr/bin/env python # Copyright 2018 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. """This example will create an OAuth2 refresh token for the Google Ads API. This example works with both web and desktop app 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. Desktop app client types do not require the local redirect to be explicitly configured in the console. Once complete, download the credentials and save the file path so it can be passed into this example. This example is a very simple implementation, for a more detailed example see: https://developers.google.com/identity/protocols/oauth2/web-server#python """ import argparse import hashlib import os import re import socket import sys from urllib.parse import unquote # If using Web flow, the redirect URL must match exactly what’s configured in GCP for # the OAuth client. If using Desktop flow, the redirect must be a localhost URL and # is not explicitly set in GCP. from google_auth_oauthlib.flow import Flow _SCOPE = "https://www.googleapis.com/auth/adwords" _SERVER = "127.0.0.1" _PORT = 8080 _REDIRECT_URI = f"http://{_SERVER}:{_PORT}" def main(client_secrets_path, scopes): """The main method, starts a basic server and initializes an auth request. Args: client_secrets_path: a path to where the client secrets JSON file is located on the machine running this example. scopes: a list of API scopes to include in the auth request, see: https://developers.google.com/identity/protocols/oauth2/scopes """ 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", ) # 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 = unquote(get_authorization_code(passthrough_val)) # Pass 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") print( "Add your refresh token to your client library configuration as " "described here: " "https://developers.google.com/google-ads/api/docs/client-libs/python/configuration" ) 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. """ # Open 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() data = connection.recv(1024) # Parse 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. """ # Decode the request into a utf-8 encoded string decoded = data.decode("utf-8") # Use a regular expression to extract the URL query parameters string match = re.search(r"GET\s\/\?(.*) ", decoded) params = match.group(1) # Split the parameters to isolate the key/value pairs pairs = [pair.split("=") for pair in params.split("&")] # Convert pairs to a dict to make it easy to access the values return {key: val for key, val in pairs} if __name__ == "__main__": parser = argparse.ArgumentParser( description=( "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." ), ) # The following argument(s) should be provided to run the example. parser.add_argument( "-c", "--client_secrets_path", required=True, type=str, help=( "Path to the client secrets JSON file from the Google Developers " "Console that contains your client ID, client secret, and " "redirect URIs." ), ) parser.add_argument( "--additional_scopes", default=None, type=str, nargs="+", help="Additional scopes to apply when generating the refresh token.", ) args = parser.parse_args() configured_scopes = [_SCOPE] if args.additional_scopes: configured_scopes.extend(args.additional_scopes) main(args.client_secrets_path, configured_scopes)
Ruby
#!/usr/bin/env ruby # Encoding: utf-8 # # Copyright 2018 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. # # This example will create an OAuth2 refresh token for the Google Ads API using # the Web application flow. # # This example will start a basic server that listens for requests at # http://localhost:PORT, where PORT is the port specified below. require 'googleauth' require 'securerandom' require 'uri' require 'cgi' require 'socket' require 'json' require 'optparse' def generate_user_credential(path) json = JSON.load_file(path) creds, callback_uri, port = if !json["installed"].nil? [json["installed"], "http://#{SERVER}:#{PORT}", PORT] elsif !json["web"].nil? web_creds = json["web"] # If you have more than one redirect URI, you may need to add some # code here to ensure that you choose the correct one. uri = json["web"]["redirect_uris"].first port = uri.split(":").last [web_creds, uri, port] else raise "No installed or web credentials found." end # Create an anti-forgery state token as described here: # https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken state = SecureRandom.hex(16) client_id = Google::Auth::ClientId.new( creds["client_id"], creds["client_secret"], ) # This example does not store credentials, so no TokenStore is needed. user_authorizer = Google::Auth::UserAuthorizer.new( client_id, SCOPE, nil, callback_uri) authorization_url = user_authorizer.get_authorization_url(state: state) printf("Paste this url in your browser:\n%s\n", authorization_url) printf("Waiting for authorization and callback...\n") printf("Listening at %s\n", callback_uri) response_params = get_authorization_code(port) # Confirm that the state in the response matches the state token used to # generate the authorization URL. unless state == response_params['state'][0] raise StandardError, 'State returned from callback does not match the expected state' end user_credentials = user_authorizer.get_credentials_from_code( code: response_params['code'][0]) printf("Your refresh token is: %s\n", user_credentials.refresh_token) printf("Copy your refresh token above into your google_ads_config.rb in your "\ "home directory or use it when instantiating the library.\n") end def get_authorization_code(port) authorization_code = nil server = TCPServer.open(port) client = server.accept callback_request = client.readline # Use a regular expression to extract the request line from the first line of # the callback request, e.g.: # GET /?code=AUTH_CODE&state=XYZ&scope=... HTTP/1.1 matcher = /GET +([^ ]+)/.match(callback_request) response_params = CGI.parse(URI.parse(matcher[1]).query) unless matcher.nil? client.puts("HTTP/1.1 200 OK") client.puts("Content-Type: text/html") client.puts("") client.puts("<b>") if response_params['code'].nil? client.puts("Failed to retrieve authorization code.") else client.puts("Authorization code was successfully retrieved.") end client.puts("</b>") client.puts("<p>Please check the console output.</p>") client.close return response_params end if __FILE__ == $PROGRAM_NAME SCOPE = 'https://www.googleapis.com/auth/adwords' SERVER = '127.0.0.1' PORT = 8080 # To fill in the values below, generate a client secret JSON file from the # Google Cloud Console (https://console.cloud.google.com) by creating # credentials for a web or desktop application. If using a web application, # add the following to its "Authorized redirect URIs": # http://127.0.0.1:[PORT] options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. options[:path] = 'INSERT_CLIENT_SECRETS_PATH_HERE' OptionParser.new do |opts| opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' opts.on('-P', '--client-secrets-path CLIENT-SECRETS-PATH', String, 'Client Secrets Path') do |v| options[:path] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! generate_user_credential( options[:path], ) end
Perl
#!/usr/bin/perl -w # # Copyright 2019, 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 an OAuth2 refresh token for the Google Ads API. # This works with both web and desktop app OAuth client ID types. # # This example will start a basic server that listens for requests at # http://127.0.0.1:PORT, where PORT defaults to 8080 as below. # # # [IMPORTANT]: For web app client types, you must add http://127.0.0.1 to the # "Authorize redirect URIs" list in your Google Cloud Console project before # running this example. Desktop app client types do not require the local # redirect to be explicitly configured in the console. use strict; use warnings; use utf8; use FindBin qw($Bin); use lib "$Bin/../../lib"; use Google::Ads::GoogleAds::Client; use Google::Ads::GoogleAds::Utils::GoogleAdsHelper; use Getopt::Long qw(:config auto_help); use Pod::Usage; use Cwd qw(abs_path); use Data::Uniqid qw(uniqid); use Digest::SHA qw(sha1_hex); use constant OAUTH2_CALLBACK_BASE_URI => "http://127.0.0.1"; use constant PORT => 8080; # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. my $client_id = "INSERT_CLIENT_ID_HERE"; my $client_secret = "INSERT_CLIENT_SECRET_HERE"; my $additional_scopes = "INSERT_ADDITIONAL_SCOPES_HERE"; # Create an anti-forgery state token as described here: # https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken our $state = sha1_hex(uniqid); sub generate_user_credentials { my ($api_client, $client_id, $client_secret, $additional_scopes) = @_; my $auth_handler = $api_client->get_oauth2_handler(); my $callback_url = sprintf("%s:%d", OAUTH2_CALLBACK_BASE_URI, PORT); $auth_handler->set_client_id($client_id); $auth_handler->set_client_secret($client_secret); $auth_handler->set_redirect_uri($callback_url); $auth_handler->set_additional_scopes($additional_scopes) if check_params($additional_scopes); # Open a browser and point it to the authorization URL, authorize the access. print "\nPaste this url in your browser:\n", $auth_handler->get_authorization_url($state), "\n\n"; printf "Waiting for authorization and callback to %s ...\n", $callback_url; SimpleCallbackServer->new(PORT, $auth_handler)->run; return 1; } { # Simple CGI server that listens for the OAuth2 callback. package SimpleCallbackServer; use HTTP::Server::Simple::CGI; use base qw(HTTP::Server::Simple::CGI); sub new { my ($class, $port, $auth_handler) = @_; my $self = HTTP::Server::Simple::CGI->new($port); $self->{auth_handler} = $auth_handler; bless $self, $class; return $self; } my %dispatch = ('/' => \&resp_callback); sub handle_request { my ($self, $cgi) = @_; my $path = $cgi->path_info; my $handler = $dispatch{$path}; if (ref($handler) eq "CODE") { print "HTTP/1.0 200 OK\r\n"; $handler->($self, $cgi); } else { print "HTTP/1.0 404 Not found\r\n"; print $cgi->header, $cgi->start_html('Not found'), $cgi->h1('Not found'), $cgi->end_html; } } # The method to handle the callback request after user logs in and accepts # the OAuth2 prompt. An authorization code will be returned which can be # exchanged for an access token and refresh token. sub resp_callback { my ($self, $cgi) = @_; return if !ref $cgi; # Get the authorization code and state parameters from the URL. my $code = $cgi->param('code'); my $state = $cgi->param('state'); if (!$code) { print "\r\n<b>Failed to retrieve the authorization code.<b>"; } elsif ($state ne $main::state) { # Confirm that the state in the response matches the state token used to # generate the authorization URL. print "\r\n<b>State in the callback does not match the expected value.<b>"; } else { my $auth_handler = $self->{auth_handler}; $auth_handler->issue_access_token($code); # After the access token and refresh token are generated, you should store the # refresh token and reuse it for future calls, by either changing your # googleads.properties file or setting in the authorization handler as follows: # # $api_client->get_oauth2_handler()->set_client_id($client_id); # $api_client->get_oauth2_handler()->set_client_secret($client_secret); # $api_client->get_oauth2_handler()->set_refresh_token($refresh_token); print "\r\n<b>Authorization code was successfully retrieved.</b>"; print "<p><b>Replace the following keys and values in your " . "googleads.properties configuration file:</b><p>"; printf( "clientId=%s<br>clientSecret=%s<br>refreshToken=%s<br>", $auth_handler->get_client_id, $auth_handler->get_client_secret, $auth_handler->get_refresh_token ); print STDERR "Press Ctrl+C to quit.\n"; } } } # Don't run the example if the file is being included. if (abs_path($0) ne abs_path(__FILE__)) { return 1; } # Get Google Ads Client with the default API version. my $api_client = Google::Ads::GoogleAds::Client->new(); # Parameters passed on the command line will override any parameters set in code. GetOptions( 'client_id=s' => \$client_id, 'client_secret=s' => \$client_secret, 'additional_scopes=s' => \$additional_scopes ); # Print the help message if the parameters are not initialized in the code nor # in the command line. pod2usage(2) if not check_params($client_id, $client_secret); # Call the example. generate_user_credentials($api_client, $client_id, $client_secret, $additional_scopes); =pod =head1 NAME generate_user_credentials =head1 DESCRIPTION This example will create an OAuth2 refresh token for the Google Ads API for either a web or desktop app OAuth client ID. For web app client types, you must add B<http://127.0.0.1> to the "Authorize redirect URIs" list in your L<Google Cloud Console project|https://console.developers.google.com/apis/credentials> before running this example, where PORT defaults to 8080. Desktop app client types do not require the local redirect to be explicitly configured in the Cloud console. =head1 SYNOPSIS generate_user_credentials.pl [options] -help Show the help message. -client_id The OAuth2 client id. -client_secret The OAuth2 client secret -additional_scopes [optional] Additional OAuth2 scopes seperated by comma. =cut