I'm building an app that uses Google Cloud Speech.
I have a Google Service account key in my app, and I use it to call the API.
It works well when used by one user, but does not work when multiple users use it at the same time.
For example, only one user is available or all are unavailable.
The rights of the service account key are project owner.
I think it's a service account key issue...
How do I fix it?
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
#Override
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
// Check if the current token is still valid for a while
if (tokenValue != null && expirationTime > 0) {
if (expirationTime > System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION_TOLERANCE) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
}
final InputStream stream = mContext.getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
prefs.edit()
.putString(PREF_ACCESS_TOKEN_VALUE, token.getTokenValue())
.putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, token.getExpirationTime().getTime())
.apply();
return token;
} catch (IOException e) {
Log.e(TAG, "Failed to obtain access token.", e);
}
return null;
}
#Override
protected void onPostExecute(AccessToken accessToken) {
mAccessTokenTask = null;
final ManagedChannel channel = new OkHttpChannelProvider()
.builderForAddress(GOOGLE_API_HOSTNAME, GOOGLE_API_PORT)
.nameResolverFactory(new DnsNameResolverProvider())
.intercept(new GoogleCredentialsInterceptor(new GoogleCredentials(accessToken)
.createScoped(SCOPE)))
.build();
mApi = SpeechGrpc.newStub(channel);
// Schedule access token refresh before it expires
if (mHandler != null) {
mHandler.postDelayed(mFetchAccessTokenRunnable,
Math.max(accessToken.getExpirationTime().getTime() - System.currentTimeMillis() - ACCESS_TOKEN_FETCH_MARGIN, ACCESS_TOKEN_EXPIRATION_TOLERANCE));
}
}
}
This code is the code that calls 'credential.json' file on Android and gets 'Access token'.
The server for this app is python and communicates via http.
https://github.com/GoogleCloudPlatform/android-docs-samples/tree/master/speech/Speech
The description in the link above tells you to delegate the authentication to the server.
I want to write that part with python code.
What should I do?
In the link you provided in the description, they suggest you to read first the basic authentication concepts document. In your case, use a service account for the Android application.
I understand that you have already been able to provide end user credentials to a Google Cloud Platform API, as for example Cloud Speech API.
If you want to authenticate multiple users to your application you should use instead Firebase authentication. The link contains a brief explanation and a tutorial.
There are several Python client libraries for GCP that you can use, depending on what operations do you want to perform on the server. And regarding Python authentication on the server side, this documentation shows how the authentication for Google Cloud Storage works (have this example in mind as a reference).
Related
I'm trying to retrieve a Google user's contacts list.
This getContacts() is called in the doInBackground() of an AsyncTask.
I have -at this point- already received a token.
I based my self on this codeLab sample : https://codelabs.developers.google.com/codelabs/appauth-android-codelab/#0
I'm modifying the point n°10 of this tutorial so trying to fetch user's contact list instead of the user's personal info ( < which is working)
private List<ContactEntry> getContacts() {
ContactsService contactsService = new ContactsService("MY_PRODUCT_NAME");
contactsService.setHeader("Authorization", "Bearer " + token);
try {
URL feedUrl = new URL("https://www.google.com/m8/feeds/contacts/default/full");
Query myQuery = new Query(feedUrl);
ContactFeed resultFeed = contactsService.query(myQuery, ContactFeed.class);
List<ContactEntry> contactEntries = resultFeed.getEntries();
return contactEntries;
} catch (Exception e) {
}
return null;
}
My problem is that I always get an Exception with this message :
java.lang.NullPointerException: No authentication header information
Any help?
thanks in advance
You may refer with this related thead. Try modifying the client library's user
agent:
A workaround is to change the user agent of your client after you create it:
ContactsService service = new ContactsService(applicationName);
service.getRequestFactory().setHeader("User-Agent", applicationName);
Also based from this post, service.setOAuth2Credentials() doesn't refresh token internally like the other services in Google Sites, Google Drive and etc. If you build Google Credential, just add this line after building the Google Credential: googleCredential.refreshToken();.
I'm developing an Android app that consumes data from my own REST API server. I want to use Firebase authentication because it allows the user to login using Google, Facebook, Twitter... in a very simple way.
But I'm not sure how to use ID tokens:
Because ID tokens have expiration date, should I call getToken method on every request in the client app, so I'm sure I'm sending a valid token every time?
Should I call verifyIdToken in the server each time I receive a request from the client app?
I don't know what these methods (getToken and verifyIdToken) do under the hood, and because they are asynchronous, I fear they are doing a request to Firebase servers on every call. So I think that making 2 request to Firebase servers in each of my requests is not the way to go...
Both getToken() and VerifyIdToken() are designed to be called for every outgoing/incoming request.
1) Although getToken() is asynchronous, the Firebase Android SDK actually caches the current Firebase user token in local storage. So long as the cached token is still valid (i.e. within one hour since issued), getToken() returns the token immediately. Only when the cached token expires does the SDK fetch a new token from remote Firebase server.
2) VerifyIdToken() is also optimized for performance. It caches the Firebase token public cert (valid for 6 hours) which is used to validate the token signature on local machine. No RPC is involved except for downloading the public cert.
You refresh token each time when is no more valid. And yes, you should verify token on server-side each time. If is no more valid, you send 401 error code with error message (if you want). Verify token is used when you refresh token, and token is append to each request. If you use OkHttp you can create an interceptor that is adding token in header to each request and also can refresh token when error code is 401.
POST https://YOUR_AUTH0_DOMAIN/delegation
Content-Type: 'application/json'
{
"client_id": "YOUR_CLIENT_ID",
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"refresh_token": "your_refresh_token",
"api_type": "app"
}
From what you have explained in the question, I guess you are talking about cross client resource access using Google sign in. And specifically you seem to be interested in obtaining the Id token once and use it without having to obtain on each subsequent API call.
This more or less is synonymous with the offline access mechanism.
In offline access, the Client I.e. the Android app asks for user authorisation for requested scopes. Upon authorisation, instead of issuing an access token, auth server returns a short lived authorisation code which can be used to generate an access token and refresh token.
The client then can pass the authorisation code to the backend over a secure connection. Backend server can retrieve the author token and refresh token and store them in a secure location. The access token is short lived and can be used to access scoped resources for a short time and refreshed from time to time using the refresh token. The refresh token does not expire but can be revoked. If revoked, server app should ask the client app to re-fetch the author code.
Please go through this link which details the complete infrastructure along with the steps to be followed both by client and server app -
https://developers.google.com/identity/protocols/CrossClientAuth
Now coming to your question, you should use a slightly different API to obtain the auth code. Check out this API -
https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInOptions.Builder.html#requestServerAuthCode(java.lang.String)
Sample code at - https://developers.google.com/identity/sign-in/android/offline-access
Use below code in your application class and regId is the value holder for your device token.
private void checkPlayService() {
// Check device for Play Services APK. If check succeeds, proceed with
// GCM registration.
if (checkPlayServices()) {
GoogleCloudMessaging googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
regId = getRegistrationId();
if (TextUtils.isEmpty(regId)) {
registerInBackground();
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
private String getRegistrationId() {
String registrationId = sp.getString(Consts.PROPERTY_REG_ID, "");
if (TextUtils.isEmpty(registrationId)) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = sp.getInt(PROPERTY_APP_VERSION,0);
int currentVersion = getAppVersion();
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (googleCloudMessaging == null) {
googleCloudMessaging = GoogleCloudMessaging.getInstance(activity);
}
regId = googleCloudMessaging.register(Consts.PROJECT_NUMBER);
msg = "Device registered, registration ID=" + regId;
Log.e("GCMID",msg);
storeRegistrationId(regId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
Log.i(TAG, msg + "\n");
}
}.execute(null, null, null);
}
private void storeRegistrationId(String regId) {
int appVersion = getAppVersion();
Log.i(TAG, "Saving regId on app version " + appVersion);
sp.edit().putString(Consts.PROPERTY_REG_ID, regId).commit();
sp.edit().putInt(PROPERTY_APP_VERSION, appVersion).commit();
}
I want to restrict my API endpoints access only to my android app, but without google_account/password.
I've the choice of those methods : https://developers.google.com/accounts/docs/OAuth2
For test, I succeeded to authenticate my android app to my API with this method: https://cloud.google.com/appengine/docs/python/endpoints/consume_android
==> This method allow you to authenticate your app with combo:
Login/password (Google account)
SHA1 and package name of your android APP
So if someone get my code (Decompiling apk) and modify my android code, they can't access to my API because SHA1 fingerprint of my app will change. (I tested it, and it works =) )
This method works fine, but I don't want Google login/password for authentication..
So I tried this method: https://developers.google.com/accounts/docs/OAuth2ServiceAccount
I successfully authenticate my android app, BUT, if my android code is modified by someone else(So the SHA1 changed), my android app can still connect to my API !! So if someone get my package and decompile it, he'll changed freely code and successfully access to my API..
Here is my API Code:
#ApiMethod( name = "ListCampagnes", httpMethod = ApiMethod.HttpMethod.GET, path="list", clientIds = {CONSTANTES.ANDROID_CLIENT_ID, CONSTANTES.WEB_CLIENT_ID, CONSTANTES.SERVICE_CLIENT_ID, com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}, audiences = {CONSTANTES.ANDROID_AUDIENCE})
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);
}
Here is my android code:
GoogleCredential credentialToAppengine;
try {
String p12Password = "notasecret";
KeyStore keystore = KeyStore.getInstance("PKCS12");
InputStream keyFileStream = getAssets().open("59ce5a08e110.p12");
keystore.load(keyFileStream, p12Password.toCharArray());
PrivateKey key = (PrivateKey)keystore.getKey("privatekey", p12Password.toCharArray());
credentialToAppengine = new GoogleCredential.Builder().setTransport(AndroidHttp.newCompatibleTransport()).setJsonFactory(new JacksonFactory()).setServiceAccountId("301991144702-3v9ikfp4lsmokee1utkucj35847eddvg#developer.gserviceaccount.com").setServiceAccountPrivateKey(key).setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email")).build();
} catch (GeneralSecurityException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
Do I try an other method for authenticate my android App ? Or did I missing something in my API code ?
Thanks a looot in advance,
Authenticate Android End point without Google User Account is just impossible ! I tried every ways but still doesn't works !
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 !
I am using the Amazon Web Service to send push notifications directly to a device. After I install the app I get the device id, that I need to manually add to the Amazon SNS. I would like to know if there is anyway to register the device id directly with the amazon server the moment the user starts the application.
I have read this, but found it difficult to understand. Does anyone have any previous experience of how to do this?
EDIT 2 (What I have done so far)
I've followed the instructions from this link
I download the snspobilepush.zip file as instructed and extract and import the project into eclipse. I add the GCM project number, add the jar files and run the application. I get my device registration Id.
I open the Amazon SNS, add my device id and I publish a message. I receive the message on my mobile phone. Works great so far.
MY PROBLEM
I would be having a lot of potential users for my application. So adding every device id manually to the SNS makes no sense. I need the Amazon SNS to directly register my device id when I start the app. Is there any option for me to do that? I couldn't find any definitive answer in the docs.
This link tells me to Use the "AWS Token Vending Service". However, I could not find any example of how to do that.
Using the AmazonSNSClient documented here:
http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
it should be possible to register using code similar to this:
AWSCredentials awsCredentials = new BasicAWSCredentials("XXXXXX", "XXXXXXXXXXXXXXX");
String platformApplicationArn = "arn:aws:sns:us-east-1:123456789:app/GCM/myappname";
AmazonSNSClient pushClient = new AmazonSNSClient(awsCredentials);
String customPushData = "my custom data";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
It was not liking my ARN, but that was a stupid typo that Reid pointed out and is now fixed above.
There is Android AWS SDK available to use. Check out the documentation link: http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/
Also, more information available: http://aws.amazon.com/sdkforandroid/
This is working for Firebase and Cognito. An AsyncTask is necessary to avoid running on the Main Thread.
private class RegisterIdForAWS extends AsyncTask<String, Void, Void> {
private Exception exception;
protected Void doInBackground(String... urls) {
try {
String pushNotificationRegId = FirebaseInstanceId.getInstance().getToken();
if (pushNotificationRegId != null) {
CognitoCachingCredentialsProvider provider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"us-west-2:aaaaaaaaa-1234-1234-1234-0bbbbbbbbbbbb",
Regions.US_WEST_2);
String platformApplicationArn = "arn:aws:sns:us-west-2:123456789:app/GCM/appname";
AmazonSNSClient pushClient = new AmazonSNSClient(provider);
pushClient.setRegion(Region.getRegion(Regions.US_WEST_2));
String customPushData = "";
CreatePlatformEndpointRequest platformEndpointRequest = new CreatePlatformEndpointRequest();
platformEndpointRequest.setCustomUserData(customPushData);
platformEndpointRequest.setToken(pushNotificationRegId);
platformEndpointRequest.setPlatformApplicationArn(platformApplicationArn);
CreatePlatformEndpointResult result = pushClient.createPlatformEndpoint(platformEndpointRequest);
Log.w(TAG, "Amazon Push reg result: " + result);
}
} catch (Exception e) {
this.exception = e;
}
return null;
}
protected void onPostExecute(String text) {
Log.w(TAG, "Amazon Push reg Finished");
}
}
i developed one app integrated with linkedIn..!
i do SignIn authentication in linkedIn using OAuth Service to post the Network Update..but now how to sign out (de-authenticate) to the LinkedIn automatically?
Thanks in adv..
As per the official blog
Token Invalidation
Now you can invalidate an OAuth token for your application. Just send an OAuth signed GET request to:
https://api.linkedin.com/uas/oauth/invalidateToken
A 200 response indicates that the token was successfully invalidated.
However as per this :
Third party applications do not have any way to log a user out from
LinkedIn - this is controlled by the website. Invalidating the token
makes the user re-authorize the next time they try to use the
application, but once they have logged into LinkedIn their browser
will remain logged in until they log out via the website.
So In conclusion : as of this date of writing, Linked In does not give this support to 3rd Party Applications
Reading your question i have also tried to find solution and also talked to Mr. Nabeel Siddiqui - Author of linkedin-j API
and this was his reply when i asked if it's possible to sign out using linkedin-j api?
Hi Mayur
There is a method LinkedInOAuthService#invalidateAccessToken that is supposed to invalidate your access token. Its not used much by the community so I am not sure if it works as expected or not. Do try it and let me know if there are problems.
Regards
Nabeel Mukhtar
so in my activity i tried it using this way.
final LinkedInOAuthService oAuthService = LinkedInOAuthServiceFactory.getInstance().createLinkedInOAuthService(consumerKey, consumerSecret);
final LinkedInApiClientFactory factory = LinkedInApiClientFactory.newInstance(consumerKey, consumerSecret);
LinkedInRequestToken liToken;
LinkedInApiClient client;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
liToken = oAuthService.getOAuthRequestToken(CALLBACKURL);
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(liToken.getAuthorizationUrl()));
startActivity(i);
}
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
Uri uri = intent.getData();
if (uri != null && uri.toString().startsWith(CALLBACKURL))
{
String verifier = intent.getData().getQueryParameter("oauth_verifier");
LinkedInAccessToken accessToken = oAuthService.getOAuthAccessToken(liToken, verifier);
client = factory.createLinkedInApiClient(accessToken);
Connections con = client.getConnectionsForCurrentUser();
//AFTER FETCHING THE DATA I HAVE DONE
oAuthService.invalidateAccessToken(accessToken);
//this is for sign out
}
}
Please, Try this way once and tell me if it solves your problem.
cause I have also donwloaded and seen the SourceCode for linkedin-j API and in
LinkedInOAuthServiceImpl.java
they have given the function and that function also works if we write the same code in our file.
that is,
#Override
public void invalidateAccessToken(LinkedInAccessToken accessToken) {
if (accessToken == null) {
throw new IllegalArgumentException("access token cannot be null.");
}
try {
URL url = new URL(LinkedInApiUrls.LINKED_IN_OAUTH_INVALIDATE_TOKEN_URL);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
final OAuthConsumer consumer = getOAuthConsumer();
consumer.setTokenWithSecret(accessToken.getToken(), accessToken.getTokenSecret());
consumer.sign(request);
request.connect();
if (request.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new LinkedInOAuthServiceException(convertStreamToString(request.getErrorStream()));
}
} catch (Exception e) {
throw new LinkedInOAuthServiceException(e);
}
}