Skip to content

Instantly share code, notes, and snippets.

@akkornel
Created July 13, 2021 04:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akkornel/657033d2fd33a5e32113bf7c82b00835 to your computer and use it in GitHub Desktop.
Save akkornel/657033d2fd33a5e32113bf7c82b00835 to your computer and use it in GitHub Desktop.
A Python 3.6+ script showing how to get an OpenID Connect Token from Google Auth, and comparing the one you get with your own OAuth2 client ID against the one you get with the Google Cloud SDK OAuth2 client ID
#!/usr/bin/env python3
# vim: ts=4 sw=4 et
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT
# This was written by A. Karl Kornel <akkornel@stanford.edu>
# It is © The Board of Trustees of the Leleand Stanford Junior University
# It is made available under the MIT License
# https://opensource.org/licenses/MIT
# This script demonstrates how to get an OpenID Connect ID Token for a user,
# using Google Auth and the OAuth2 authorization code flow.
# You can provide multiple sets of OAuth2 client credentials, and the resulting
# ID Tokens will be displayed for easy viewing.
# To use it, you need Python 3.6+, a venv and three packages from PyPI:
# jwcrypto, requests, and requests_oauthlib
# Here's an example of how to set everything up:
# mkdir id_token_demo; cd id_token_demo
# python3.6 -m venv .
# . bin/activate
# pip install --quiet --upgrade pip
# pip install --quiet jwcrypto requests requests_oauthlib
# python path/to/id_token_demo.py
# Repeat the final `python` command as much as you want.
# To clean up, run `deactivate` and delete the `id_token_demo` directory.
# Before running, your need to provide one configuration.
# You need to provide a Google OAuth client ID and secret.
# How to get that is out of the scope of this document.
# Once you get your Google OAuth 2.0 client ID and secret, put them into the
# appropriate slots in the `you` section. The `gcloud` section has the client
# ID and secret for the `gcloud` command.
oauth2_credentials = {
'gcloud': {
'client_id': '32555940559.apps.googleusercontent.com',
'client_secret': 'ZmssLNjJy2998hD4CTg2ejr2',
},
'you': {
'client_id': 'PUT CLIENT ID HERE',
'client_secret': 'PUT CLIENT SECRET HERE',
},
}
# That is all the configuration required!
# As things run, notice how similar the OpenID Connect ID tokens are.
# And yet, only the ID token generated by `gcloud` is acceptable for
# authenticating to Google Cloud Functions.
# Now, the code!
import json
import jwcrypto.jwk
import jwcrypto.jwt
import requests
import requests_oauthlib
import sys
# STEP 1: Get Keys and URLs
# This is the URL to the OpenID Connect Configuration for Google Auth
oauth_config_url = 'https://accounts.google.com/.well-known/openid-configuration'
# Get the OpenID Connect configuration
oidc_config = requests.get(oauth_config_url).json()
# Get the ID Token JWT signing keys
# This works because the jwks_url returns an RFC 7517 JWK set, which jwcrypto
# can import directly.
jwks_url = oidc_config['jwks_uri']
id_token_keys = jwcrypto.jwk.JWKSet.from_json(
requests.get(jwks_url).text
)
# STEP 2: Do the OAuth Flows
# This will hold the JWTs
jwt_strings = dict()
# For each set of credentials, ask the user to authenticate.
print(f"You will be asked to authenticate {len(oauth2_credentials)} times.")
for (oauth2_name, oauth2_credentials) in oauth2_credentials.items():
# Set up the OAuth2 client
oauth = requests_oauthlib.OAuth2Session(
client_id=oauth2_credentials['client_id'],
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
scope='openid https://www.googleapis.com/auth/userinfo.email',
)
(oauth_url, oauth_state) = oauth.authorization_url(
url=oidc_config['authorization_endpoint'],
)
print('')
print(f"Please go to {oauth_url} and log in.")
try:
oauth_code = input('Enter the authorization code: ')
except KeyboardInterrupt:
sys.exit(0)
# Fetch the ID token from Google
oauth_token = oauth.fetch_token(
token_url=oidc_config['token_endpoint'],
code=oauth_code,
client_secret=oauth2_credentials['client_secret'],
)
jwt_strings[oauth2_name] = oauth_token['id_token']
# STEP 3: Decode and display the ID Tokens
for (oauth2_name, jwt_string) in jwt_strings.items():
# Parse and verify the JWT, then extract the claims.
# We use the key set downloaded from the URL in the OpenID Connect config.
# The list of allowed signing algorithms is also from that config.
jwt = jwcrypto.jwt.JWT(
jwt=jwt_strings[oauth2_name],
key=id_token_keys,
algs=oidc_config['id_token_signing_alg_values_supported']
)
jwt_claims = json.loads(jwt.claims)
# The claims was JSON-encoded. Re-encode as pretty JSON, and display.
jwt_formatted = json.dumps(jwt_claims, indent=4)
print('')
print(f"ID Token from {oauth2_name} is:")
print(jwt_formatted)
$ python id_token_demo.py
You will be asked to authenticate 2 times.
Please go to https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=32555940559.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=cFf660mO74FMpORQ0SEIhN199xDeoM and log in.
Enter the authorization code: CODE_REDACTED
Please go to https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=ID_REDACTED&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=zj7g7XI0tjDw2qoDdMa6LAcLW4SxMm and log in.
Enter the authorization code: CODE_REDACTED
ID Token from gcloud is:
{
"iss": "https://accounts.google.com",
"azp": "32555940559.apps.googleusercontent.com",
"aud": "32555940559.apps.googleusercontent.com",
"sub": "ID_REDACTED",
"hd": "stanford.edu",
"email": "akkornel@stanford.edu",
"email_verified": true,
"at_hash": "RBA2a9vSICFHLzCt78gk_w",
"iat": 1626125182,
"exp": 1626128782
}
ID Token from you is:
{
"iss": "https://accounts.google.com",
"azp": "ID_REDACTED",
"aud": "ID_REDACTED",
"sub": "ID_REDACTED",
"hd": "stanford.edu",
"email": "akkornel@stanford.edu",
"email_verified": true,
"at_hash": "PFkMpgB6t4U4TPgnX8Sc_w",
"iat": 1626125191,
"exp": 1626128791
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment