The google api project for my app contains a simple android key with two applications allowed. one is the com.examples.youtubeapidemo, the other is my app. If I take the com.examples.youtubeapidemo in and out of the key it fails as expected. The other app always returns this response:
11-06 08:06:52.321 32579-528/com.wfs.android.youtubesearchtest E/com.wfs.android.youtubesearchtest﹕ 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "usageLimits",
"message" : "Access Not Configured. Please use Google Developers Console to activate the API for your project.",
"reason" : "accessNotConfigured"
} ],
"message" : "Access Not Configured. Please use Google Developers Console to activate the API for your project."
}
my code is this:
[in an async task]
{
YouTube.Builder builder = new YouTube.Builder(
new NetHttpTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request)
throws IOException {
}
})
.setApplicationName((String.valueOf(R.string.app_name)));
return builder.build();
}
and this:
{
// ...
YouTube youTube = createYouTubeService();
YouTube.Search.List search = null;
try {
search = youTube.search().list("id,snippet");
} catch (IOException e) {
Log.e(TAG, e.getMessage());
return null;
}
search.setKey(Developerkey.DEVELOPER_KEY);
search.setQ(q[0]);
search.setType("video");
search.setFields("items(id/kind,id/videoId,snippet/title,snippet/thumbnails/default/url)");
search.setMaxResults(NUMBER_OF_VIDEOS_RETURNED);
SearchListResponse searchListResponse = null;
try {
searchListResponse = search.execute();
}
The exception is thrown in searchListResponse = search.execute();
I have tested the key, is there anything I may have missed in the code?
I also tested with a browser key and the com.examples.youtubeapidemo works with either one. My app still has the same issue; So all the posts about using a browser key instead did not work for me.
Ok this is old but I figured this out for my case and I thought it might help others. I went to oauth and it seemed to resolve.
the real issue is that if you use an unrestricted key [and maybe, depepending on the api enabled] have a billing account linked; the api should work. If it works unrestricted, you are on the right track.
Once you restrict it to android it will fail again with that key until you sign your app. The easiest way i found was the use a signing config for the variants under android in the gradle file.
signingConfigs {
debug {
storeFile file("/users/xxxxxxxxxx/Android/keystores/google_demos/debugandroid.jks")
storePassword "xxxxxxxxxx"
keyAlias "com.example.xxxxxxxxxx"
keyPassword "xxxxxxxx"
}
}
Related
I am developing a .Net 6 application, hosted in an Azure App Service and using Azure AD Authentication.
When viewing a Request page, I would like to check if the user belongs to an Azure Ad Group. This works sometimes, but users will periodically get an error when trying to view the page: "Access token has expired or is not yet valid."
I assume the token is being expired as if the user clears their cookies, AAD will re-authenticate them creating a new token and all is fine again, but but I haven't been able to find anything around refreshing tokens and am not sure where to go from here.
Has anyone experienced this behaviour and found a solution for it?
Here are some relevant sections of the code
Startup.cs File:
string[] initialScopes = Configuration.GetValue<string>("GraphAPI:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(options =>
{
Configuration.Bind("AzureAd", options);
}, initialScopes)
.AddInMemoryTokenCaches()
.AddMicrosoftGraph(options =>
{
options.Scopes = String.Join(' ', initialScopes);
});
AADGroupFunctions.cs
AADGroupFunctions.cs
private readonly GraphServiceClient _graphServiceClient;
public AADGroupFunctions(GraphServiceClient graphServiceClient)
{
_graphServiceClient = graphServiceClient;
}
public async Task<List<IADLookupModel>> FindUsersInGroup(string groupId)
{
var listOfUsers = new List<IADLookupModel>();
var filterString = $"startswith(mail, '{groupId}')";
var groups = await _graphServiceClient.Groups
.Request()
.Header("ConsistencyLevel", "eventual")
.Filter(filterString)
.Expand("members")
.Top(1)
.GetAsync();
if (groups.Any())
{
if (groups.First().Members.Any())
{
foreach (Microsoft.Graph.User user in groups.First().Members)
{
try
{
var mail = "";
if (user.Mail != null)
{
mail = user.Mail.ToLower();
listOfUsers.Add(new UserModel()
{
DisplayName = user.DisplayName,
UPN = user.UserPrincipalName.ToLower(),
Email = mail,
Description = user.JobTitle ?? ""
});
}
}
catch (Exception)
{
}
}
}
}
return listOfUsers;
}
Error Message when trying to call the FindUsersInGroup() function:
An unhandled exception occurred while processing the request.
ServiceException: Code: InvalidAuthenticationToken Message: Access token has expired or is not yet valid. Inner error: AdditionalData: date: 2022-02-21T17:37:46 request-id: [removed] client-request-id: [removed] ClientRequestld: [removed] Microsoft.Graph.HttpProvider.SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
Routing
The access token have a short lifetime, sometimes like an hour or even shorter. So you need to use a refresh token to ask AzureAd for a new access token when the current one is about to expire.
see this link https://learn.microsoft.com/en-us/azure/active-directory/develop/refresh-tokens
according to documentation we may use the following endpoints for fetching sensitivity labels:
/me/informationProtection/policy/labels (using delegated permissions)
/informationProtection/policy/labels (using application permission. App should have InformationProtectionPolicy.Read.All permission to use this end point)
The following C# code uses app permissions and it works on tenant1:
static void Main(string[] args)
{
string accessToken = getTokenImpl().Result;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("User-Agent", "PostmanRuntime/7.24.1");
using (var response = client.GetAsync($"https://graph.microsoft.com/beta/informationprotection/policy/labels").Result)
{
using (var content = response.Content)
{
string result = content.ReadAsStringAsync().Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine(result);
}
}
}
}
}
private static async Task<string> getTokenImpl()
{
string clientId = "...";
string clientSecret = "...";
string tenant = "{...}.onmicrosoft.com";
string authority = string.Format("https://login.microsoftonline.com/{0}", tenant);
var authContext = new AuthenticationContext(authority);
var creds = new ClientCredential(clientId, clientSecret);
var authResult = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", creds);
return authResult.AccessToken;
}
But it doesn't work on another tenant2 - there it always returns 404 "The resource could not be found" with the following inner exception "User not found to have labels, policy is empty". Here is full response:
{
"error": {
"code": "itemNotFound",
"message": "The resource could not be found.",
"innerError": {
"code": "notFound",
"message": "User not found to have labels, policy is empty",
"target": "userId",
"exception": null,
"date": "2020-11-18T09:29:20",
"request-id": "657ad51c-9cab-49f2-a242-50929cdc6950",
"client-request-id": "657ad51c-9cab-49f2-a242-50929cdc6950"
}
}
}
Interesting that attempt to call endpoint /me/informationProtection/policy/labels with delegated permissions on the same tenant2 gives the same error, but on tenant1 it also works. Did anybody face with this problem or have idea why it may happen? Need to mention that on tenant2 earlier we created and published several sensitivity labels for specific user - this user doesn't have neither O365 license nor Azure subscription. I.e. when you try to login to SPO/Azure and create site/group - sensitivity labels were not shown at all for this user. We tried to remove these sensitivity labels and their policies with audience targeting to this user, but both end points still return error.
PS. AAD app is Ok on tenant2 - it has InformationProtectionPolicy.Read.All permission and admin consent is granted:
Update 2020-11-25: behavior has been changed on both tenants without any change from our side: now on both tenants we get 502 Bad Gateway. Does MS rolls out this functionality globally now? Here is response which we get now from /beta/me/informationProtection/policy/labels:
{
"error":{
"code":"UnknownError",
"message":"<html>\r\n<head><title>502 Bad Gateway</title></head>\r\n<body>\r\n<center><h1>502 Bad Gateway</h1></center>\r\n<hr><center>Microsoft-Azure-Application-Gateway/v2</center>\r\n</body>\r\n</html>\r\n",
"innerError":{
"date":"2020-11-25T12:59:51",
"request-id":"93557ae1-b0d9-44a9-bbea-871f18e379ea",
"client-request-id":"93557ae1-b0d9-44a9-bbea-871f18e379ea"
}
}
}
Update 2020-12-07: it started to work by its own. I.e. MS has fixed that on backend side somehow for the tenant when this issue was reproduced.
I'm implementing the authorization code flow for Installed Application.
Code is similar to snippet below:
public static void main(String[] args) {
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport,
JSON_FACTORY,
IA_CLIENT,
IA_SECRET,
Collections.singleton(DriveScopes.DRIVE)).setDataStoreFactory(dataStoreFactory)
.build();
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
drive = new Drive.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName(APPLICATION_NAME).build();
System.out.println(drive.about());
return;
} catch (IOException e) {
System.err.println(e.getMessage());
} catch (Throwable t) {
t.printStackTrace();
}
System.exit(1);
}
Everything is working fine except in the case when I provide an invalid client_id. (I've the same issue if I use a json file and alter its content).
I get this get of error message from Google server:
401. That’s an error.
Error: invalid_client
The OAuth client was not found.
Request Details
client_id=573900000-hsoobsdsstem84tg8br4pmdsds.apps.googleusercontent.com
redirect_uri=http://localhost:40441/Callback
response_type=code
scope=https://www.googleapis.com/auth/drive
... and the callback server never receives any feedback. So, the application
is still running endlessly.
I've looked at the LocalServerReceiver class but could find any way to provide a
timeout or any potential solution.
What's the cleanest way to handle this case ?
I started playing with tweetinvi to connect to twitter api. I keep getting "The remote server returned an error: (401) Unauthorized." error message when I call CredentialsCreator.GetCredentialsFromVerifierCode() after being redirected.
I added my phone to my account.
I made use the Consumer Key and Consumer Secret are the same.
I made use that my time is current
The callback url in app settings is http://127.zero.zero.1:53260/
I'm kinda of lost on what to do next.
This is the only code that I use:
protected void Page_Load( object sender, EventArgs e )
{
Tweetinvi.WebLogic.TemporaryCredentials applicationCredentials = (Tweetinvi.WebLogic.TemporaryCredentials)CredentialsCreator.GenerateApplicationCredentials( Properties.Settings.Default.TwitterConsumerKey, Properties.Settings.Default.TwitterConsumerSecret );
if (Request["oauth_token"] == null)
{
string url = CredentialsCreator.GetAuthorizationURLForCallback( applicationCredentials, "http://127.0.0.1:53260/twitter.aspx" );
Response.Redirect( url, false );
}
else
{
string verifierCode = Request["oauth_verifier"];
// error calling this code
var newCredentials = CredentialsCreator.GetCredentialsFromVerifierCode( verifierCode, applicationCredentials );
Console.WriteLine( "Access Token = {0}", newCredentials.AccessToken );
Console.WriteLine( "Access Token Secret = {0}", newCredentials.AccessTokenSecret );
}
}
Looks like you have everything correct. It might be the library you are using. Have you tried LinqToTwitter?
They have several examples you can test at http://linqtotwitter.codeplex.com/SourceControl/latest#ReadMe.txt. Just download their source files and you'll find Linq2TwitterDemos_WebForms project that you can text out.
i am getting the same issue as described in this post
. we have used almost exactly the same code. i have tried both with Client ID and Email address of the google service account in below mehotd
setServiceAccountId(GOOGLE_SERVICE_CLIENT_EMAIL) OR
setServiceAccountId(GOOGLE_CLIENT_ID)
error changes with the change in a/c id. if i use client id, error is
400 Bad Request { "error" : "invalid_grant" }
and if i use service email id, error is
401 Unauthorized {
"code" : 401, "errors" : [ {
"domain" : "androidpublisher",
"message" : "This developer account does not own the application.",
"reason" : "developerDoesNotOwnApplication" } ], "message" : "This developer account does not own the application." }
any idea?
There appears to be some evidence that Google Play API does not currently work with Service Accounts (madness). There is another thread on the issue here. You can read about the Google Service Accounts here. You can read about authentication for Android Google Play API here.
Once you have done the dance on the Google API Console to get a refresh_token you can get an access token like this:
private String getAccessToken()
{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
try
{
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
nameValuePairs.add(new BasicNameValuePair("grant_type", "refresh_token"));
nameValuePairs.add(new BasicNameValuePair("client_id", "YOUR_CLIENT_ID);
nameValuePairs.add(new BasicNameValuePair("client_secret", "YOUR_CLIENT_SECRET"));
nameValuePairs.add(new BasicNameValuePair("refresh_token", "YOUR_REFRESH_TOKEN"));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer buffer = new StringBuffer();
for (String line = reader.readLine(); line != null; line = reader.readLine())
{
buffer.append(line);
}
JSONObject json = new JSONObject(buffer.toString());
return json.getString("access_token");
}
catch (IOException e) { e.printStackTrace(); }
catch (JSONException e) { e.printStackTrace(); }
return null;
}