How to delete a video from youtube using youtube v3 api and C# - c#-4.0

Well I am able to upload video on Youtube but i didn't find a way or relevant code to delete video/videos from Youtube.
Here is my code which i tried to delete the youtube video.
private async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.Youtube },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var videosDeleteRequest = youtubeService.Videos.Delete("Video ID");
await videosDeleteRequest.ExecuteAsync();
}
But getting 403 response
Error: Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermis
sions] Domain[global]
]
A little help or any possible solution will be highly appreciable.

The error translates to:
The video that you are trying to delete cannot be deleted. The request
might not be properly authorized.
https://developers.google.com/youtube/v3/docs/videos/delete
Have you successfully acquired the token of the user that owns the video?

The videos.delete method is preformed on private user data. In order to delete the data you must have permission or consent of the user to access their account. They must have granted you permission in one of the following scopes.
The error message
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermis
sions] Domain[global]
Means that the user did not grant you permission with a hig enough scope. If for example you asked for authorization with only a read only scope you would then not have enough permissions to delete a video.
However if we check your code we can see that you are in fact using YouTubeService.Scope.Youtube. However if you have previously run your application then the client library stored the consent of the user. If you then have changed the scope and not forced the user to consent to authorization again. Then you are still running on the old consent.
The solution in this case is to change "user" to something else which will force it to request authorization again.

Related

Microsoft Graph returning null account even after passing a valid account ID

I am encountering a weird issue with Microsoft Graph on an integration that was built a few years back.
This issue started happening a few months back. After I sync a Microsoft Account and provide email and calendar read/write access, everything works fine for some time. I am able to retrieve emails and calendar events. However, after some time, I notice that when a call is made to GetAccountAsync with a valid AccountID, null is returned. This is causing AcquireTokenSilent to fail with the following error:
Error Code: user_null
Error Message: No account or login hint was passed to the AcquireTokenSilent call.
I have also noticed that this happens under the following scenarios:
When the WebJob (console app) is run every 15 minutes, I encounter this issue
To narrow down the root cause, I have deleted the WebJob to see if the issue occurs on the web app. It looks like the issue starts to occur after an hour or so even without the web job running.
I have upgraded to the latest version of MSAL and implemented 4.46.1.0 version of Microsoft.Identity.Client. I am using .NET Framework 4.8 and this is a .NET MVC 5 app.
Here's my code:
public async Task<string> GetAccessTokenAsync()
{
string accessToken;
UserExternalApp.Scope = string.IsNullOrWhiteSpace(UserExternalApp.Scope) ? "" : UserExternalApp.Scope;
// Load the app config from web.config
var microsoftScopes = UserExternalApp.Scope.Replace(' ', ',').SplitAndTrim(new char[] { ',' }).ToList();
var accountID = UserExternalApp.ExternalUserAccountID;
var app = ConfidentialClientApplicationBuilder.Create(ClientID)
.WithRedirectUri(DefaultRedirectUrl) // https:\//mywebsite.com
.WithClientSecret(Secret)
.Build();
app.AddDistributedTokenCache(services =>
{
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
try
{
var account = await app.GetAccountAsync(accountID);
var query = app.AcquireTokenSilent(microsoftScopes, account); // This is where the error is thrown
var acquireTokenSilent = await query.ExecuteAsync();
accessToken = acquireTokenSilent.AccessToken;
}
catch
{
// This is the error thrown:
// Exception Type: MsalUiRequiredException
// Error code: user_null
// Exception Details: No account or login hint was passed to the AcquireTokenSilent call.
throw;
}
return accessToken;
}
I know the token is persisted on my SQL Server:
I think the MSAL uses an in memory token cache by default, Once the client logins, authentication information will be stored in cookie(if cookie has not been disabled). Even your web application restarts, the client will keep logged in.
To solve this, you can use custom Token cache serialization in MSAL.NET:https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-net-token-cache-serialization?tabs=aspnet.
Hope this helps.
I would like to share the resolution to this problem in case if it helps someone in the future. I feel that this is a Microsoft Bug that was introduced during one of their many upgrade process as this code went from working to broken without any change from our end. Here are the steps I took:
While exchanging the code for a token after user authentication, I retrieved and saved Account.HomeAccountId.Identifier, Account.HomeAccountId.ObjectId and TenantId for the account.
I implemented my own version of IAccount.
Instead of calling await app.GetAccountAsync(accountID), I used my implementation of IAccountand initialized it with the data I saved in Step 1.
I used this account to call app.AcquireTokenSilent(microsoftScopes, account).
And that's it! No error was thrown once this was done!

How to transfer ownership of file create with service account using Drive API v3

I'm using the googleapis package in npmjs to create a spreadsheet as follows:
const { google } = require("googleapis")
const sheets = google.sheets("v4")
const drive = google.drive("v3")
async function run() {
// ....
// auth obtained here
// ....
var resp = await sheets.spreadsheets.create({
auth,
resource: {
properties: {
title:"SSTEST2",
}
}
})
var folder = "1XXXXXXXXXXXXXXXXXXXXX" // actual folder id omitted
var ssid = resp.data.spreadsheetId
resp = await drive.files.update({
auth,
addParents:[folder],
removeParents:"root",
fileId:ssid
})
}
I'm using a service account I created, so as a result when the process is complete, the spreadsheet is owned by googlesheetsuser#some-random-words.iam.gserviceaccount.com. This doesn't seem to prevent me from editing the document, however if I delete it, presumably it is still sitting off in limbo somewhere (if I try to access it before moving it to my folder I get a page telling me to request access, which doesn't make any sense because a service account address isn't a real e-mail and so there's no way for this to succeed!) so I'm not sure if it's even going into a trash can for the service account or if it will sit around on Google's servers forever. Not actually owning the document causes issues so I need to get ownership.
On the other hand, I haven't found any documentation that explains exactly what I need to do to transfer ownership to myself. Google's API seem to leave out a lot of information about exactly what to pass; leaving out important info in the sample source with comments like "TODO: Add desired properties to the request body" is beyond unhelpful! I have found a few examples in other languages using other mechanisms I don't recognize which purport to do at least something close to this, but I haven't been able to glean any useful information about how to do it in my particular setup. I have also found a couple of answers which seem to imply that doing this is impossible as you can't change ownership between domains, in which case I just have no words because obviously leaving it owned by my service account creates issues and leaving it owned by the service account indefinitely just isn't an option so some solution is needed.
How can I wind up with my file (Google Spreadsheet) owned by myself at the end of this process?
Update: Per the suggested by #Kessy, I tried to use this code:
const resp = await drive.permissions.update({
auth,
fileId: <fileId>,
permissionId: <id found using list call>,
transferOwnership: true,
requestBody: {
role: "owner",
emailAddress: <my email address>,
type: "user",
}
})
I get this error:
Error: The resource body includes fields which are not directly writable.
I don't know which field it thinks isn't writeable. I tried omitting "type" and get the same error. I tried omitting "role" and it complains that this field is required: Error: The permission role field is required. If I omit "emailAddress" then I don't get the error, but this defeats who whole purpose of the call, which is to transfer ownership to that e-mail address!

Google Search Console API: How do I Solve "User does not have sufficient permission for site"? (When User Has Permissions)

I'm trying to use Google's Search Console API via their Node package, and my code looks like the following:
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/webmasters.readonly',
});
const webmasters = google.webmasters('v3');
const params = {
auth,
siteUrl: 'example.com',
resource: {
startDate: '2015-08-25',
endDate: '2015-08-25',
dimensions: ['query', 'page'],
rowLimit: 10,
},
aggregationType: 'byPage',
};
const res = await webmasters.searchanalytics.query(params);
console.log(res.data);
... except that in my version example.com has been replaced with my actual domain.
I'm invoking it at the command line with:
GOOGLE_APPLICATION_CREDENTIALS="/path/to/service_key.json" node index.js
I have a service account created, and the service key JSON file is from it. That service account has access to my Search Console account. When I look at https://search.google.com/search-console/users I see the service user there, and in the Permission column it has "Full".
Can anyone help me understand why I'm getting the following error when I run my code?
{
message: "User does not have sufficient permission for site 'http://example.com'. See also: https://support.google.com/webmasters/answer/2451999.",
domain: 'global',
reason: 'forbidden'
}
The URL mentioned, https://support.google.com/webmasters/answer/2451999, simply links me to the search console users page ... which (again) says the service user has full permissions.
After rooting in google forums and rest of internet I have figured out why it happen.
Need to copy Service account email, long weird format(example: whiskey-tango-foxtrot#certain-something-0123456.iam.gserviceaccount.com) from Google Cloud: https://console.cloud.google.com/apis/credentials to Search Console https://search.google.com/u/1/search-console/users, as a "Full site" user.
Make sure you have added that site(as from on Search Console) via your API or with this tool: https://developers.google.com/webmaster-tools/v1/sites/add
Then, when you perform "list_sites" request, your site should be listed ere and permission_level is "siteFullUser"(according to step 1)
When you add_site or perform query API requests, make sure to set siteUrl according to steps above, eg: http://www.example.com/ (for a URL-prefix property) or sc-domain:example.com (for a Domain property)
Happy coding

Facebook graph API - i recieve the error "Missing authorization code"?

i am new to the facebook api and i came across a weird issue and i cannot really find a solution for it. i am trying to get an access token using the following instructions :
but when i try to do :
curl -X GET "https://graph.facebook.com/oauth/access_token?client_id=[ID]&client_secret=[SECRET]&redirect_uri=http://localhost&grant-type=clients_credentials"
it fails even when i do it in my code , it also fails:
var firstOptions = {
method: 'GET',
url: 'https://graph.facebook.com/oauth/access_token?client_id=[ID]&client_secret=[SECRET]&grant-type=client_credentials&redirect_uri=http://localhost',
json: true,
};
request(firstOptions, function (error, response, body) {
console.log(body);
});
so i was wondering if someone could tell me where and how i get the authorization code ? or if i am doing something wrong. because the facebook image isn't including any authorization code..
EDIT:
after a suggestion i tried the following :
var pageOptions={
method: 'GET',
url: 'https://graph.facebook.com/[PAGE-ID]/posts?access_token=' + 'ID|SECRET',
json:true
};
but then i got the follow error:
{ message: '(#10) To use \'Page Public Content Access\', your use of this endpoint must be reviewed and approved by Facebook. To submit this \'Page Public Content Access\' feature for review please read our documentation on reviewable features: https://developers.facebook.com/docs/apps/review.',
this error does not occure when i use an access_token generated by the Access Token Debugger:
https://developers.facebook.com/tools/debug/accesstoken/
{ message: '(#10) To use \'Page Public Content Access\', your use of this endpoint must be reviewed and approved by Facebook. To submit this \'Page Public Content Access\' feature for review please read our documentation on reviewable features: https://developers.facebook.com/docs/apps/review.',
this error does not occure when i use an access_token generated by the Access Token Debugger
You are simply using the wrong kind of access token here.
To access content of just any arbitrary public page, your app would need to be reviewed by Facebook first.
It works with the token you generated in the debug tool, because that is a user token and you have an admin role on the page in question - which means this is not general access to just “public” data any more, but to content you actually have admin access to. With an app access token, the API has no way of checking for that.
You need to use a page admin user token, or a page token for this kind of request.

Facebook iOS SDK won't open login UI after user changes password (iOS 6)

Some context: the user had previously installed the app, authorized FB, everything worked great, then they changed their FB password (through facebook.com), deleted the app, and have now reinstalled it and are running it for the first time again after reinstall.
I am calling [FBSession openActiveSessionWithReadPermissions:allowLoginUI:completionHandler] with allowLoginUI: YES and the read permissions being "email, user_about_me, user_birthday, user_interests, user_location."
The FBSessionState I am getting in the completionHandler is FBSessionStateClosedLoginFailed. The NSLog of the error is this:
Error Domain=com.facebook.sdk Code=2 "The operation couldn’t be completed. (com.facebook.sdk error 2.)" UserInfo=0x1cd68c00 {com.facebook.sdk:ErrorLoginFailedReason=com.facebook.sdk:ErrorLoginFailedReason, com.facebook.sdk:ErrorInnerErrorKey=Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: Error validating access token: The session has been invalidated because the user has changed the password." UserInfo=0x1cd5b970 {NSLocalizedDescription=The Facebook server could not fulfill this access request: Error validating access token: The session has been invalidated because the user has changed the password.}}
That internal error domain is ACErrorDomain and error code ACErrorPermissionDenied. So, how do I let the user re-authorize the app?
I have tried calling openActiveSessionWithReadPermissions again but that just keeps outputting the same error. I have also tried [FBSession.activeSession closeAndClearTokenInformation] but that doesn't seem to do anything (presumably because there is no activeSession).
Hitting a very similar sort of bug with 3.2.1 Facebook SDK. In my case, I get into FBSessionStateOpen but have been given an invalid access token. As the question states, the normal closeAndClearTokenInformation and even deleting the app doesn't fix it. The only way I have been able to get-back-in under this scenario is to have the user change their password in the setting app. So this is what I do.
// In my completion handler FBSessionStateOpen is called BUT an
// invalid accessToken was detected.
[session closeAndClearTokenInformation];
[FBSession renewSystemCredentials:^(ACAccountCredentialRenewResult result,
NSError *error)
{
if (result == ACAccountCredentialRenewResultFailed ||
result == ACAccountCredentialRenewResultRejected)
{
[self showErrorMessage:NSLocalizedString(#"You may need to re-enter your Facebook password in the iPhone Settings App.\n", nil)];
}
else
{
// attempt opening a session again (after they have updated their account
// settings I end up here)
[self facebookLogin]; // Performs openActiveSessionWithReadPermissions,
// but this time around the token issued should be good.
}
}];
This is the only pragmatic solution I have been able to come up with.
I think you need to get a new access token with code like this...
[FBSession.activeSession closeAndClearTokenInformation];
[[FBSession class] performSelector:#selector(renewSystemAuthorization)];
[FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
}

Resources