I am getting below error, I wanted to get all the comments posted on a youtube video.
So basically I am passing video id and I wanted to get all the comments associated with that video
Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]]
Here is my code:
protected void btnGetVideoDesc_Click(object sender, EventArgs e)
{
string videoId = txtVideoID.Text;
YoutubeVideo video = new YoutubeVideo(videoId);
lblTitle.Text = video.title;
lblPublishedDate.Text = video.publishdate.ToShortDateString();
}
public class YoutubeVideo
{
public string id, title, description ;
public DateTime publishdate;
public YoutubeVideo(string id)
{
this.id = id;
YoutubeAPI.GetVideoInfo(this);
}
}
public class YoutubeAPI
{
private static YouTubeService ytService = Auth();
private static YouTubeService Auth()
{
UserCredential creds;
var service = new YouTubeService();
try
{
using (var stream = new FileStream(#"C:\v-mmarat\Project\EMEA_Development\YoutubeWebCrawling\YoutubeWebCrawling\youtube_client_secret.json", FileMode.Open, FileAccess.Read))
{
creds = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly }, "user", CancellationToken.None,
new FileDataStore("YoutubeAPI")
).Result;
}
service = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = creds,
ApplicationName = "YoutubeAPI",
ApiKey = "My_API_Key"
});
}
catch (Exception e)
{ }
return service;
}
public static void GetVideoInfo(YoutubeVideo video)
{
try
{
//This code work perfectly
var videoRequest = ytService.Videos.List("snippet");
videoRequest.Id = video.id;
var response = videoRequest.Execute();
if (response.Items.Count > 0)
{
video.title = response.Items[0].Snippet.Title;
video.description = response.Items[0].Snippet.Description;
video.publishdate = response.Items[0].Snippet.PublishedAt.Value;
}
else
{
//error
}
var CommentRequest = ytService.Comments.List("snippet");
videoRequest.Id = video.id;
//Getting error at this line after CommentRequest.Execute();
var Commentresponse = CommentRequest.Execute();
if (Commentresponse.Items.Count > 0)
{
video.title = Commentresponse.Items[0].Snippet.ChannelId;
video.description = Commentresponse.Items[0].Snippet.TextDisplay;
video.publishdate = Commentresponse.Items[0].Snippet.PublishedAt.Value;
}
else
{
//error
}
}
catch (Exception e)
{ }
}
In GoogleWebAuthorizationBroker.AuthorizeAsync change "user" to "admin".
This is a really late response but I had a similar issue. My project was using the YouTube API to upload videos to an account and in a separate section of the code was using it again to search for a video by ID to check its status.
My issue was I was using the same OAuth credentials to upload and then also for searching for a video.
This was failing because I had already set the YouTube Scope when uploading which was not the correct scope for searching for a video.
My simple solution for this was to create another set of OAuth credentials (Of type "Other") via the Google Developer Console, download the json file and use these details to obtain a different access token for the searching part of my code.
Hopefully this helps someone out.
I know this answer is a little late, but this is what fixed it for me. Hopefully it helps someone else.
I was receiving the same permissions denied error. After adding the YouTubeService.Scope.YoutubeForceSsl item to the scopes list, I was able to pull the comments.
Also, it looks like your code is not quite right. You will want to pull the CommentThreads based on the VideoId and include the replies (if you want them).
You won't be able to pull the comments for a video using Comments.
var threadsRequest = Client.CommentThreads.List("snippet,replies");
threadsRequest.VideoId = videoId;
var response = threadsRequest.Execute();
Same here.
Got it solved by specifying a the "dataStore" parameter in "GoogleWebAuthorizationBroker.AuthorizeAsync".
The service than wrote an acces-token JSON file into that folder.
Seems the storage option was required for authorization.
Also when authorization succeded, the browser jumped to an Google-authorisation URL where I had to logon and allow the levels of access I had requested from the API.
I used the API all the time before, but only for readonly ops.
Seems the "action" stuff (insert, update, delete, upload) requires more grants.
Related
My need is very specific. I need to access a directory on Google Drive that is a shared folder. The only thing in it will be empty form documents and spreadsheet templates. There is nothing of value in the folder, and it is used internally only. So, I can be very optimistic WRT security concerns. I just need access.
I am extending an existing ERP system that runs as an IIS application.
My customization is .NET/C# project that extends the ERP's .NET classes. I cannot implement a login/auth system because one already exists for the ERP.
I did the .NET quickstart, but of course that is a console app, and will not work when I move it to IIS. The suggestion to follow the standard MVC model doesn't work for me -- adding a second web site/page is needlessly complicated for my needs.
My question is: How can I authorize access to a Google Drive that
A) Runs within IIS
B) Does not require a separate ASP Web Application to implement MVC for authorization.
=============================
Similar to issues in:
Google API Fails after hosting it to IIS
you could use OAuth authorization with your asp.net application:
Create Web Server client_secret.json.by using GetAuthorizationUrl() create url for get toke temporary token.Redirect to GoogleCallback() and get refresh and access tokens using ExchangeAuthorizationCode().Save them to the file "~/Resources/driveApiCredentials/drive-credentials.json/Google.Apis.Auth.OAuth2.Responses.TokenResponse-{account}".Use this saved tokens.
you could refer to the below link for more detail:
https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-aspnet-mvc
Google Drive API upload Fails after hosting it to IIS. showing the error as Failed to launch the Browser with
Google Drive API not uploading file from IIS
Google Data API Authorization Redirect URI Mismatch
Jalpa's answer was not what I was looking for, nor was anything referenced in any of the links.
I'm going to put my answer here, because it is what I needed, and it might be useful to others.
First the overview
Google's QuickStart for .NET only shows the console based solution. As many have discovered, this does not work when you switch to an IIS based solution. It is almost as if the API deliberately defeats your attempts to do so. It simply will not allow you to use a token created for a local application using GoogleWebAuthorizationBroker.AuthorizeAsync -- it will error even if a browser isn't needed. (ie the token hasn't expired, so it won't need the browser to authenticate anything.)
Trying to run a refresh authorization gives you a token, but not a service. And even if the token is valid, you still can't use AuthorizeAsync to get your service from an IIS application (see above)
This is how I handle this:
Do the quick start and run the authorization that pops up the local browser and allows you to login and authenticate.
It creates a local folder(token.json), where it puts a token file (Google.Apis.Auth.OAuth2.Responses.TokenResponse-user) It's just a json file. Open it in notepad++ and you will find the fields:
"access_token": "token_type": "expires_in": "refresh_token":
"scope": "Issued": "IssuedUtc":
You need the refresh_token. I simply combined that with the initial credentials file I downloaded from the Google API Console (i.e. "credentials.json") and named it "skeleton_key.json"
This file is all you will need to generate valid tokens forever.
I have 2 classes I use for this. First the class that creates the Drive Service:
public class GDriveClass
{
public String LastErrorMessage { get; set; }
static string[] Scopes = { DriveService.Scope.Drive }; // could pull this from skeleton_key
static string ApplicationName = "GDrive Access"; // this is functionally irrelevant
internal UserCredential usrCredentials;
internal Google.Apis.Drive.v3.DriveService CurrentGDriveService = null;
internal String basePath = "."; // this comes in from calling program
// which uses HttpContext.Current.Server.MapPath("~");
public GDriveClass(string logFileBasePath)
{
basePath = logFileBasePath;
LastErrorMessage = "";
}
#region Google Drive Authenticate Code
public bool AuthenticateUser(string FullTokenAccessFileName)
{
UserCredential credential;
String JsonCredentialsonFile = System.IO.File.ReadAllText(FullTokenAccessFileName);
string credPath = basePath + #"\Credentials\token.json";
// Force a Refresh of the Token
RefreshTokenClass RTC = new RefreshTokenClass();
// Set field values in RefreshTokenClass:
var jObject = Newtonsoft.Json.Linq.JObject.Parse(JsonCredentialsonFile);
var fieldStrings = jObject.GetValue("installed").ToString();
var fields = Newtonsoft.Json.Linq.JObject.Parse(fieldStrings);
RTC.client_id = fields.GetValue("client_id").ToString();
RTC.client_secret = fields.GetValue("client_secret").ToString();
RTC.refresh_token = fields.GetValue("refresh_token").ToString();
RTC.ExecuteRefresh(); // this gets us a valid token every time
try
{
GoogleCredential gCredentials = GoogleCredential.FromAccessToken(RTC.access_token);
CurrentGDriveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = gCredentials,
ApplicationName = ApplicationName,
});
return true;
}
catch (Exception ex)
{
LastErrorMessage = "Error: Authenticating - " + ex.Message;
return false;
}
}
Usage is pretty straight forward:
string TokenFile = #basePath + #"\skeleton_key.json";
GDRIVER.AuthenticateUser(TokenFile);
var rslt = GDRIVER.LastErrorMessage;
if (!String.IsNullOrEmpty(rslt))
{
WriteToLogFile("ERROR in Google AuthenticateUser() ");
AlertMessage("Unable To Connect to Google Drive - Authorization Failed");
return;
}
And this is the class that refreshes the token via REST API as needed:
public class RefreshTokenClass
{
public string application_name { get; set; }
public string token_source { get; set; }
public string client_id { get; set; }
public string client_secret { get; set; }
public string scope { get; set; }
public string access_token { get; set; }
public string refresh_token { get; set; }
public RefreshTokenClass()
{
}
public bool ExecuteRefresh()
{
try
{
RestClient restClient = new RestClient();
RestRequest request = new RestRequest();
request.AddQueryParameter("client_id", this.client_id);
request.AddQueryParameter("client_secret", this.client_secret);
request.AddQueryParameter("grant_type", "refresh_token");
request.AddQueryParameter("refresh_token", this.refresh_token);
restClient.BaseUrl = new System.Uri("https://oauth2.googleapis.com/token");
var restResponse = restClient.Post(request);
// Extracting output data from received response
string response = restResponse.Content.ToLower(); // make sure case isn't an issue
// Parsing JSON content into element-node JObject
var jObject = Newtonsoft.Json.Linq.JObject.Parse(restResponse.Content);
//Extracting Node element using Getvalue method
string _access_token = jObject.GetValue("access_token").ToString();
this.access_token = _access_token;
return true;
}
catch (Exception ex)
{
//Console.WriteLine("Error on Token Refresh" + ex.Message);
return false;
}
}
Note: This makes use of Newtonsoft.Json and RestSharp.
Thanks to user: "OL." who gave me the way of creating a service from a token (that somehow I missed in the docs!)
How to create Service from Access Token
And to user:"purshotam sah" for a clean REST API approach
Generate Access Token Using Refresh Token
I wonder if anyone has got any further than me using the new Search Analytics functions of the Google Webmaster Tools API via .Net?
I am using the Google.Apis.Webmasters.v3 Nuget package and have got as far as authenticating and connecting (using a Service Account)
However I'm struggling to get anywhere with Search Analytics.
I couldn't find any code samples online so have been guided by the Class info at https://developers.google.com/resources/api-libraries/documentation/webmasters/v3/csharp/latest/annotated.html and a lot of guesswork.
Here is the code I am using:
SearchanalyticsResource mySearchanalyticsResource = new SearchanalyticsResource(service);
SearchAnalyticsQueryRequest myRequest = new SearchAnalyticsQueryRequest();
myRequest.StartDate = "2015-08-01";
myRequest.EndDate = "2015-08-31";
myRequest.RowLimit = 10;
SearchanalyticsResource.QueryRequest myQueryRequest = mySearchanalyticsResource.Query(myRequest, site.SiteUrl);
SearchAnalyticsQueryResponse myQueryResponse = myQueryRequest.Execute();
It runs OK until the Execute method when I get "An Error occurred, but the error response could not be deserialized". Exception detail below...
Newtonsoft.Json.JsonReaderException {"Error parsing NaN value. Path '', line 0, position 0."}
Any help or code samples would be very gratefully received!
This compiles but its not returning any data for me.
Auth:
public static WebmastersService WMAuthenticateOauth(string clientId, string clientSecret, string userName)
{
string[] scopes = new string[] { WebmastersService.Scope.Webmasters }; // View analytics data
try
{
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore(".", true)).Result;
WebmastersService service = new WebmastersService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "WebMasters API Sample",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
Request
var service = Authentcation.WMAuthenticateOauth(clientid, secret, "testmmm");
IList<string> newlist = new List<string> ();
newlist.Add("country");
newlist.Add("device");
SearchAnalyticsQueryRequest body = new SearchAnalyticsQueryRequest();
body.StartDate = "2015-04-01";
body.EndDate = "2015-05-01";
body.Dimensions = newlist;
var result = service.Searchanalytics.Query(body, "http://www.daimto.com/").Execute();
I have also tried testing using the try me at the bottom of this page. It doesn't return anything either.
Strange API this.
Update:
I finally got data back I set the dates to
body.StartDate = "2015-09-01";
body.EndDate = "2015-09-15";
I wonder if this thing has limited data, it only goes back so far.
I've been trying to list users in my google apps domain for a while now.
No problem in Python, but in C# i get an error message:
An error has occured:
Google.Apis.Requests.RequestError
Bad Request [400]
Errors [
Message[Bad Request] Location[ - ] Reason[badRequest] Domain[global]
]
I'm no C# guru of any sorts but when I looked through the Google.Apis.Admin.directory_v1.cs - file it looked to me as if the UserResource ListRequest is wrong???
It's found on line 7349-7352 in the file. Anyone know's if it's not yet implemented in the API?
Edit:
I start with why I THINK the code in Google.Apis.Admin.directory_v1.cs, lines 7349-7352 is wrong(as I mentioned - I'm not a C#-guru):
The Code:
/// <summary>Retrieve either deleted users or all users in a domain (paginated)</summary>
public virtual ListRequest List() {
return new ListRequest(service);
}
Why I find it odd:
I can' see where to pass the customerid or domain as an argumant to this request, but in the APIs Explorer it's needed (otherwise I get the same error message as above, in my original post).
Edit : I looked a bit further down in the file and I guess that line 8904 and onwards is doing what I looked for earlier. My Bad!
But still I can't get my code to work?!?!?
And my code that won't work:
static void Main(string[] args)
{
// Display the header and initialize the sample.
CommandLine.EnableExceptionHandling();
Console.WriteLine("List users in a google apps domain!");
Console.WriteLine("by Jonas Bergstedt 2013");
// Get the domainname
Console.Write("Domain: ");
string domain = Console.ReadLine();
// Register the authenticator.
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = <myClientId>,
ClientSecret = <myClientSecret>",
};
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthorization);
// Create the service.
var service = new DirectoryService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = "List Users",
ApiKey = <myApiKey>
});
// Trying to add the domain
service.Users.List().Domain = domain;
Users results = service.Users.List().Execute();
foreach (User list in results.UsersValue)
{
Console.WriteLine("- " + list.Name);
}
}
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { DirectoryService.Scopes.AdminDirectoryUser.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.WriteLine();
Console.Write("Authorization Code: ");
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
ListRequest had those properties. It looks like those properties aren't mandatory, so they aren't part of the constructor.
You can do the following:
var listReq = service.Users.List();
listReq.Customer = "CUSTOMER_HERE";
listReq.Domain = "DOMAIN_HERE";
Users results = listReq.Execute();
I've spent some time over the last few days trying to implement a feature for my web application. The feature should add new events to a users google calendar while they are offline. I read the Google OAuth2 documentation for web server applications and seem to understand the basics of it. I created a link to authorize the application for offline access:
<a target='_blank' href='https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Ftasks&response_type=code&client_id=<MY CLIENT ID>&access_type=offline&redirect_uri=http%3A%2F%2Flocalhost:49949%2Foauth2callback.aspx'>Grant Tasks Permission</a>
If the user accepts then I capture the refresh token at the redirect uri like this:
private static OAuth2Authenticator<WebServerClient> _authenticator;
protected void Page_Load(object sender, EventArgs e)
{
if (HttpContext.Current.Request["code"] != null)
{
_authenticator = CreateAuthenticator();
_authenticator.LoadAccessToken();
}
Response.Write("Refresh Token: " + _authenticator.State.RefreshToken);
}
private OAuth2Authenticator<WebServerClient> CreateAuthenticator()
{
var provider = new WebServerClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "<MY CLIENT ID>";
provider.ClientSecret = "<MY CLIENT SECRET>";
return new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization);
}
private IAuthorizationState GetAuthorization(WebServerClient client)
{
return client.ProcessUserAuthorization(new HttpRequestInfo(HttpContext.Current.Request));
}
For testing purposes I have been copying the refresh token to a text file for further use.
My problem is using this refresh token for offine access. I have been using this code to refresh the token:
protected void btnGetTasks_Click(object sender, EventArgs e)
{
if (_service == null)
{
_authenticator = CreateAuthenticator();
_service = new TasksService(new BaseClientService.Initializer() { Authenticator = _authenticator });
}
var cl = _service.Tasklists.List().Fetch();
}
private OAuth2Authenticator<WebServerClient> CreateAuthenticator()
{
// Register the authenticator.
var provider = new WebServerClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "<MY CLIENT ID>";
provider.ClientSecret = "<MY CLIENT SECRET>";
var authenticator = new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization);
return authenticator;
}
private IAuthorizationState GetAuthorization(WebServerClient client)
{
string scope = "https://www.googleapis.com/auth/tasks";
IAuthorizationState state = new AuthorizationState(new[] { scope });
state.RefreshToken = "<REFRESH TOKEN FROM FIRST STEP>";
var result = client.RefreshToken(state);
return client.ProcessUserAuthorization();
}
Everything seems fine at this point. When I step through the code I can see the result from client.RefreshToken(state) is true. The issue is when I call this line of code:
_service.Tasklists.List().Fetch();
It returns a (401) unauthorized error from google. I'm looking into the cause but I am not sure how to proceed and I am running short on time with this feature. Any advice would be greatly appreciated. Thanks!
Seems just the act of putting code on here always helps me figure it out a little sooner :)
It now appears this line is unnecessary:
return client.ProcessUserAuthorization();
removing that from the GetAuthorization method and just returning the state passed to RefreshToken has resolved the unauthorized error. I'll leave the question in case it stumps anyone else.
Coding Platform ASP.NET 4.0 WebForms
I have two pages that are relevant here
Login.aspx
LandingPage.aspx
On Login.aspx when I click an ImageButton, I redirect to Facebook site with the following code
protected void FacebookLoginButton_Click(object sender, ImageClickEventArgs e)
{
try
{
Response.Redirect(GetFacebookLoginURL());
}
catch (System.Threading.ThreadAbortException)
{
throw;
}
catch (Exception err)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(err);
}
}
private string GetFacebookLoginURL()
{
try
{
string baseURL = System.Configuration
.ConfigurationManager
.AppSettings["WebsiteURL"]
.ToString();
string[] extendedPermissions = new[] {
"publish_stream",
"offline_access"
};
var oauth = new FacebookOAuthClient {
ClientId = FacebookContext.Current.AppId
};
var parameters = new Dictionary<string, object>{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
parameters["redirect_uri"] = String.Format("{0}LandingPage.aspx", baseURL);
return oauth.GetLoginUrl(parameters).OriginalString;
}
catch (Exception err)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(err);
return "";
}
}
That part is working properly. But I am clueless on how to access the user info at the LandingPage which is my redirect_uri. Have tried this.
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.AbsoluteUri;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
But I doubt it wont work since I dont have window.hash.location at Server Side(Its not working anyway)
var client = new FacebookClient(FacebookContext.Current);
dynamic me = client.Get("me");
string firstName = me.first_name;
string lastName = me.last_name;
string email = me.email;
Although I am not sure, getting the Access Token will solve my problem, won't it?
This is the error I am getting.
(OAuthException) An active access
token must be used to query
information about the current user.
What am I doing wrong?
here's a sample of a standalone website using webforms. Check out Default.aspx.cs and Web.config. Please note that this sample could be modified for use with the latest source code and might not work with the latest release (5.0.3 beta).
I just recently did this implementation. To access "me", you need to have an Access Token.
The implementation is pretty straighforward, however, I will say that I found the major stumbling block for me was to make sure that my redirect_uri from my "Login.aspx" url matched the redirect_uri from my landing page.
It must be simple, but I dodnt get it. So I used the lesser used Facebook C# SDK by Facebook. I pretty much did what this blog suggested. Everything is fine now.