Reputation: 71
I would like to be able to programmatically generate an id token for iap using the user default credential on a dev environment (i.e. my own laptop with google cloud sdk installed and logged in).
When following the documentation, I managed to generate an authorization token using a service account file.
When using google.auth.default
on my personal computer, I can see the credentials of type google.oauth2.credentials.Credentials
have a refresh_token
. I wanted to use it to generate the token as it is done with curl in the documentation under
Authenticating from a desktop app -> Accessing the application
but I could not make it work. Does someone know if there is a way to authenticate this way ?
Upvotes: 7
Views: 5734
Reputation: 1174
As Matthew stated, project for Client ID used to obtain refresh token should match project for IAP Client ID. Gcloud uses Client ID and secret defined in path_to/google-cloud-sdk/lib/googlecloudsdk/api_lib/auth/util.py
for default credentials (DEFAULT_CREDENTIALS_DEFAULT_CLIENT_ID
and DEFAULT_CREDENTIALS_DEFAULT_CLIENT_SECRET
). Because of that, you can't use refresh token from google.auth.default()
without util.py
change, as an attempt to obtain ID token will fail with:
{
"error": "invalid_audience",
"error_description": "The audience client and the client need to be in the same project."
}
Your options are:
Sample code for both options:
import google.auth
import requests
import json
from webbrowser import open_new_tab
from time import sleep
# use gcloud app default credentials if gcloud's util.py is patched
def id_token_from_default_creds(audience):
cred, proj = google.auth.default()
# data necessary for ID token
client_id = cred.client_id
client_secret= cred.client_secret
refresh_token = str(cred.refresh_token)
return id_token_from_refresh_token(client_id, client_secret, refresh_token, audience)
def id_token_from_refresh_token(client_id, client_secret, refresh_token, audience):
oauth_token_base_URL = "https://www.googleapis.com/oauth2/v4/token"
payload = {"client_id": client_id, "client_secret": client_secret,
"refresh_token": refresh_token, "grant_type": "refresh_token",
"audience": audience}
res = requests.post(oauth_token_base_URL, data=payload)
return (str(json.loads(res.text)[u"id_token"]))
# obtain ID token for provided Client ID: get authorization code -> exchange for refresh token -> obtain and return ID token
def id_token_from_client_id(client_id, client_secret, audience):
auth_code = get_auth_code(client_id)
refresh_token = get_refresh_token_from_code(auth_code, client_id, client_secret)
return id_token_from_refresh_token(client_id, client_secret, refresh_token, audience)
def get_auth_code(client_id):
auth_url = "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&response_type=code&scope=openid%%20email&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob"%client_id
open_new_tab(auth_url)
sleep(1)
return raw_input("Authorization code: ")
def get_refresh_token_from_code(auth_code, client_id, client_secret):
oauth_token_base_URL = 'https://www.googleapis.com/oauth2/v4/token'
payload = {"code": auth_code, "client_id": client_id, "client_secret": client_secret,
"redirect_uri": "urn:ietf:wg:oauth:2.0:oob", "grant_type": "authorization_code"}
res = requests.post(oauth_token_base_URL, data=payload)
return (str(json.loads(res.text)[u"refresh_token"]))
print("ID token from client ID: %s" % id_token_from_client_id("<Other client ID>", "<Other client secret>", "<IAP Client ID>")) # other client ID should be from the same project as IAP Client ID
print("ID token from \"default\" credentials: %s" % id_token_from_default_creds("<IAP Client ID>"))
Upvotes: 9
Reputation: 1585
thanks for pointing this out! I'd love it if we had code samples for this, but as you discovered, at least for Python the code sample we have for service account auth doesn't work with user accounts.
I'm not familiar enough with our Python client libraries to tell you the whether they can help you with any of this, but the gist of what https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_desktop_app is guiding you through is:
Make a new client ID (in the same project as the IAP-secured app) for your client app, and get a refresh token using that client ID. You can't just use the refresh token from application default credentials, since that's going to have the wrong client ID and probably scopes. Instead, you'll need to add functionality like "gcloud auth login" to your application and persist the refresh token.
Once you have a refresh token, when your client app wants to access the IAP app: POST to https://www.googleapis.com/oauth2/v4/token with the client ID and secret for your app's OAuth client, the refresh token, and the IAP client ID. This returns an OpenID Connect token that will be valid to authenticate to IAP for one hour.
Is that at least enough to get started?
Upvotes: 3