I got the google cloud endpoints working with an android app and backend written in python app engine. When I uninstalled and re-installed my app during development, I started running into a 401 error from the backend. This is without any changes in backend after I got it to work the first time. So,
I cleaned up debug.keystore file in C:\Users\username\.android and
also other files in the same directory.Regenerated the
debug.keystore file again and got the new sha1 footprint
Deleted the API project in google apps console and regenerated web
client_id & android client_id with new sha1 footprint
Used the new web client_id and android client_id in server side code
Also updated the android cliend_id in audience scope in android code
After I installed the app now, still see the same 401 error. My app engine logs show this,
I 2013-04-07 16:45:27.297 Checking for id_token.
W 2013-04-07 16:45:27.299 id_token verification failed: Invalid token signature: eyJhbGciOiJSUzI1NiIsImtpZCI6IjMzMTJkNDc0ZWUzNWRjNWU4YzFhMDE4OTkzOGZkN2E3YmUzYmRlZjYifQ.eyJpc3Mi
I 2013-04-07 16:45:27.299 Checking for oauth token.
I was able to gather more information based on Cloud endpoints oauth2 error and http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html,
I decoded the id_token being sent to the app_engine server and found that it has these fields,
cid = Android app client_id that I registered in app console for
azp = Android app client_id
aud = web client_id for App engine
iss = accounts.google.com
verified_email = true
email_verified = true
email = account i selected in account picker in android app
exp = a datetime having a value in the future
first_segment = {u'alg': u'RS256', u'kid': u'3312d474ee35dc5e8c1a0189938fd7a7be3bdef6'}
Above values seem right, but id_verification fails. Not sure why.
Had the issue again. Happens, when I reinstall my app after uninstalling. Cloudend points service threw invalid token error. Used the token and verified it using the url. Get an output like below which means the token is valid.
"issuer": "accounts.google.com",
"issued_to": "172895167251-1prp5r093hf2nro5bei2cmc1tqf4aasdu.apps.googleusercontent.com",
"audience": "172895167251.apps.googleusercontent.com",
"user_id": "myuserid",
"expires_in": 3086,
"issued_at": 1365644284,
"email": "emailIselectedinadroidapp",
"verified_email": true
Interesting. What happens if you send the id_token to tokeninfo endpoint?:
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=$id_token
Related
I have an android app with a python server. I need the server to have access to the users' emails constantly, so I'm following this guide:
https://developers.google.com/identity/sign-in/android/offline-access
def google_api(auth_token):
# If this request does not have X-Requested-With header, this could be a CSRF
#if not request.headers.get('X-Requested-With'):
#abort(403)
# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.developers.google.com/apis/credentials
CLIENT_SECRET_FILE = 'secret.json'
# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
CLIENT_SECRET_FILE,
['https://www.googleapis.com/auth/gmail.readonly', 'profile', 'email'],
auth_token)
print credentials
return credentials.id_token
I get the following error:
FlowExchangeError: redirect_uri_mismatch
Here is the secret.json:
{"web":{"client_id":"REDACTED",
"project_id":"REDACTED",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"REDACTED",
"redirect_uris":["http://localhost:8080/"],
"javascript_origins":["http://localhost:8080"]}}
I've also tried using http://my_actual_domain.com:5000/ for the redirect_uris and it still returns the same error.
I don't understand what redirect_uris is supposed to be or mean? It's not mentioned in the guide I link at the top
Redirect uri is something which you give it when you created OAuth Client ID. The one you used in the application should match the one you gave while requesting the Client ID.
Please make sure this configured properly.
I currently have a login flow with facebook that is working 100% and I just added Google into the equation. I wrote the code so that my google provider tokens are formatted and cached in the same way as my facebook tokens. My code gets the facebook and google tokens and does the folling: (the following example is for the google login):
USER_GOOGLE_TOKEN = new JsonObject();
USER_GOOGLE_TOKEN.addProperty("access_token", accessToken);
cacheProviderToken(USER_GOOGLE_TOKEN.toString());
setCurrentProvider("google");
Then, in my authentication method in the activity, here is how I mobileserviceclient.login:
MobileServiceAuthenticationProvider provider = null;
if (getCurrentProvider().equals("facebook")){
provider = MobileServiceAuthenticationProvider.Facebook;
} else if (getCurrentProvider().equals("google")) {
provider = MobileServiceAuthenticationProvider.Google;
}
token.addProperty("access_token", googleToken);
ListenableFuture<MobileServiceUser> mLogin = mClient.login(MobileServiceAuthenticationProvider.Google, getProviderToken());
The problem is that Facebook works and Google produces a 401 unauthorized.
The specific error I get (before the future returns failed), is: W/DefaultRequestDirector: `Authentication error: Unable to respond to any of these challenges: {}. Azure logs show that it is a 401 unauthorized.
I am successfully printing the Google-Sign-In token and formatting it in the same way as my facebook tokens before trying to use them. Are my google keys wrong? I regenerated them once already and I tried setting origions to https, I tried different key combos, and I even got rid of my google-services.json(apparently its not needed). I did not use the client key that was generated on this google doc.
Another question here is how does the authentication settings of my Azure Mobile App come into play? I had originally configured my facebook before migrating from a MobileService to MobileApp, and now I do not need to use the "turnkey authentication" feature for it to work. I went ahead and entered my app key and secret for facebook, but it definitely works regardless of whether or not this is enabled. Is this a necessary feature for oauth?
Also, Depending on the token format on Login and the On/Off status of the AUthentication feature, the errror: W/DefaultRequestDirector: Authentication error: Unable to respond to any of these challenges: {}
, will have some text in the curly brackets about the azure endpoint.
Thank you so much if you can help. Such a picky issue here.
The App Service Authentication / Authorization is incompatible with a migrated Mobile Service and must remain off.
To update Authentication settings with a migrated Mobile Service, you set the app settings. For facebook, that's MS_FacebookAppID and MS_FacebookAppSecret. For Google, that's MS_GoogleClientID and MS_GoogleClientSecret.
For reference, check out https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/#authentication
I am an Android developer building my first Google App Engine (java) back-end for my apps. I don't want anybody else to access this API other than my app. (I plan to use App engine for verifying InApp purchases in my Android app). My data is not relevant to users so,
I don't want users to be able to access my API even if they are logged in with their Google accounts (on web or Android devices).
I followed the steps mentioned in - "Specifying authorized clients in the API backend"
(https://developers.google.com/appengine/docs/java/endpoints/auth)
like generating client IDs and add them in #Api (clientIds and audiences)
except "Add a User parameter" - since I don't need user authentication.
Then I deployed App engine and I am still able to access the API through API explorer (https://your_app_id.appspot.com/_ah/api/explorer)
(I haven't added API_EXPLORER client ID)
I tested with the APK that was built with the endpoint libs before adding client IDs and can still access the API.
Is adding a "User parameter" to all endpoint APIs a must? to achieve my purpose (restrict API to only my Android apps).
Can I pass null as userAccount name from Android client and ignore user parameter value on server (since it will be null)? Will this ensure that the API is accessible only from my android apps (since the client ID is generated for my package name and SHA1 of the APK?)
Should I use something like a service account for this purpose?
The documentation says for Android, both Android and Web client IDs must be added and audience must be the same as web client ID. Does this open access to any other web client? can I skip mentioning web client ID and still achieve my purpose?
Appreciate your time and help.
...... updating with my further investigation ...
I did the following:
Added User parameter to APIs on backend - but did not check for null value. API can still be accessed without passing any credentials (from Android debug APK and API explorer)
Then, I tried
mCredential = GoogleAccountCredential.usingAudience(this, "server:client_id:" + WEB_CLIENT_ID);
mCredential.setSelectedAccountName(null);
and passed this credential to API builder (as suggested in some other posts)
Caused FATAL EXCEPTION. So, we can't pass null account name.
I could call the API using API explorer without OAuth. But when I enabled OAuth, it gave error saying this client ID is not allowed! ( I haven't yet added com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID in client_ids{})
Then I added code to throw OAuthRequestException on the backend if the user is null. This resulted in API explorer getting errors without OAuth. It works with OAuth enabled after adding API_EXPLORER_CLIENT_ID to client_ids)
Added code to pass valid user account name(email) from my Android app. Then, I am able to access API only with my release APK. Even the debug APK gets exceptions! - which is what I expected..So, I assume no other Android apps will be able to access this API.
So, not checking for null user on back-end API is a bad idea (as suggested in other posts). It is as good as not mentioning any client_ids and not having User param.
Only question I have at this moment is: If some one can figure out the WEB_CLIENT_ID from the APK, will they be able to use it to build a web client to access my API (I haven't mentioned client secret anywhere in the code. So I am thinking this is not possible).
I did search Google groups and Stackoverflow, but still it is not clear.
(Authenticate my “app” to Google cloud endpoints not a “user”)
Authenticate my "app" to Google Cloud Endpoints not a "user"
(How do I protect my API that was built using Google Cloud Endpoints?)
How do I protect my API that was built using Google Cloud Endpoints?
(Restrict access to google cloud endpoints to Android app)
Restrict access to google cloud endpoints to Android app
I had a similar issue, not between Android and App Engine, but between a separate server and App Engine. The way I handled it was to add a signature hash field as a parameter to each API call. If the request had an improper signature, it would be denied.
For example, suppose your API end-point is example.com/api/do_thing?param1=foo. I would hash the entire url, along with a secret key, and then append the result of the hash to the request: example.com/api/do_thing?param1=foo&hash=[some long hex value].
Then, on the server side, I would first remove the hash from the url request, then run the hash on everything that was remaining. Finally, you check whether the calculated hash matches the one that was sent with the request and if they don't, you can deny the request.
It is very important however that your secret key remain secret. You have to be careful with this on Android because someone could attempt to decompile your APK.
Facing the same problem than you ! Authenticate Android End point without Google User Account is just impossible !
So here is my way to resolv this problem, without any user interaction (Maybe not the right but that works, and you've got strong authentication (SHA1 + Google Account)):
HERE IS MY ANDROID CODE
Get and Build Valid Credential
//Get all accounts from my Android Phone
String validGoogleAccount = null;
Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
Account[] accounts = AccountManager.get(context).getAccounts();
for (Account account : accounts) {
if (emailPattern.matcher(account.name).matches()) {
//Just store mail if countain gmail.com
if (account.name.toString().contains("gmail.com")&&account.type.toString().contains("com.google")){
validGoogleAccount=account.name.toString();
}
}
}
//Build Credential with valid google account
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this,"server:client_id:301991144702-5qkqclsogd0b4fnkhrja7hppshrvp4kh.apps.googleusercontent.com");
credential.setSelectedAccountName(validGoogleAccount);
Use this credential for secure calls
Campagneendpoint.Builder endpointBuilder = new Campagneendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential);
HERE IS MY API BACKEND CODE:
API Annotation
#Api(
scopes=CONSTANTES.EMAIL_SCOPE,
clientIds = {CONSTANTES.ANDROID_CLIENT_ID,
CONSTANTES.WEB_CLIENT_ID,
com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID},
audiences = {CONSTANTES.ANDROID_AUDIENCE},
name = "campagneendpoint",
version = "v1"
)
Method code:
public Collection<Campagne> getCampagnes(#Named("NumPortable")String NumPortable, User user) throws UnauthorizedException {
if (user == null) throw new UnauthorizedException("User is Not Valid");
return CampagneCRUD.getInstance().findCampagne(NumPortable);
}
For the moment, it only works on Android (I don't know how we gonna do on IOS..)..
Hope It will help you !
Google provides ways to do this for Android, web and iOS
The steps involves:
Specifying a client Id for apps you want to allow to make requests to your API
Adding a User parameter to all exposed methods to be protected by authorization.
Generating the client library again for any Android clients
Redeploying your backend API.
Updating the regenerated jar file to your Android project for your Android client.
These steps are laid out in clear detail on Google's Using Auth with Endpoints and also on this blog
Facing the same problem, here are the result of my research :
Added Android cliend id with SHA1 fingerprint in Google console
Use of it in the API annotation
BUT :
If i dont add user parameter to methods : the check about android app client id does not work
If I add the USER parameter but do not ask the user to choose its google account to create the credential ... also it does not work ...
Conclusion : It seems to be mandatory to connect a user account for the check about the app client id to be executed ... I really do not understand why because no link exist between the 2 processes
Access this site
Choose your project, go to credentials section
Create a new api key
Create a new android key
Click on "Edit allowed android applications" and enter your SHA1 key; your android package name
Let me know if this solves the issues.
I need to verify on my server each Android purchase that was made before by user in my Android APP.
I though that working with google-api-php-client it would be easy the authentication and managing of the token in server. But there aren't any sample, and yesterday Google published new version 0.6.3 providing in-app-purchases service.
I followed -> *code.google.com/p/google-api-php-client/wiki/OAuth2#Service_Accounts*
On my code.google.com/apis/console/ I pushed on, "Google Play Android Developer API" and I configured the "service account" in API Access.
From Android client APP, server recives the PACKAGE_NAME, PRODUCT_ID and purchase TOKEN.
My server code is the following:
require_once '../../src/Google_Client.php';
require_once '../../src/contrib/Google_AndroidpublisherService.php';
// Set your client id, service account name, and the path to your private key.
// For more information about obtaining these keys, visit:
// https://developers.google.com/console/help/#service_accounts
const CLIENT_ID = 'asdf.apps.googleusercontent.com';
const SERVICE_ACCOUNT_NAME = 'asdf#developer.gserviceaccount.com';
// Make sure you keep your key.p12 file in a secure location, and isn't
// readable by others.
const KEY_FILE = '../../asdf/privatekey.p12';;
$client = new Google_Client();
$client->setApplicationName({APP_PACKAGE_NAME});
// Set your cached access token. Remember to replace $_SESSION with a
// real database or memcached.
session_start();
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
// Load the key in PKCS 12 format (you need to download this from the
// Google API Console when the service account was created.
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new Google_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/androidpublisher'),
$key)
);
$client->setClientId(CLIENT_ID);
$service = new Google_AndroidPublisherService($client);
$res = $service->inapppurchases->get({APP_PACKAGE_NAME},{APP_PACKAGE_NAME.PRODUCT_ID}, {PURCHASE_TOKEN});
var_dump($res);
The error showed is:
Google_ServiceException: Error calling GET https://www.googleapis.com/androidpublisher
/v1.1/applications/{APP_PACKAGE_NAME}/inapp/{APP_PACKAGE_NAME.PRODUCT_ID}/purchases
/{PURCHASE_TOKEN}: (401) This developer account does not own the application. in
/.../google-api-php-client/src/io/Google_REST.php on line 66 Call Stack: 0.0201
266376 1. {main}() ............
Token is correct, and I'm working with the same account in Google API Console(https://code.google.com/apis/console) and Google Developer Console (https://play.google.com/apps/publish/). I'm only using Service account api, and don't working with Client ID for web applications, and Simple API Access. For security I changed here some code values.
Could somebody help me to know what's wrong on my purchase server verification using Google API please?How I know the owner of my app? Have something to do with Google Apis new project, project domain, project number, project ID, etc...?
I think my problem was because I was trying to use Service Accounts with a Google Apps Gmail own account ( non #gmail.com account ).
I had to delegate domain-wide authority to my service account.
And I had to instantiate a Android Publisher Service as follows: ( only founded in Google Api Drive documentation ).
I added "sub" parameter programmatically in Google_AssertionCredentials like follows:
$auth = new Google_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
'https://www.googleapis.com/auth/androidpublisher',
$key);
$auth->sub = "myown#email.com";
$client->setAssertionCredentials($auth);
The documentation in Google Play Android Developer API is very poor, and Google Support doesn't help, they redirects you to documentation. Google PHP developers even don't know how Service Accounts works.
In spite of having found the answer by myself, Google needs to improve all new Google Play In-app Billing API version 3.
I've been having problems implementing Google Play Services login on my android app and passing the authorisation code to my backend server, so the server will exchange the code for access token and refresh token.
First let me write a few lines what has already been tried/read:
on code.google.com/apis/console I've created a new project, with two clients (WEB client and Android installed client)
read articles on https://developers.google.com/+/mobile/android/sign-in#cross-platform_single_sign_on and http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html
Next I wrote simple android app (based on Google Play Services sample auth app) and a simple python code using gdata (using web service client_id and secret).
On android app I first used four scopes delimited with space and got a token. If I use this token in my python code I always get {'error_message': 'unauthorized_client'}.
Then I tried to change the scope to this values and always got invalid scope error.
oauth2:server:client_id:server-client-id:api_scope:scope1 scope2
audience:server:client_id:server-client-id:api_scope:scope1 scope2
oauth2:audience:server:client_id:server-client-id:api_scope:scope1 scope2
For server-client-id I used the client_id of web server client, android client, other client
Please can anyone help me with this problem.
Thanx
Here is the code for python backend
import gdata
import gdata.gauth
CLIENT_ID = 'client_id'
CLIENT_SECRET = 'secret_id'
SCOPES = ["https://www.google.com/m8/feeds", "https://mail.google.com", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"]
USER_AGENT = 'my-app'
token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, scope=' '.join(SCOPES), user_agent=USER_AGENT)
print "token ", token
print token.generate_authorize_url(redirect_url='urn:ietf:wg:oauth:2.0:oob')
try:
print token.get_access_token("token")
except Exception, e:
print e
print e.__dict__
You are retrieving an authorization code, not an access token. These are two different things.
Authorization codes can be used in your server side to get an access token. They are not access tokens and cannot be used as such.