Here's a sample you can use for authorization:
Java
// 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
//
// 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.
package
shopping.merchant.samples.utils
;
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.GoogleCredentials
;
import
com.google.auth.oauth2.UserAuthorizer
;
import
com.google.auth.oauth2.UserCredentials
;
import
com.google.common.base.MoreObjects
;
import
com.google.common.base.Strings
;
import
com.google.common.collect.ImmutableList
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
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.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* Class that contains all the authentication logic, both for service accounts and to create an
* OAuth 2 refresh token for the Merchant API.
*
* <p>IMPORTANT FOR OAUTH: 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
Authenticator
{
// OAUTH2_CALLBACK_BASE_URI set to localhost by default
private
static
final
String
OAUTH2_CALLBACK_BASE_URI
=
"http://127.0.0.1"
;
// Scopes for the generated OAuth2 credentials. The list here only contains the Merchant 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/content"
).
build
();
public
GoogleCredentials
authenticate
()
throws
IOException
{
Config
config
=
Config
.
load
();
if
(
config
.
getPath
()
==
null
)
{
throw
new
IllegalArgumentException
(
"Must update Config.java to set a configuration directory."
);
}
File
serviceAccountFile
=
new
File
(
config
.
getPath
(),
"service-account.json"
);
System
.
out
.
printf
(
"Checking for service account file at: %s%n"
,
serviceAccountFile
);
if
(
serviceAccountFile
.
exists
())
{
System
.
out
.
println
(
"Attempting to load service account credentials"
);
try
(
InputStream
inputStream
=
new
FileInputStream
(
serviceAccountFile
))
{
GoogleCredentials
credential
=
GoogleCredentials
.
fromStream
(
inputStream
);
System
.
out
.
println
(
"Successfully loaded service account credentials"
);
return
credential
;
}
}
System
.
out
.
println
(
"No service account file found."
);
// Non-service account OAuth flow below
// First see if a refresh token exists, and if so, use it
File
tokenFile
=
new
File
(
config
.
getPath
(),
"token.json"
);
System
.
out
.
printf
(
"Checking for user credentials file at: %s%n"
,
tokenFile
);
if
(
tokenFile
.
exists
())
{
System
.
out
.
println
(
"Loading OAuth2 refresh token."
);
UserCredentials
userCredentials
=
UserCredentials
.
fromStream
(
new
FileInputStream
(
tokenFile
));
System
.
out
.
println
(
"Successfully loaded OAuth2 refresh token"
);
return
userCredentials
;
}
// If the refresh token does not exist, attempt to use client
// credentials to get a refresh token
File
clientSecretsFile
=
new
File
(
config
.
getPath
(),
"client-secrets.json"
);
if
(
!
clientSecretsFile
.
exists
())
{
throw
new
IOException
(
"No authentication credentials found. Checked the paths "
+
serviceAccountFile
.
getCanonicalPath
()
+
" and "
+
clientSecretsFile
.
getCanonicalPath
()
+
". Please read the accompanying README."
);
}
System
.
out
.
println
(
"Loading OAuth2 client credentials."
);
try
(
InputStream
inputStream
=
new
FileInputStream
(
clientSecretsFile
))
{
ClientId
parsedClient
=
ClientId
.
fromStream
(
inputStream
);
String
clientId
=
parsedClient
.
getClientId
();
String
clientSecret
=
parsedClient
.
getClientSecret
();
// 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
(
""
,
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 new refresh token is: %s%n"
,
userCredentials
.
getRefreshToken
());
// Save the refresh token to be used for the future
userCredentials
.
save
(
new
File
(
config
.
getPath
(),
"token.json"
).
getPath
());
return
userCredentials
;
}
catch
(
IOException
e
)
{
throw
new
IOException
(
"Could not retrieve OAuth2 client credentials from the file "
+
clientSecretsFile
.
getCanonicalPath
());
}
}
/** 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
(
Authenticator
.
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
();
}
}
}
PHP
< ?php
/**
* Copyright 2025 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);
$path = 'web';
$redirectUrl = str_replace('tcp:', 'http:', $socket->getAddress());
if(is_null($json_data[$path])){
$path = 'installed';
}
$oauth2 = new OAuth2(
[
'clientId' => $json_data[$path]['client_id'],
'clientSecret' => $json_data[$path]['client_secret'],
'authorizationUri' => $json_data[$path]['auth_uri'],
'redirectUri' => $redirectUrl,
'tokenCredentialUri' => $json_data[$path]['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;
$path = 'web';
if(is_null($json_data[$path])){
$path = 'installed';
}
$token_file_credentials = [
'client_id' => $json_data[$path]['client_id'],
'client_secret' => $json_data[$path]['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
"
\n
Waiting 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
"
\n
Your 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.
match
=
re
.
search
(
r
"GET\s\/\?(.*) "
,
decoded
)
if
not
match
:
return
{}
params
=
match
.
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
}