Google Sign-In: "Certificate for key id xxxx not found" when using google-auth Python package - jwt

I'm maintaining a website and its mobile apps (iOS and Android). For Google Sign-In in mobile app, I'm using google-auth Python package on the server side.
Since about one month ago, I started to receive error reports related to Google Sign-In from the server side. The error message looks like the following:
Certificate for key id 728f4016652079b9ed99861bb09bafc5a45baa86 not found.
The server-side Google Sign-In authentication backend follows this document:
from google.oauth2 import id_token
from google.auth.transport import requests
# ...
try:
# The following line may raise ValueError with message:
# Certificate for key id xxxx not found.
id_info = id_token.verify_oauth2_token(google_id_token, requests.Request())
if id_info['aud'] not in VALID_CLIENT_IDS:
logger.error('Invalid aud from Google ID token: %s', id_info['aud'])
raise ValueError('Unverified audience.')
# ...
except ValueError as exc:
logger.error('Fail to verify Google ID token: %s', exc, extra={'request': request})
The error comes from the google.auth.jwt module, when verifying the Google-issued JWT against a list of Google public certificates.
Diving into the google-auth code, I can see that verify_oauth2_token() function is fetching Google public certificates from the URL https://www.googleapis.com/oauth2/v1/certs. It seems that sometimes, for some Google ID token sent from some Android phones, the key id cannot be found in that URL.
Here are some other details which might be useful:
It seems that iOS app does not have this kind of issue. From the USER_AGENT header, I can see that the error happens only in Android app (USER_AGENT=okhttp/3.11.0). And it happens only in some Android device, not all.
I was wondering if this happens only for Android phones from China (e.g. if they are connecting via a VPN). So I also checked the user IP address. But it turned out that some of those users were from Europe.
Some key id is recurring again and again in the server error logs. For example, the key id aa436c3f63b281ce0d976da0b51a34860ff960eb is seen dozens of times, from the beginning of November to now (end of Dec).
I am constantly seeing this error, several times (10 ~ 30 times) every day.
My website is running on the following environment:
OS: Linux (CentOS 7) 64-bit
Apache 2 with mod_wsgi 4.5.24
Python 3.6.7 and Django 2.1.2
google-auth version: tried both 1.3.0 and 1.6.1
As I could not reproduce this issue either with my iPhone, or with my Android phone (Huawei P20, bought in France), I am completely stuck.
But, one of my friends is having this issue now, and he bought his Android phone from Hongkong. This makes me think if it is possible that for some countries, Google Sign-In uses some different certs other than the public ones at https://www.googleapis.com/oauth2/v1/certs?
I don't think this is a bug in google-auth package. I'm wondering if any of you have ever heard of this error, and could give me a hint about the possible cause of it?
Thanks in advance!

OK, I finally figured it out. I'm posting my findings here, hoping that it could help someone else.
The server-side Python code has nothing wrong. The reason why it failed was that, the client app submitted an expired Google ID token.
Here's the fixed version of my LoginActivity:
...
#OnClick(R.id.google_sign_in_button)
void loginWithGoogle() {
//
// If user has already signed in to our app with Google, sign him out first.
//
// NOTE: This step is required, or the ID token might not pass the server-side validation!
//
// After sign-in, we need to get the user's ID token issued and signed by Google, and send
// it back to our server for validation.
//
// Google is rotating its OAuth2 certificate regularly, so an old ID token issued long time
// ago by Google might not pass the server-side validation -- if the certificate used to
// sign the ID token has expired.
//
// This may happen when user has already signed in to our app with Google. In such case,
// the ID token we get from the user's Google account is obsolete. Our server will fail to
// validate it, with the error message:
//
// Fail to verify Google ID token: Certificate for key id xxx not found.
//
// Google recommends using the `silentSignIn` method for the already-signed-in user
// (see step 2 of: https://developers.google.com/identity/sign-in/android/backend-auth).
// For the sake of simplicity, we don't do that. Instead, we go directly to step 3
// by signing user out, giving him the option to sign-in again.
//
final GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account != null) {
// User has already signed in: Sign out and sign in again.
// NOTE: THIS IS THE FIX TO MY PROBLEM.
mGoogleSignInClient.signOut().addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
startGoogleSignInActivity();
}
});
} else {
// User is not yet signed in: Start the Google sign-in flow.
startGoogleSignInActivity();
}
}
private void startGoogleSignInActivity() {
final Intent intent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(intent, REQUEST_LOGIN_WITH_GOOGLE);
}
The key point is that: I need to check if user has already signed in with Google. If yes, I need to sign the user out and re-start the Sign-in-with-Google activity.
As Android has native support for Google account, I suppose the OS may cache user's Google account if user is already authenticated (in some other app or system-wide). But that cached account may probably contain an expired ID token. Forcing to sign the user out and re-sign-in will give me a fresh new ID token.
This also explains why my iOS app does not have this issue. Because iOS never caches user's Google account.

Related

Facebook login architecture

I'm developing an App which uses Facebook to authenticate. So far, what I have is the following:
Client->FB: authentication data (fb user & password),
FB->Client: FB id, FB token,...
Client->AppServer: FB data (id, token,..)
AppServer->Client: OK/Not OK, whatever necessary data.
Where:
Client: The App installed in the user's device (in this case, iOS),
FB: FB server (the client talks to it through FB's SDK),
AppServer: My App's server.
The point of all these is to validate if the user is real. The problem is that in #3 the Client could be sending a random FB id or FB token to the AppServer, so the question is: is there anyway to check in the AppServer whether this FB data is indeed real?
This worked for me:
Given a FB_CLIENT_TOKEN and FB_CLIENT_ID coming from a client, and knowing your FB_APP_ID and FB_APP_SECRET, we want to validate in our server if the fb account from the client is real. Then:
From server, access https://graph.facebook.com/oauth/access_token?client_id=FB_APP_ID&client_secret=FB_APP_SECRET&grant_type=client_credentials
This will return a plaintext with access_token=APP_TOKEN.
Next, access https://graph.facebook.com/debug_token?input_token=FB_CLIENT_TOKEN&access_token=APP_TOKEN
This will return a JSON with a field named user_id, so the last step is to check if this field equals FB_CLIENT_ID.

AADSTS65005 - Consent validation failed

This might not be the right forum, but i have registered my application in the 'Application Registration Portal' : https://apps.dev.microsoft.com/#/appList
I have the client id/app id & the client secret, and previously this was working using the following account : admin#XXXXX.onmicrosoft.com, the 'Consent Request Pops-up' and i am able to give consent.
However, recently i am trying via another account, i.e. user3#XXXXX.onmicrosoft.com, this redirects to my registered URL without the 'Consent screen'.
Below is the error message returned,
error=access_denied&error_description=AADSTS65005%3a+Consent+validation+failed%3a%0d%0aTrace+ID%3a+be71ab23-b45c-47a9-8932-50a24ce86505%0d%0aCorrelation+ID%3a+a49121b9-5bc2-4bd3-934d-551eaa68261b%0d%0aTimestamp%3a+2015-10-26+08%3a28%3a02Z
Any hints would be great!

Account sign-in details are out of date. Sign in again. (Issue after upgrade to Chrome 33)

I have been trying to follow the sample gdrive in the Google Drive API samples. I started out with Chrome 32 and the login and authorization was working well. I upgraded to Chrome 33 and now the sample fails during authorization and takes me to settings page with this error message - "Account sign-in details are out of date. Sign in again". I am already signed in to the account, but it still thinks I am not signed in. Any ideas what the problem is?
Check chrome.runtime.lastError in the getAuthToken callback. There should be an error string describing what went wrong in a little more detail.
If you're not using the default OAuth client ID and manifest "key" parameter that came with the sample, I would double check that those match up with your registration in the cloud console.

How to set up a service account for Google Apps API

Inorder to manage google apps using API, iam working with admin-cmdline-sample client project, which i downloaded from admin sdk.
Now i am able to connect to the Google apps cloud and manage user features.
The problem that i am facing now is that in the process of authorization code flow to get the access token,
iam getting redirected to the login page if the emailId that i passed in the code is not already there in the credential store.
so is there any mechanism in which i can pass the password in the client code itself so that i wont be redirected to the login page.
I found some related post, which recommend to setup service account.
I tried that as mentioned in "https://developers.google.com/console/help/new/#serviceaccounts", but i am not able to find generate certificate and download private key links.
Currently iam using a free subscription plan,is there any alternative way to generate certificate and download private key for free subscribers or is it possible for paid subscribers only
I hope this can help you.
You can create a Service Account with a free subscription or a paid subscriber.
Log in with your Google Account.
Visit the Google developer console. (click here)
Create a new project.
When your project is created:
Click on the "APIs" tab.
Turn on the apis that you need.
After that, you will see something like this picture.
Click on the "Create new client ID" buttton and choose "service account".
After that, your private key (.p12 file) will be downloaded and you will have:
A Client ID
An Email Adress
A Public key fingerprint
You can generate new Keys and download a JSON file.
You can read more information about the Google developer console here

Unexpected Authorization Request in Workflow C2QB WF3.0

We have a multi-tenanted/multi-domain app and we're looking at publishing on IPP. Because of the multi-domain nature our configured endpoints in the App setup are generic and users are then redirected to their specific account once their identity is established.
The issue is with C2QB WF3.0.
The test steps are:
go here: https://appcenter.intuit.com/Home/MyApps/
on the "Launch My Apps" tab, click the app
Expected Result:
the Sign In screen for the app is displayed
The requirement is:
if not still signed into the app, sign in screen is displayed
if still signed into the app, take user into the app
The issue is that the even if the user is currently logged into our application, it still requests authorization as per this message:
"domain" is requesting some information from your Intuit account
By approving this request "domain" will be able to access your:
Name
Email Address
The workflow that it is actually following is to perform a callback to our connect url. The normal way to determine which account the callback is coming from is via the realmId, but this is not received from Intuit and therefore it needs to be requested. Once a user has completed this authorization, it will not ask again. It should be noted that we're not actually requesting a name or an email address as the screen suggests.
We've been informed by Intuit that it should not request authorization and it is not part of the workflow.
We're wondering if anyone else has encountered this problem and if there is a workaround for it.
We discussed with the Engineering teams and they confirmed what Pete has mentioned above. This is not a bug and please follow the steps as Pete has mentioned.
If you want to replicate the 'access your app with appcenter flow', you have be either logged in QBO online company file in same browser session or navigate to the app via blue dot menu from your application or run your application from localhost(.net) and then go to Appcenter and login there on same tab and then click on your app.
Since your desktop application is running, your code will be hit.
You will then get the second Auth screen only for authorizing your company file. After authorization your realm will be set in a cookie and in the code you can see its value. You can replicate this behavior via firebug and see the qbn.parentid cookie value has the realm after authorization is done.