// 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.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.Console; 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.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Creates an OAuth2 refresh token for the Google Ads API using the Web application * flow. * * <p>This example will start a basic server that listens for requests at {@code * http://localhost:PORT}, where {@code PORT} is dynamically assigned. * * <p>IMPORTANT: You must add {@code http://localhost/oauth2callback} to the "Authorize redirect * URIs" list in your Google Cloud Console project before running this example. */ public class AuthenticateInWebApplication { // Scopes for the generated OAuth2 credentials. The list here only contains the AdWords 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 = "/oauth2callback"; 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 a Web application. // Set the "Authorized redirect URIs" to: // http://localhost/oauth2callback String clientId; String clientSecret; String loginEmailAddressHint; Console console = System.console(); if (console == null) { // The console will be null when running this example in some IDEs. In this case, please // set the clientId and clientSecret in the lines below. 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 { console.printf( "NOTE: When prompting for the client secret below, echoing will be disabled%n"); console.printf(" since the client secret is sensitive information.%n"); console.printf("Enter your client ID:%n"); clientId = console.readLine(); console.printf("Enter your client secret:%n"); clientSecret = String.valueOf(console.readPassword()); console.printf("(Optional) Enter the login email address hint:%n"); loginEmailAddressHint = Strings.emptyToNull(console.readLine()); } new AuthenticateInWebApplication().runExample(clientId, clientSecret, loginEmailAddressHint); } public void runExample(String clientId, String clientSecret, String loginEmailAddressHint) 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) .setCallbackUri(URI.create(OAUTH2_CALLBACK)) .build(); baseUri = URI.create("http://localhost:" + 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 from the Web application flow. */ 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("http://localhost" + 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(AuthenticateInWebApplication.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(); } } }
// 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 System; namespace Google.Ads.GoogleAds.Examples { public partial class Login : System.Web.UI.Page { /// <summary> /// The login helper. /// </summary> private WebLoginHelper loginHelper; /// <summary> /// Initializes a new instance of the <see cref="Login"/> class. /// </summary> public Login() { loginHelper = new WebLoginHelper(this); } /// <summary> /// Handles the Load event of the Page control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void Page_Load(object sender, EventArgs e) { // Initialize login helper only in the page load, otherwise session information // won't be available. if (loginHelper.IsLoggedIn) { // Redirect to the main page. Response.Redirect("/Default.aspx"); } else if (loginHelper.IsCallbackFromOAuthServer()) { loginHelper.ExchangeAuthorizationCodeForCredentials(); // Redirect to the main page. Response.Redirect("/Default.aspx"); } else { // Redirect the user to the OAuth2 login page. loginHelper.RedirectUsertoOAuthServer(); } } } }
<?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\Factory; use React\Http\Response; use React\Http\Server; use UnexpectedValueException; /** * This example will create an OAuth2 refresh token for the Google Ads API using the Web application * flow. * * <p>This example will start a basic server that listens for requests at `http://localhost:PORT`, * where `PORT` is dynamically assigned. * * <p>IMPORTANT: You must add `http://localhost/oauth2callback` to the "Authorize redirect * URIs" list in your Google Cloud Console project before running this example. */ class AuthenticateInWebApplication { /** * @var string the OAuth2 scope for the Google Ads API * @see https://developers.google.com/google-ads/api/docs/oauth/internals#scope */ 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 */ const AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/v2/auth'; /** * @var string the OAuth2 call back URL path. */ const OAUTH2_CALLBACK_PATH = '/oauth2callback'; public static function main() { if (!class_exists(Server::class)) { echo 'Please install "react/http" package to be able to run this example'; exit(1); } $loop = Factory::create(); // Creates a socket for localhost with random port. $socket = new \React\Socket\Server(0, $loop); 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 . self::OAUTH2_CALLBACK_PATH, '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 Server( function (ServerRequestInterface $request) use ($oauth2, $loop, &$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. 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']) ); $loop->run(); } } AuthenticateInWebApplication::main();
#!/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 creates an OAuth 2.0 refresh token for the Google Ads API. This illustrates how to step through the OAuth 2.0 native / installed application flow. It is intended to be run from the command line and requires user input. """ import argparse from google_auth_oauthlib.flow import InstalledAppFlow SCOPE = u'https://www.googleapis.com/auth/adwords' def main(client_secrets_path, scopes): flow = InstalledAppFlow.from_client_secrets_file( client_secrets_path, scopes=scopes) flow.run_local_server() print('Access token: %s' % flow.credentials.token) print('Refresh token: %s' % flow.credentials.refresh_token) if __name__ == '__main__': parser = argparse.ArgumentParser( description='Generates OAuth 2.0 credentials with the specified ' 'client secrets file.') # The following argument(s) should be provided to run the example. parser.add_argument('--client_secrets_path', required=True, help=('Path to the client secrets JSON file from the ' 'Google Developers Console that contains your ' 'client ID and client secret.')) parser.add_argument('--additional_scopes', default=None, help=('Additional scopes to apply when generating the ' 'refresh token. Each scope should be separated ' 'by a comma.')) args = parser.parse_args() configured_scopes = [SCOPE] if args.additional_scopes: configured_scopes.extend(args.additional_scopes.replace(' ', '') .split(',')) main(args.client_secrets_path, configured_scopes)
#!/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 'optparse' def authenticate_in_web_application(client_id, client_secret, port) callback_uri = sprintf('http://localhost:%s', port) # 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(client_id, 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' # 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 a Web application. Set the "Authorized redirect URIs" to: # http://localhost:[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[:client_id] = 'INSERT_CLIENT_ID_HERE' options[:client_secret] = 'INSERT_CLIENT_SECRET_HERE' options[:port] = 'INSERT_PORT_HERE' OptionParser.new do |opts| opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' opts.on('-I', '--client-id CLIENT-ID', String, 'Client ID') do |v| options[:client_id] = v end opts.on('-S', '--client-secret CLIENT-SECRET', String, 'Client Secret') do |v| options[:client_secret] = v end opts.on('-p', '--port PORT', String, 'Port') do |v| options[:port] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! authenticate_in_web_application(options[:client_id], options[:client_secret], options[:port]) end