Reputation: 116938
I have a curl script GoogleAuthenticationCurl.sh which i have been using for around ten years to request information from Googles different Google APIs.
This script users installed application credentials to build the consent screen for Googles oauth server. I copy the link and it shows the consent screen.
# Authorization link. Place this in a browser and copy the code that is returned after you accept the scopes.
https://accounts.google.com/o/oauth2/auth?client_id=[Application Client Id]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=[Scopes]&response_type=code
Google recently made a change which deprecated the redirect uri of urn:ietf:wg:oauth:2.0:oob
. (#instructions-oob)
If I use the link i used to use i get the following
Google wants us to use redirect_uri=http://127.0.0.1:port or http://[::1]:port">http://[::1]:port
instead of urn:ietf:wg:oauth:2.0:oob
.
So I changed my link to the following and placed it in a web browser
https://accounts.google.com/o/oauth2/auth?client_id=[ClientId]&redirect_uri=http://127.0.0.1b&scope=profile&response_type=code
All went well in the beginning I was able to see the consent screen again and consent to authorization. But instead of getting a authorization code returned I got
This being due to the fact that I am not running a webpage I am just trying to authorize a curl script.
Is there anyway to get my curl script to respond to this request or have google completely removed the ability to authorize a curl Script now?
Upvotes: 5
Views: 4316
Reputation: 21
So, this can be solved by using redirect uri loopback. More on why here
Explanation below the script.
#!/bin/sh
# Tutorial https://www.daimto.com/how-to-get-a-google-access-token-with-curl/
# YouTube video https://youtu.be/hBC_tVJIx5w
# Client id from Google Developer console
# Client Secret from Google Developer console
# Scope this is a space seprated list of the scopes of access you are requesting.
CLIENT_ID=
CLIENT_SECRET=
SCOPE="https://www.googleapis.com/auth/drive"
REDIRECT_URI="http%3A//localhost"
TOKEN_URL="https://accounts.google.com/o/oauth2/token"
AUTHORIZATION_CODE_REGEX='[0-9]/[0-9A-Za-z_-]+'
TMPFILE="$(mktemp)"
[ -z "${CLIENT_ID:+${CLIENT_SECRET}}" ] && printf "Client ID or SECRET not set.\n" && return 1
server_string='Now go back to command line..'
server_port='8079'
# run a loop until a closed port has been found
# check for 50 ports
while :; do
: "$((server_port += 1))"
if [ "${server_port}" -gt 8130 ]; then
"${QUIET:-_print_center}" "normal" "Error: No open ports found ( 8080 to 8130 )." "-"
return 1
fi
{ curl -Is "http://localhost:${server_port}" && continue; } || break
done
# start the server to be used as loopback ip address
# try to start the server using netcat or python3
# https://docs.python.org/3/library/http.server.html
if command -v python 1> /dev/null && python -V | grep -q 'Python 3'; then
python << EOF 1> "${TMPFILE}" 2>&1 &
from http.server import BaseHTTPRequestHandler, HTTPServer
class handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
if '/?code' in self.path:
message = '${server_string}'
self.wfile.write(bytes(message, "utf8"))
with HTTPServer(('', ${server_port}), handler) as server:
server.serve_forever()
EOF
server_pid="${!}"
elif command -v nc 1> /dev/null; then
# https://stackoverflow.com/a/58436505
printf "%b" "HTTP/1.1 200 OK\nContent-Length: $(printf "%s" "${server_string}" | wc -c)\n\n${server_string}" | nc -l -p "${server_port}" 1> "${TMPFILE}" 2>&1 &
server_pid="${!}"
else
printf "Error: neither netcat (nc) nor python3 is installed. It is required to required a http server which is used in fetching authorization code. Install and proceed.\n"
return 1
fi
# https://developers.google.com/identity/protocols/oauth2/native-app#obtainingaccesstokens
code_challenge="$(date '+%s')_authorization_code"
printf "Visit the below URL, follow the instructions and then come back to commandline\n"
URL="https://accounts.google.com/o/oauth2/auth?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}%3A${server_port}&scope=${SCOPE}&response_type=code&code_challenge_method=plain&code_challenge=${code_challenge}"
printf "\n%s\n\n" "${URL}"
printf "Press enter if you have completed the process in browser\n"
read -r _
# kill the server pid
kill "${server_pid}"
if ! authorization_code="$(grep -m1 'GET.*code.*HTTP/1.1' < "${TMPFILE}" | sed -e 's/.*GET.*code=//' -e 's/\&.*//')" &&
printf "%s\n" "${authorization_code}" | grep -q "${AUTHORIZATION_CODE_REGEX}"; then
printf "Code was not fetched properly , here is some info that maybe helpful.. "
printf "%s\n" "Code that was grabbed: ${authorization_code}"
printf "Output of http server:\n"
cat "${TMPFILE}"
(rm -f "${TMPFILE}" &)
return 1
fi
(rm -f "${TMPFILE}" &)
# Print the refresh token json to stdout
# https://developers.google.com/identity/protocols/oauth2/native-app#handlingresponse
curl --compressed -X POST \
--data "code=${authorization_code}&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&redirect_uri=${REDIRECT_URI}%3A${server_port}&grant_type=authorization_code&code_verifier=${code_challenge}" \
"${TOKEN_URL}"
This method additionally needs code_challenge
and code_challenge_method
to be passed with the url parameters. Also redirect uri needs to be same as provided in credentials.json
( which is usually http://localhost
.
code_challenge
can be generated in two ways, i will use the easy way. Easy way basically means a random string.
code_challenge_method
can be plain
( easy way ) and S256
.
Then we need to start a local http server with a available port. To start the server in the script, i have used python or netcat, whatever available.
redirect_uri
will be http://localhost:8080
Then visit the url in browser and follow steps, allow and done.
Now to get refresh token
using this authorization code, code_verifier
parameter is added to the token fetch request. code_verifier
is same as code_challenge
.
I recently implemented this in my akianonymus/gdrive-downloader. See https://github.com/Akianonymus/gdrive-downloader/blob/master/src/common/auth-utils.sh#L339 for reference.
Upvotes: 2
Reputation: 15070
Google has deprecated the OOB flow, and so the redirect URL urn:ietf:wg:oauth:2.0:oob
is removed since Feb 28, 2022. It is an unsafe feature for clients that cannot listen on an HTTP port.
You need to migrate to another flow. This does not necessarily mean you cannot use curl. But somehow, you need to be able to receive the redirect call with the necessary code.
http://127.0.0.1
, notice the removed b
at the end.http://127.0.0.1/?code=COPY_THIS_CODE
.Postman could be interesting.
Upvotes: 6