Youtube v3 api 401 Unauthorized - c#

I am trying to upload a file using v3 of the youtube api and without using the c# library. I really can't use the library because I am making my own library that allows me to use a few common apis (youtube, vimeo, facebook, etc)
I have already got my access token and refresh token which is fine. Now I need to upload a file using the youtube resumable uploads defined here:
https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol
but for some reason my code is coming back with a 401 Unauthorized error but I can't see why.
Here is my code that is creating the request:
private void CreateUploadRequest(SynchronisedAsset asset)
{
var endPoint = api.ApiUrl + "/videos?uploadType=resumable&part=snippet&key=" + api.Tokens.ConsumerKey; // read for the different ways to interact with videos https://developers.google.com/youtube/v3/docs/#Videos
var maxSize = 68719476736; // 64 gig
try
{
var location = CompanyProvider.GetUploadLocation(this.baseUploadDirectory, companyId, FileType.Asset);
var filePath = System.IO.Path.Combine(location, asset.FileName);
using (var data = new FileStream(filePath, FileMode.Open))
{
if (maxSize > data.Length && (asset.MimeType.ToLower().StartsWith("video/") || asset.MimeType.ToLower().Equals("application/octet-stream")))
{
var json = "{ \"snippet\": { \"title\": \"" + asset.FileName + "\", \"description\": \"This is a description of my video\" } }";
var buffer = Encoding.ASCII.GetBytes(json);
var request = WebRequest.Create(endPoint);
request.Headers[HttpRequestHeader.Authorization] = string.Format("Bearer {0}", api.Tokens.AccessToken);
request.Headers["X-Upload-Content-Length"] = data.Length.ToString();
request.Headers["X-Upload-Content-Type"] = asset.MimeType;
request.ContentType = "application/json; charset=UTF-8";
request.ContentLength = buffer.Length;
request.Method = "POST";
using (var stream = request.GetRequestStream())
{
stream.Write(buffer, 0, (int)buffer.Length);
}
var response = request.GetResponse();
}
}
}
catch (Exception ex)
{
eventLog.WriteEntry("Error uploading to youtube.\nEndpoint: " + endPoint + "\n" + ex.ToString(), EventLogEntryType.Error);
}
}
api.ApiUrl is https://www.googleapis.com/youtube/v3. I am not sure if the key is needed, it doesn't show it in the documentation but I added it to see if I could solve my Unauthorized issue.
Also, I figured that without the key, how would it know what account to upload to?
Can anyone see what is wrong with my code?
Any help would be greatly appreciated.
Update 1
After a bit of time sorting stuff out, I have now added some code that checks the youtube credentials before trying to do an upload. This is done with these bits of code:
public string GetAuthorizeApplicationUrl()
{
var sb = new StringBuilder();
var dictionary = new Dictionary<string, string>
{
{"client_id", Tokens.ConsumerKey},
{"redirect_uri", callbackUrl},
{"response_type", "code"},
{"scope", "https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtubepartner"},
{"approval_prompt", "force"},
{"access_type", "offline"},
{"state", ""}
};
sb.Append(requestTokenUrl);
foreach (var parameter in dictionary)
{
var query = (sb.ToString().Contains("?")) ? "&" : "?";
sb.Append(query + parameter.Key + "=" + parameter.Value);
}
return sb.ToString();
}
This bit of code is responsible for building the URL that allows us to ask the user for access.
With the code that is returned to our return URL we call this bit of code:
public void RequestAccessToken(string code)
{
var dictionary = new Dictionary<string, string>
{
{"code", code},
{"client_id", Tokens.ConsumerKey},
{"client_secret", Tokens.ConsumerSecret},
{"redirect_uri", callbackUrl},
{"grant_type", "authorization_code"}
};
var parameters = NormalizeParameters(dictionary);
var resultString = "";
using (var wc = new WebClient())
{
//wc.Headers[HttpRequestHeader.Host] = "POST";
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
resultString = wc.UploadString(requestAccessTokenUrl, parameters);
}
var json = JObject.Parse(resultString);
Tokens.AccessToken = json["access_token"].ToString();
Tokens.RefreshToken = (json["refresh_token"] != null) ? json["refresh_token"].ToString() : null;
Tokens.Save(companyId);
}
Now because we have declared our app as offline, when we do any api calls we can just use this bit of code:
public bool CheckAccessToken()
{
try
{
RefreshToken(); // Get and store our new tokens
return true;
}
catch
{
return false;
}
}
private void RefreshToken()
{
var dictionary = new Dictionary<string, string>
{
{"refresh_token", Tokens.RefreshToken},
{"client_id", Tokens.ConsumerKey},
{"client_secret", Tokens.ConsumerSecret},
{"grant_type", "refresh_token"}
};
var parameters = NormalizeParameters(dictionary);
var resultString = "";
using (var wc = new WebClient())
{
//wc.Headers[HttpRequestHeader.Host] = "POST";
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
resultString = wc.UploadString(requestAccessTokenUrl, parameters);
}
var json = JObject.Parse(resultString);
Tokens.AccessToken = json["access_token"].ToString();
Tokens.Save(companyId);
}
In my windows service I have this code:
public void SynchroniseAssets(IEnumerable<SynchronisedAsset> assets)
{
if (api.CheckAccessToken())
{
foreach (var asset in assets)
{
var uploadAssetThread = new Thread(() => CreateUploadRequest(asset));
uploadAssetThread.Start(); // Upload our assets at the same time
}
}
}
which as you can see calls the Original code above.
The error which I am getting when I parse it into json is this:
{
"error":{
"errors":[
{
"domain":"youtube.header",
"reason":"youtubeSignupRequired",
"message":"Unauthorized",
"locationType":"header",
"location":"Authorization"
}
],
"code":401,
"message":"Unauthorized"
}
}
So, if anyone could help me work out what that means, that would be great.
Cheers,
/r3plica

The reason "youtubeSignupRequired" usually means the YouTube account has not been set up properly or you have not created your YouTube channel yet. This is a requirement before you can upload videos.
It would be great if Google could improve their error message for this case and make it a bit more verbose.

The issue returning 401 Unautorized was because the account I was gaining access to did not have a youtube account associated with it.

Related

C# httpwebrequest is only sending GET requests even when method is set to POST

I'm using Xamarin C# and php to create a simple android app that sends coordinate data to an apache sql database. When I use httpwebrequest to send a POST request to the server php, it sends a GET instead. Even though I set the request type to POST. The strange thing is that other requests that I have used using the exact same code work okay, this one specifically isn't working.
public class gpsGetterScript{
public static double longitude;
public static double latitude;
public static SimpleLocationManager locationManager = new SimpleLocationManager();
public static void startGetLocation()
{
Timer timer = new Timer();
timer.Interval = 6 * 1000;
void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
double lon = locationManager.LastLocation.Longitude;
double lat = locationManager.LastLocation.Latitude;
longitude = lon;
latitude = lat;
Console.WriteLine(lon.ToString());
Console.WriteLine(lat.ToString());
Dictionary<string, string> gpsData = new Dictionary<string, string>();
gpsData.Add("longitude", lon.ToString());
gpsData.Add("latitude", lat.ToString());
gpsData.Add("device_owner", "Test");
var url = "theURL";
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = "POST";
var contentType = "application/json";
//var contentType = "application/x-www-form-urlencoded";
httpRequest.ContentType = contentType;
var data = JsonConvert.SerializeObject(gpsData);
//var data = "longitude=" + lon.ToString() + "&latitude=" + lat.ToString() + "&device_owner=" + "Test";
using (var streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
{
Console.WriteLine(data.ToString());
Console.WriteLine(httpRequest.RequestUri.ToString());
Console.WriteLine(httpRequest.Headers);
Console.WriteLine(httpRequest.Method);
streamWriter.Write(data);
}
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
Console.WriteLine(result);
}
}
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
timer.Start();
locationManager.StartLocationUpdates(LocationAccuracy.Balanced, 5, TimeSpan.FromMinutes(.5), TimeSpan.FromSeconds(20));
SimpleLocationLogger.Enabled = true;
}
}
here is one of the httprequests from a related project that does work (same server being used)
public static string sendLoginRequest(string email, string password)
{
var url = "theURL";
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
var data = "email=" + email + "&password=" + password;
using (var streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
{
streamWriter.Write(data);
}
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
return result;
}
}
I know it is the app sending the wrong request, as when I made the php echo the request type it came back as GET. Strangely, when I use reqbin to make the POST request using the same URL, it works fine. It really is just for some reason the request in C# is not sending a POST. The other question about this was resolved by adding a www. to the api url, but that did not work for me.
UPDATE: 4/20/2022- Here is the php code
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
include_once '../../../Database.php';
include_once '../location_obj.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$database = new Database();
$db = $database->getConnection();
$data = json_decode(file_get_contents("php://input"));
$item = new location_obj($db);
$stmt = $item->InputCoordinates();
$item->longitude = $data->longitude;
$item->latitude = $data->latitude;
$item->device_owner = $data->device_owner;
if ($item->InputCoordinates()) {
echo json_encode(array("message" => "Successfully", "data" => $item, "Request Type" => $_SERVER['REQUEST_METHOD']));
}
else {
echo json_encode(array("error" => "failed! Check your data and try again.", "data" => $item, "Request Type" => $_SERVER['REQUEST_METHOD']));
}
}
else {
echo json_encode(array("error" => "Only the POST method is supported!", "Request Type" => $_SERVER['REQUEST_METHOD']));
}
?>`
I get the error {"error":"Only the POST method is supported!","Request Type":"GET"} but when I use reqbin I get "message": "Successfully", "data": { "longitude": "theLongitude", "latitude": "theLatitude", "device_owner": "Test" }, "Request Type": "POST"
I checked the url that is used using Console.WriteLine(url); , it is the right one.
I fixed it, seems I forgot to add a / at the end of the URL. It was https://example.com/api/v2/location/input instead of https://example.com/api/v2/location/input/

Trying to build a Twitter Thread programmatically

I've built a Twitter application that works in C#. It's a WPF or Windows-like application that makes a web request to the Twitter API endpoint. Now, I have a business case to create a Twitter thread when it's sending a series of 280-character tweets. Because of all the business rules we have, there's no way I can use an existing Twitter app on the market.
The issue is that I'm always getting a 401 unauthorized whenever I try to send a tweet with the in_reply_to_status_id_str. In fact it does this whenever I try to add ANY OPTIONAL PARAMETERS besides the required STATUS parameter. I'm using OAuth 1.0A.
I'm trying to create a series of Tweets that will display Show This Thread at the bottom and can be viewed together.
The method I've provided would be called in a loop for each 280-character segment.
NOTE: I NOW HAVE THIS WORKING. I REALIZED YOU HAVE TO ORDER THE PARAMETERS BEFORE SIGNING THEM
public async Task<string> TwitterWebRequest(string status, string resource_url, string authHeader, int counter)
{
string id = "";
using (var request = new HttpRequestMessage(HttpMethod.Post, resource_url))
{
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var data = new Dictionary<string, string>();
if (counter == 0)
{
data = new Dictionary<string, string>
{
["status"] = status
};
}
else
{
data = new Dictionary<string, string>
{
["in_reply_to_status_id"] = "1169662308278292480",
["status"] = status
};
}
request.Content = new FormUrlEncodedContent(data);
using (HttpResponseMessage response = await client.Value.SendAsync(request))
{
JavaScriptSerializer json = new JavaScriptSerializer();
string responseStream = await response.Content.ReadAsStringAsync();
Dictionary<string, object> jsonObj = json.DeserializeObject(responseStream) as Dictionary<string, object>;
if (counter == 0) { id = jsonObj["id_str"].ToString(); }
if (response.StatusCode == HttpStatusCode.OK) {
return "OK";
}
else { return ""; }
}
}
This is what I'm doing so far for the signature:
This is what I did to make it work. I had to put the parameters in order. This is a requirement for OAuth, otherwise you'll get a 401 Unauthorized.
private static string CreateBaseString(string oauth_nonce, string oauth_timestamp, string status)
{
string baseFormat = "in_reply_to_status_id=1169662308278292480&oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}"
+ "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&status={6}";
string baseString = String.Format(baseFormat, oauth.OAuth_Consumer_Key, oauth_nonce, oauth.OAuth_Signature_Method,
oauth_timestamp, oauth.OAuth_Token, oauth.OAuth_Version, EncodingUtils.UrlEncode(status));
return String.Concat("POST&", EncodingUtils.UrlEncode(oauth.Resource_Url), "&", EncodingUtils.UrlEncode(baseString));
}

How to add a specific user to a segment in OneSignal

I'm developing a C#.NET MVC Web Api for an Android application. At the moment I am using OneSignal to send push notifications to the users by calling the OneSignal Api and passing the notification content. I need to know how to add a user to a specific segment so that i can send notifications to individual users as well as users of that segment collectively. I have searched in on their documentation but I didn't understand how to do it using OneSignal.SendTag method. So basically how to do it in Visual Studio? So far i have done this:
string api_key = "dsabjd";
var request = WebRequest.Create("https://onesignal.com/api/v1/notifications") as HttpWebRequest;
if (user != null)
{
string message = "This job is posted by: \n" + user.Name + "\n" + user.Contact + "\n" +user.City;
if (request != null)
{
request.KeepAlive = true;
request.Method = "POST";
request.ContentType = "application/json";
request.Headers.Add("authorization", "Basic "+api_key);
var serializer = new JavaScriptSerializer();
var obj = new
{
app_id = "1651",
contents = new { en = message },
//data = new { image = "http://dsadasdasd.png" },
data = new { image = imageUrl },
included_segments = new string[] { "All" }
};
var param = serializer.Serialize(obj);
byte[] byteArray = Encoding.UTF8.GetBytes(param);
try
{
using (var writer = request.GetRequestStream())
{
writer.Write(byteArray, 0, byteArray.Length);
}
string responseContent=null;
using (var response = request.GetResponse() as HttpWebResponse)
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
responseContent = reader.ReadToEnd();
}
}
if (responseContent != null)
{
// parsing the json returned by OneSignal Push API
dynamic json = JObject.Parse(responseContent);
int noOfRecipients = json.recipients;
if (noOfRecipients > 0)
{
flag = true;
}
}
}
catch (WebException ex)
{
flag = false;
}
}
}
To set tags it is recommend you use sendTags from the OneSignal Android SDK in your app it's supported offline and handles retries for you.
If you need to target individual users it is recommend to call idsAvailable in your app and send this to your server. You can later use the include_player_ids field on the create notification REST API POST call to send a notification to a list of users.

Box API Create Shared link

I am trying to upload some documents to Box and create and retrieve a shared link for each of them.
This is the code I am using for it, but I always retrieve 403:access_denied_insufficient_permissions.
Any idea of why this is happening?
Hope you can help me! Thanks.
// CREATE THE FILE
BoxFileRequest req = new BoxFileRequest
{
Name = zipFile.Name,
Parent = new BoxRequestEntity { Id = newFolder.Id}
};
BoxFile uploadedFile = client.FilesManager.UploadAsync(req, stream).Result;
//REQUEST SHARED LINK
BoxSharedLinkRequest sharedLinkReq = new BoxSharedLinkRequest()
{
Access = BoxSharedLinkAccessType.open,
Permissions = new BoxPermissionsRequest
{
Download = BoxPermissionType.Open,
Preview = BoxPermissionType.Open,
}
};
BoxFile fileLink = fileManager.CreateSharedLinkAsync(uploadedFile.Id, sharedLinkReq).Result;
you need to give the access token and url. I have use the Following code and in JSON Format you will get the Response. For more reference check the box API document
HttpWebRequest httpWReq = HttpWebRequest)WebRequest.Create("https://api.box.com/2.0/folders/" + FolderID);
ASCIIEncoding encoding = new ASCIIEncoding();
string putData = "{\"shared_link\": {\"access\": \"open\"}}";
byte[] data = encoding.GetBytes(putData);
httpWReq.Method = "PUT";
httpWReq.Headers.Add("Authorization", "Bearer ");
httpWReq.ContentType = "application/json";
httpWReq.ContentLength = data.Length;
Use the httpwebrequest PUT method after this.
Mark it as Answer if its helpful.
It looks as if you are using the 3rd party BoxSync V2 API object. If you would like to just code to the API directly I had a similar issue that you are having. If you view this post you will see the answer. Here is the code I use and it works.
string uri = String.Format(UriFiles, fileId);
string response = string.Empty;
string body = "{\"shared_link\": {\"access\": \"open\"}}";
byte[] postArray = Encoding.ASCII.GetBytes(body);
try
{
using (var client = new WebClient())
{
client.Headers.Add("Authorization: Bearer " + token);
client.Headers.Add("Content-Type", "application/json");
response = client.UploadString(uri, "PUT", body);
}
}
catch (Exception ex)
{
return null;
}
return response;

"Value does not fall within the expected range." error when call GAE-hosted REST service from Windows Phone 7

We're having troubles when sending http POST request to GAE-hosted script from an application in Windows Phone 7 emulator. When the callback method HttpWebRequest.EndGetResponse() is invoked, it either hangs with timeout or crashes with exception "Value does not fall within the expected range". The flow is asynchronous, we tried HttpWebRequest based sample piece of code from MSDN, stackoverflow and other sources but they doesn't help.
Could you please give us some advice on this topic or provide some sample code for making POST requests with content-type application/x-www-form-urlencoded (just like any normal web form)?
Please find the code sample below:
public void CallRestService() {
var request = WebRequest.Create("https://blogboosterapi.appspot.com/") as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.BeginGetRequestStream(ar => {
var request1 = (HttpWebRequest) ar.AsyncState;
using (var postStream = request1.EndGetRequestStream(ar)) {
var formData = Encoding.UTF8.GetBytes(ParametersToString(GetParameters()));
postStream.Write(formData, 0, formData.Length);
postStream.Close();
}
request1.BeginGetResponse(a => {
var request2 = (HttpWebRequest) a.AsyncState;
WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);
var response = (HttpWebResponse) request2.EndGetResponse(a);
// It fails here ^^^^^^^^^^^^^^^^^^^^^^^^^^
using (var streamResponse = response.GetResponseStream()) {
using (var streamReader = new StreamReader(streamResponse)) {
var responseString = streamReader.ReadToEnd();
//
}
}
}, request1);
}, request);
}
private static string ParametersToString(Dictionary<string, string> parameters) {
var stringBuilder = new StringBuilder();
foreach (var parameter in parameters) {
stringBuilder.AppendFormat("{0}={1}&", parameter.Key, parameter.Value);
}
return stringBuilder.ToString();
}
public Dictionary<string, string> GetParameters() {
var parameters = new Dictionary<string, string>();
//Mandatory parameters
parameters["methodName"] = "initUser";
parameters["timestamp"] = DateTime.UtcNow.ToString("o");
parameters["language"] = "en";
parameters["clientId"] = string.Empty;
parameters["apiVersion"] = "1";
//Method parameters
parameters["clientType"] = "4";
parameters["clientUDID"] = "1234567890";
parameters["clientOS"] = "Windows Phone 7";
//Optional parameters
parameters["clientFirmware"] = "1.0";
parameters["country"] = "ru";
parameters["phoneNumber"] = "+791012345678";
return parameters;
}
it could be if you have '&' or '=' without values in your Request e.g. http://smth.com?method=get&param=4& or http://smth.com?method=get&param=
Ironically, this problem still occured after copying the code from a WP7 to a Xamarin project (WP 8.1). It also doesn't work in a Xamarin Android project, though with another exception.
After two days of research I finally gave up and moved to the WebClient class for my web requests, which works like a charm.

Categories

Resources