I am building a simple test android app, where I am trying to get data about a particular video. I have taken the SHA1 fingerprint of my debug keystore and created a google api key with it (and my package name). I have activated the youtube api service for it.
This is my code:
YouTube youtube = new YouTube.Builder(new NetHttpTransport(), new GsonFactory(), new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {}
}).setApplicationName("MyApp").build();
try {
YouTube.Videos.List listVideosRequest = youtube.videos().list("snippet,contentDetails");
listVideosRequest.setId("A3PDXmYoF5U");
listVideosRequest.setKey(GoogleAPIKey.DEBUG_GOOGLE_API_KEY);
VideoListResponse youtubeResponse = listVideosRequest.execute();
List<Video> youtubeVideos = youtubeResponse.getItems();
return youtubeVideos;
} catch (IOException e) {
Log.e("MyApp", e.getLocalizedMessage());
return null;
}
So, i always get an exception: 403, Access not configured.
I am running out of ideas, what could be wrong.
Anyone have successfully used the youtube api for android yet with an api key?
Update:
I just deeply debugged the google libraries in order to find, what the actual request looks like, that is sent to google. It is this:
https://www.googleapis.com/youtube/v3/videos?id=A3PDXmYoF5U&key=[my-debug-google-api-key]&part=snippet,contentDetails
with these headers set:
Accept-Encoding: gzip
User-Agent: MyApp Google-HTTP-Java-Client/1.15.0-rc (gzip)
I don't see anything wrong there.
Ok, after a lot of reading I found this google doc :
You must send an authorization token for every insert, update, and delete request. You must also send an authorization token for any request that retrieves the authenticated user's private data.
In addition, some API methods for retrieving resources may support parameters that require authorization or may contain additional metadata when requests are authorized. For example, a request to retrieve a user's uploaded videos may also contain private videos if the request is authorized by that specific user.
So conclusion is simple - you should use API key only for reading non-private data while videos also may be private. In my case I've tried to load Channels anonymously - and it works when I requesting only channel_id. When I've requested for contentDetails (which contains link to uploads) - I've also got 403 error.
Related
As the title says, I'm trying to use the Google Sign-In API with a Spring Boot backend server, as described here.
Just to describe the context, the Spring backend is basically a resource+authentication server, that is currently providing Oauth2 authentication to a second spring boot application containing the frontend website, via Google SSO or simple form login (similar to what's described here).
My original idea was to mimic the #EnableOauth2Sso annotation by simply providing an access token to the android app and attach it to every request as "Bearer ".
Using the user credentials for this was pretty straightforward: I simply make a request to the server at "/oauth/token", using those credentials inserted by the user as authentication and I correctly receive the access token.
Now, I have absolutely no idea on how to build a similar procedure with the Google API in Android. The tutorial page I linked before describes how to get a token ID and how the server should validate it, but after that I don't know what to do.
So far I've managed to add a filter to the security chain that simply checks the token like this:
private Authentication attemptOpenIDAuthentication(#NonNull String tokenString){
String clientId = authServices.getClientId();
GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, factory)
.setAudience(Arrays.asList(clientId, androidClient))
.build();
try {
GoogleIdToken token = verifier.verify(tokenString);
if (token != null) {
return authServices.loadAuthentication(token.getPayload());
} else {
throw new InvalidTokenException("ID token is null");
}
} catch (GeneralSecurityException | IOException e) {
throw new BadCredentialsException("Could not validate ID token");
}
}
This manages indeed to create an Authentication object, but how can I generate an access token after the authentication filtering?
To recap, so far I've got:
The Android app successfully retrieves the Google token ID and sends it to the server
The server sucessfully intercepts the request and validates the token
I'm basically missing the third point where I return a proper access token to the Android client.
Here you are a simple scheme to better understand the situation:
Is there any other way to validate the token and get an access token from the server, or should I completely change the authentication procedure on Android?
As far as I can tell: Yes, you need an access token from the server. If I understand this correctly, a webapp is already authenticated via Oauth on your backend, so the procedure is similar here: Load the user with the google-ID and generate a token. In my application I used a JWT which is valid for 30 days. If the token expires, the Google authentication in the app is usually still valid, so the token can be renewed using the Google ID. With Oauth you can also send a refresh-token directly.
It is important that the app always checks the Google authentication first and only in a second step that of the backend.
For the Authentication process on the backend u may need to manually implement a dedicated securityConfiguration for this. Have a look at the jhipster project, they implemented a custom jwt-authentication which may give you an idea how it works.
I'm using the Google Speech API in an Android app. The README states:
In this sample, we load the credential from a JSON file stored in a raw resource folder of this client app. You should never do this in your app. Instead, store the file in your server and obtain an access token from there.
Are there any samples regarding how to properly obtain an access token for a production app?
From what I've gathered, it seems that I can use Application Default Credentials provided via Compute Engine or GAE, but I have no idea how to actually respond with an access token to my app.
I'm the author of the sample.
Here's the part that should be on the server side. Basically, you have to store the JSON file securely on your server side, and fetch a new access token when you get a request from your mobile app. Mobile app should always talk with your server to get an access token. This way, you don't need to put your service account key in the APK.
// In response to a request from a mobile client
// Open the JSON file stored on the server side.
final InputStream stream = ...;
try {
// Initialize the credentials
final GoogleCredentials credentials =
GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
// Fetch a new access token.
final AccessToken token = credentials.refreshAccessToken();
// Return the token to the mobile app.
} catch (IOException e) {
// Maybe report this as an HTTP error.
}
If you don't use Java on your server side, you can find a client library for the language you use at Google Cloud Client Libraries.
Refer to Google Cloud Platform Auth Guide for basics about how to send requests from your backend to the API.
For Android, you might instead want to use the Android sample for Google Cloud Speech as the Cloud client library does not currently focus on Android support.
I am currently at a loss on how to proceed with my app.
I currently authenticate following the tutorial here and everything works swimmingly.Azure Mobile Apps Authentication
Where I am at a loss is how to use the id and/or token that is stored following this process to obtain basic profile information say a users email or their profile photo.
From what I have read online this is merely a azureId that stored not the google profile ID which I would use with the google+apis.
Has anyone got a reference that shows a novice programmer how to get the email address or userId required to use the google api.
The only reference I can find is a blog post from 2014. Surely there must be an easier way. And one specifically written to work on Mobile apps as opposed to mobile services. Blog post describing how to expand on authentication with google on mobile services which is no use
Here is my process
// We first try to load a token cache if one exists.
Log.v(TAG, "Click"+USERIDPREF );
if (loadUserTokenCache(mClient))
{
Log.v(TAG, "table" +mClient.getCurrentUser().toString());
createTable();
returnHome();
}
// If we failed to load a token cache, login and create a token cache
else
{
// Login using the Google provider.
final ListenableFuture<MobileServiceUser> mLogin = mClient.login(MobileServiceAuthenticationProvider.Google);
Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
#Override
public void onFailure(Throwable exc) {
Log.v(TAG, "Login On fail " +exc.getMessage() );
}
#Override
public void onSuccess(MobileServiceUser user) {
Log.v(TAG, "On Success" );
createTable();
cacheUserToken(mClient.getCurrentUser());
Log.v(TAG, "On Success" + mClient.getCurrentUser() );
returnHome();
}
});
}
The first documentation link you posted has the answer. From https://azure.microsoft.com/en-us/documentation/articles/app-service-authentication-overview/#working-with-user-identities-in-your-application:
Code that is written in any language or framework can get the information that it needs from these headers. For ASP.NET 4.6 apps, the ClaimsPrincipal is automatically set with the appropriate values.
Your application can also obtain additional user details through an HTTP GET on the /.auth/me endpoint of your application. A valid token that's included with the request will return a JSON payload with details about the provider that's being used, the underlying provider token, and some other user information. The Mobile Apps server SDKs provide helper methods to work with this data. For more information, see How to use the Azure Mobile Apps Node.js SDK, and Work with the .NET backend server SDK for Azure Mobile Apps.
So to summarize, you have different options depending on which language you use, but the most cross-platform option is to send an authenticated request to your mobile app's /.auth/me endpoint. You'll get back a JSON object which contains a bunch of user claims (name, provider-specific ID, email, etc.).
I am trying to authenticate the user using oauth2 api from Google. There are 2 platforms, Android and Web. For new users, I am fetching the one-time auth code on the client and sending it to the server on both platforms. Once server receives the code, it is fetching the access token and after that, the user details. The server is able to fetch the token from the one-time auth code sent from Android device, but it is failing for the code sent from Web.
This is my code snippet in JS for fetching one-time auth code..
gauth2.grantOfflineAccess({
'scope': 'profile email',
'redirect_uri': 'postmessage'
}).then(function(e){
if(!e){
// Error
}
else{
console.log('Auth code: '+e.code);
}
});
On server side, this is the POST request that I am doing to fetch the token from one-time code..
post_data = {'code': one_time_code, 'client_id': settings.GOOGLE_OAUTH2_CLIENT_ID, 'client_secret': settings.GOOGLE_OAUTH2_CLIENT_SECRET, 'grant_type': 'authorization_code'}
req = urllib2.Request("https://www.googleapis.com/oauth2/v3/token", data=urllib.urlencode(post_data), headers={'User-Agent': 'Mozilla/5.0'})
req.get_method = lambda: 'POST'
res = urllib2.urlopen(req)
if res.code == 200:
print "response 200 : "
else:
print "Access Token Validation Return Code: ", res.code
This web service call returns 200 and token when the auth-code is generated from Android. When it is sent from web, the same method throws the following error..
Error: 400, invalid request; missing parameter 'redirect_uri'
In this case, I don't need to send any redirect_uri, because I am reading the response there itself. But I still tried setting an empty value and also tried setting it to some value that exactly matches one of the values in google developers console. In that case, I am still getting..
Error: 400, invalid request; redirect_uri mismatch.
I cannot get my head around the fact that this web service call is working perfectly for android codes and not for web codes. Please note that scopes are different for Android app and Web app. But still, this seems to be some fundamental error. Any help would be appreciated.
I am trying to authorize with OAuth2 to use YouTube data API. I want to write a simple client for Android to have a possibility, for example, get lists of user subscriptions, playlist etc.
The problem is that each method I try (e.g. HTTP requests like https://www.googleapis.com/youtube/v3/subscriptions or YouTube library com.google.apis:google-api-services-youtube:v3-rev120-1.19.0), I get the same error:
401 Invalid Credentials
I do these steps:
Register app in Google's console. Enable YouTube Data API. Create OAuth credentials with my app package name and keystore.
Create Android API Key.
And here I have a lot of questions.
To have a possibility to get user subscriptions etc. do I have to register the app with OAuth2 credentials AND (?) Register API Key?
How can I use client_id and other data that is in client_secrets.json? How can I use this client_secrets.json at all? Do I need download it, and how to use it?
Because when I saw sample code, for example this:
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context.getApplicationContext(), Arrays.asList(SCOPES));
credential.setSelectedAccountName(accountName);
YouTube youtube = new YouTube.Builder(new NetHttpTransport(),
new JacksonFactory(), credential).setApplicationName(context.getString(R.string.app_name)).build();
Or used AccountManager and get token
accountManager.getAuthToken(userAccount, "oauth2:" + mScope, null, (Activity) mContext,
new OnTokenAcquired(), null);
where mScope="https://gdata.youtube.com"
and then use it this way
https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&maxResults=10&mine=true&key={here is my key}&access_token= ya29.bgJa5J8LHHu0Mu3P87mbPY9gfhSFzWUHh8gCw022FC6yfMO6GfVHveYr_BG1-HnWF-jpd-IDcGGVrF3gEf4UhSHPSrV76RAwqd4V17ixn72s1Xl6a0wQ4FVDz3cEZjUEN80o
I get the same authError error every time (in short):
"code": 401,
"message": "Invalid Credentials"
What I am doing wrong? How to use client_id in variant 1 and 2. Is it necessary? Because I think it is the reason why it does not work.