Getting unauthorised access in YouTube API [Xamarin.Forms (Portable)] - c#

I am trying to upload the Video from Xamarin.Forms (Portable) to YouTube, I have tried to use the Google APIs but the Google APIs are not compatible with the Xamarin.Forms (Portable) at this stage. So, I compulsory have to upload it via HttpClient but I am getting Unauthorised in StatusCode
public async Task UploadVideoAsync(Stream stream)
{
//var token = flow.LoadTokenAsync("", CancellationToken.None).Result;
string json = #"{
""snippet"": {
""title"": ""using API"",
""description"": ""This is a description of my video"",
""tags"": [""cool"", ""video"", ""more keywords""],
""categoryId"": ""21"",
},
""status"": {
""privacyStatus"": ""public"",
""embeddable"": true,
""license"": ""youtube""
}
}";
var JsonReqMsg = new StringContent(json);
JsonReqMsg.Headers.ContentType = new MediaTypeHeaderValue("application/json")
{
CharSet = "UTF-8"
};
var request = new HttpRequestMessage
(HttpMethod.Post, new Uri("https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status"));
request.Headers.Add("X-Upload-Content-Length", stream.Length.ToString());
request.Headers.Add("x-upload-content-type", "video/*");
request.Content = JsonReqMsg;
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", Constants.API.Google.AccessTokenType + " " + Constants.API.Google.AccessToken);
var UploadReq = await httpClient.SendAsync(request);
if (UploadReq.IsSuccessStatusCode)
{
IEnumerable<string> _VideoUrl = null;
var res = await UploadReq.Content.ReadAsStringAsync();
UploadReq.Headers.TryGetValues("Location", out _VideoUrl);
var binaryContent = new StreamContent(stream);
var UploadReq_ = await httpClient.PutAsync(new Uri(_VideoUrl.ToString()), binaryContent);
if (UploadReq_.IsSuccessStatusCode)
{
var res_ = await UploadReq_.Content.ReadAsStringAsync();
}
}
}
Is there anything wrong in the code?

You're authorization is incorrect. You should be using "bearer" or a developer key. Here is the YouTube documentation: https://developers.google.com/youtube/2.0/developers_guide_protocol#OAuth2_Calling_a_Google_API

Related

.Net Core 5 Web Api - Swagger POST ok Xunit POST Error

I'm using XUnit to test my WebApi, GET requests are working ok. But when testing a POST through XUnit I received a 404 Bad Request while Swagger returns 200 OK
Teste
[Theory]
[InlineData("POST")]
public async Task ContestCreateTest(string method)
{
var _httpClient = new Setup().httpClient;
var request = new HttpRequestMessage(new HttpMethod(method), "/v1/Contest");
var contestObject = new
{
title = "Concurso automatizado",
description = "Concurso automatizado",
submissionDeadLineInit = TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(1), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"),
submissionDeadLineEnd = TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(2), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"),
ratingDeadLineInit = TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(3), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"),
ratingDeadLineEnd = TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(4), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"),
closingDate = TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(5), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"),
useDistritoRules = true,
rules = ""
};
string contestJson = JsonConvert.SerializeObject(contestObject);
request.Content = new StringContent(contestJson, Encoding.UTF8, "multipart/form-data");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
As the image shows your API accepts form but you're sending data in json format.
var formDataContent = new MultipartFormDataContent
{
{ new StringContent("Concurso automatizado", Encoding.UTF8), "title"},
{ new StringContent("Concurso automatizado", Encoding.UTF8), "description")},
{ new StringContent(TimeZoneInfo.ConvertTime(DateTime.UtcNow.AddDays(1), TZConvert.GetTimeZoneInfo("America/Sao_Paulo")).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'", Encoding.UTF8), "submissionDeadLineInit" },
...
};
// Act
var response = await _client.PostAsync("/v1/Contest", formDataContent);

YouTube API Video Upload: BadRequest: Metadata part is too large

I am uploading a video to YouTube via their API with C#. I am using HttpClient.PostAsync() for that.
I get the following error after executing PostAsync(): Bad Request: Metadata part is too large.
I am not quite sure, if this error was generated by my code, or if the error happened on the YouTube API.
//Prepare the file from the form
var filePath = Path.GetTempFileName();
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
//Application logic, not related to YouTube API
var user = await _userManager.FindByIdAsync(User.GetClaim(OpenIdConnectConstants.Claims.Subject));
var personalPot = await _context.PersonalPots.FirstOrDefaultAsync(i => i.Id == id);
if (user.Id != personalPot.Owner.Id)
{
return Unauthorized();
}
//Get the access token for the YouTube API
var accessToken = await _externalContentService.RefreshGoogleToken(personalPot.Id, new Guid(user.Id));
//Construct the properties, which will be send with the video file to upload
var properties = new Properties()
{
snippet = new Snippet()
{
title = title,
categoryId = categoryId,
defaultLanguage = defaultLanguage,
description = description,
tags = tags.Split(",")
},
status = new Status()
{
embeddable = embeddable == "true",
license = license,
privacyStatus = privacy,
publicStatsViewable = publicStatsViewable == "true"
}
};
//Construct the HttpClient to post the file to YouTube
var client = new HttpClient
{
BaseAddress = new Uri("https://www.googleapis.com/"),
Timeout = new TimeSpan(0, 0, 0, 0, Timeout.Infinite),
MaxResponseContentBufferSize = 2147483647
};
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
var requestContent = new MultipartFormDataContent();
var fileContent = new StreamContent(formFile.OpenReadStream());
var stringContent = new StringContent(JsonConvert.SerializeObject(properties), Encoding.UTF8, "application/json");
requestContent.Add(fileContent);
requestContent.Add(stringContent);
var result = await client.PostAsync("upload/youtube/v3/videos?part=snippet,status", requestContent);
//Result content will be "Bad Request; Metadata part too large"
if (!result.IsSuccessStatusCode)
{
return BadRequest(new {content = result.Content.ReadAsStringAsync(), reasonPhrase = result.ReasonPhrase});
}

Create a new user in Azure Active Directory (B2C) with Graph API, using http post request

I have previously been adding users programmatically using Active Directory Authentication Library (ADAL), but now I need to define "signInNames" (= users email), and that doesn't seem to be possible with ADAL (please tell me if im wrong).
Now I'm trying to add a new user (local account) programmatically using HTTP POST, following the documentation on MSDN.
//Get access token (using ADAL)
var authenticationContext = new AuthenticationContext(AuthString, false);
var clientCred = new ClientCredential(ClientId, ClientSecret);
var authenticationResult = authenticationContext.AcquireTokenAsync(ResourceUrl, clientCred);
var token = authenticationResult.Result.AccessToken;
//HTTP POST CODE
const string mail = "new#email.com";
// Create a new user object.
var user = new CustomUser
{
accountEnabled = true,
country = "MS",
creationType = "LocalAccount",
displayName = mail,
passwordPolicies = "DisablePasswordExpiration,DisableStrongPassword",
passwordProfile = new passwordProfile { password = "jVPmEm)6Bh", forceChangePasswordNextLogin = true },
signInNames = new signInNames { type = "emailAddress", value = mail }
};
var url = "https://graph.windows.net/" + TenantId + "/users?api-version=1.6";
var jsonObject = JsonConvert.SerializeObject(user);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = client.PostAsync(url,
new StringContent(JsonConvert.SerializeObject(user).ToString(),
Encoding.UTF8, "application/json"))
.Result;
if (response.IsSuccessStatusCode)
{
dynamic content = JsonConvert.DeserializeObject(
response.Content.ReadAsStringAsync()
.Result);
// Access variables from the returned JSON object
var appHref = content.links.applications.href;
}
}
But i have no success, getting this response:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content:....}
Any ideas what i should do? I succeeded using Powershell-script, but I need to do this in my C# app.
Thank you for your response Fei Xue, i believe i had the right permissions. What i did to solvem my problem.
First off i removed my own custom class "NewUser", then i downloaded this sample-project: https://github.com/AzureADQuickStarts/B2C-GraphAPI-DotNet/blob/master/B2CGraphClient/B2CGraphClient.cs to eliminate the risk that my code was wrong. I modified it to support my needs, then i created a simple JObject:
var jsonObject = new JObject
{
{"accountEnabled", true},
{"country", customer.CustomerBase.Company},
{"creationType", "LocalAccount"},
{"displayName", pendingCustomer.Email.Trim()},
{"passwordPolicies", "DisablePasswordExpiration,DisableStrongPassword"},
{"passwordProfile", new JObject
{
{"password", pwd},
{"forceChangePasswordNextLogin", true}
} },
{"signInNames", new JArray
{
new JObject
{
{"value", pendingCustomer.Email.Trim()},
{"type", "emailAddress"}
}
}
}
};
client = new B2CGraphClient(ClientId, ClientSecret, TenantId);
var response = await client.CreateUser(jsonObject.ToString());
var newUser = JsonConvert.DeserializeObject<User>(response);
From B2CGraphClient.cs
private async Task<string> SendGraphPostRequest(string api, string json)
{
// NOTE: This client uses ADAL v2, not ADAL v4
var result = authContext.AcquireToken(Globals.aadGraphResourceId, credential);
var http = new HttpClient();
var url = Globals.aadGraphEndpoint + tenant + api + "?" + Globals.aadGraphVersion;
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
var formatted = JsonConvert.DeserializeObject(error);
//Console.WriteLine("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
Logger.Error("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
}
Logger.Info((int)response.StatusCode + ": " + response.ReasonPhrase);
return await response.Content.ReadAsStringAsync();
}
This finally solved all my problems, it was probably an format-error in the serialization of my NewCustomer-class, which then got rejected by the API.
Did you grant the app sufficient permission to operate users? The create user REST API works well for me for the B2C tenant.
Here are the steps I tested:
1.Create the app via the PowerShell below
PowerShell:
$bytes = New-Object Byte[] 32
$rand = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rand.GetBytes($bytes)
$rand.Dispose()
$newClientSecret = [System.Convert]::ToBase64String($bytes)
New-MsolServicePrincipal -DisplayName "My New B2C Graph API App" -Type password -Value
2.Grant the app to User Account Administrator role.
Add-MsolRoleMember -RoleObjectId fe930be7-5e62-47db-91af-98c3a49a38b1 -RoleMemberObjectId 7311370c-dac3-4f34-b2ce-b22c2a5a811e -RoleMemberType servicePrincipal
3.Get the token for the app with client credential flow
POST: https://login.microsoftonline.com/adb2cfei.onmicrosoft.com/oauth2/token
grant_type=client_credentials&client_id={AppPrincipalId return by PowerShell}&client_secret={client_secret}&resource=https%3A%2F%2Fgraph.windows.net
4.Create the user with REST below:
POST: https://graph.windows.net/adb2cfei.onmicrosoft.com/users?api-version=1.6
authorization: bearer {token}
content-type: application/json
{
"accountEnabled": true,
"creationType": "LocalAccount",
"displayName": "Alex Wu",
"passwordProfile": {
"password": "Test1234",
"forceChangePasswordNextLogin": false
},
"signInNames": [
{
"type": "userName",
"value": "AlexW"
},
{
"type": "emailAddress",
"value": "AlexW#example.com"
}
]
}

Send request as Json on UWP

I have deployed an AzureML published experiment with deployed web service. I tried to use the sample code provided in the configuration page, but universal apps do not implement Http.Formatting yet, thus I couldn't use postasjsonasync.
I tried to follow the sample code as much as possible, but I'm getting statuscode of 415 "Unsupported Media Type", What's the mistake I'm doing?
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
// client.BaseAddress = uri;
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"dataInput",
new StringTable()
{
ColumnNames = new [] {"Direction", "meanX", "meanY", "meanZ"},
Values = new [,] { { "", x.ToString(), y.ToString(), z.ToString() }, }
}
},
},
GlobalParameters = new Dictionary<string, string>() { }
};
var stringContent = new StringContent(scoreRequest.ToString());
HttpResponseMessage response = await client.PostAsync(uri, stringContent);
Many Thanks
You'll need to serialize the object to a JSON string (I recommend using NewtonSoft.Json to make it easier) and set the content type accordingly. Here's an implementation I'm using in my UWP apps (note that _client is an HttpClient):
public async Task<HttpResponseMessage> PostAsJsonAsync<T>(Uri uri, T item)
{
var itemAsJson = JsonConvert.SerializeObject(item);
var content = new StringContent(itemAsJson);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return await _client.PostAsync(uri, content);
}

C# Add item to Sharepoint list using REST API

I would like to add an item to a list in sharepoint using below code:
protected string httpGetPost(string getPostMode, string url, string dataToPost = "")
{
HttpWebRequest endpointRequest = (HttpWebRequest)WebRequest.Create(url);
endpointRequest.Method = getPostMode;
var credentialCache = new CredentialCache();
credentialCache.Add(
new Uri(endpointRequest.RequestUri.GetLeftPart(UriPartial.Authority)), // request url's host
"Digest", // authentication type
new NetworkCredential(userName, password) // credentials
);
endpointRequest.Credentials = credentialCache;
endpointRequest.Accept = "application/json;odata=verbose";
endpointRequest.ContentType = "application/json;odata=verbose";
if (!string.IsNullOrEmpty(dataToPost))
{
using (Stream dataStream = endpointRequest.GetRequestStream())
{
byte[] bs = Encoding.ASCII.GetBytes(dataToPost);
dataStream.Write(bs, 0, bs.Length);
}
}
using (var resp = endpointRequest.GetResponse())
{
var html = new StreamReader(resp.GetResponseStream()).ReadToEnd();
return html;
}
}
And call the above method using below code:
httpGetPost("POST", url, "{\"__metadata\": { \"type\": \"SP.Data.Test_x0020_ListListItem\" }, \"Title\": \"Test\", \"Column B\", \"BBB\"}");
Here's the data I'm posting:
{"__metadata": { "type": "SP.Data.Test_x0020_ListListItem" }, "Title":
"Test", "Column B", "BBB"}
I've took a look at this website https://msdn.microsoft.com/en-us/library/office/dn292552.aspx, but the authorization is different, it's using an accesstoken, but here's the problem:
In this website: http://sharepoint.stackexchange.com/questions/69617/sharepoint-2013-oauth-url-to-get-token, it saids I can't get the accesstoken, so I used username and password to login the sharepoint, but here comes another problem:
A System.Net.WebException is thrown in var resp = endpointRequest.GetResponse(), the error is saying The remote server returned an error: (403) Forbidden.
The account is a domain admin as well as a sharepoint admin.
Why I'm still getting the 403 error?
For some reasons, I can only use the REST API to communicate with sharepoint.
Here is a slightly different method to achieve your goals. Some of the objects are specific to Store Apps in this example, but they can all easily be replaced with other values in a standard app.
public string digest()
{
String retVal = "";
try
{
string url = "https://YourSite.com/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
string cmd = "_api/contextinfo";
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("ContentType", "application/json");
client.DefaultRequestHeaders.Add("ContentLength", "0");
StringContent httpContent = new StringContent("");
var response = client.PostAsync(cmd, httpContent).Result;
if (response.IsSuccessStatusCode)
{
string content = response.Content.ReadAsStringAsync().Result;
JsonObject val = JsonValue.Parse(content).GetObject();
JsonObject d = val.GetNamedObject("d");
JsonObject wi = d.GetNamedObject("GetContextWebInformation");
retVal = wi.GetNamedString("FormDigestValue");
}
}
catch
{ }
return retVal;
}
FileOpenPicker picker = new FileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
picker.ViewMode = PickerViewMode.Thumbnail;
// Filter to include a sample subset of file types.
picker.FileTypeFilter.Clear();
picker.FileTypeFilter.Add(".bmp");
picker.FileTypeFilter.Add(".png");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".jpg");
// Open the file picker.
StorageFile path = await picker.PickSingleFileAsync();
if (path != null)
{
string url = "https://YourSite.com/Subsite/";
HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
client.BaseAddress = new System.Uri(url);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", digest());
client.DefaultRequestHeaders.Add("X-HTTP-Method", "POST");
client.DefaultRequestHeaders.Add("binaryStringRequestBody", "true");
IRandomAccessStream fileStream = await path.OpenAsync(FileAccessMode.Read);
var reader = new DataReader(fileStream.GetInputStreamAt(0));
await reader.LoadAsync((uint)fileStream.Size);
Byte[] content = new byte[fileStream.Size];
reader.ReadBytes(content);
ByteArrayContent file = new ByteArrayContent(content);
HttpResponseMessage response = await client.PostAsync("_api/web/lists/getByTitle(#TargetLibrary)/RootFolder/Files/add(url=#TargetFileName,overwrite='true')?#TargetLibrary='Project Photos'&#TargetFileName='TestUpload.jpg'", file);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{ }
}

Categories