OAuth in PlayReady License Retrieval in UWP - c#

Any idea how to inject OAuth headers into PlayReadyLicenseAcquisitionServiceRequest so they are included with the BeginServiceRequest()? I can't leverage the license URL's query string, or embed the OAuth token in the body; it has to be in the header of the License retrieval's http request.

I found some great sample code here:
https://www.eyecatch.no/blog/using-playready-and-smooth-streaming-in-a-windows-10-uwp-app/
but this was the magic sauce below (with my header addition):
public static async Task<bool> RequestLicenseManual(PlayReadyLicenseAcquisitionServiceRequest request, params KeyValuePair<string, object>[] headers)
{
Debug.WriteLine("ProtectionManager PlayReady Manual License Request in progress");
try
{
var r = request.GenerateManualEnablingChallenge();
var content = new ByteArrayContent(r.GetMessageBody());
foreach (var header in r.MessageHeaders.Where(x => x.Value != null))
{
if (header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
{
content.Headers.ContentType = MediaTypeHeaderValue.Parse(header.Value.ToString());
}
else
{
content.Headers.Add(header.Key, header.Value.ToString());
}
}
var msg = new HttpRequestMessage(HttpMethod.Post, r.Uri) { Content = content };
foreach (var header in headers)
{
msg.Headers.Add(header.Key, header.Value.ToString());
}
Debug.WriteLine("Requesting license from {0} with custom data {1}", msg.RequestUri, await msg.Content.ReadAsStringAsync());
var client = new HttpClient();
var response = await client.SendAsync(msg);
if (response.IsSuccessStatusCode)
{
request.ProcessManualEnablingResponse(await response.Content.ReadAsByteArrayAsync());
}
else
{
Debug.WriteLine("ProtectionManager PlayReady License Request failed: " + await response.Content.ReadAsStringAsync());
return false;
}
}
catch (Exception ex)
{
Debug.WriteLine("ProtectionManager PlayReady License Request failed: " + ex.Message);
return false;
}
Debug.WriteLine("ProtectionManager PlayReady License Request successfull");
return true;
}

Related

Teams Outgoing WebHook HMAC problem not matching

I created an outgoing Teams webhook.
The callback URL points to a controller on my API, and I would like to use the HMAC provided by the webhook in the request header.
However, when I compute the HMAC with the secret key, I don't obtain the same key as the one in the header.
I tried this code :
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
try
{
if (!this.Request.Headers.TryGetValue("Authorization", out var headerValue))
{
return AuthenticateResult.Fail("Authorization header not found.");
}
var sentKey = headerValue.ToString().Replace("HMAC ", null);
string requestBody = null;
using (var reader = new StreamReader(this.Request.Body, Encoding.UTF8))
{
requestBody = await reader.ReadToEndAsync();
}
if (string.IsNullOrWhiteSpace(requestBody))
{
return AuthenticateResult.Fail("No content to authenticate.");
}
var secretKeyBytes = Encoding.UTF8.GetBytes(this.Options.SecretKey);
using (var hmac = new HMACSHA256(secretKeyBytes))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
var expectedSignature = WebEncoders.Base64UrlEncode(hash);
if (!string.Equals(sentKey, expectedSignature, StringComparison.Ordinal))
{
return AuthenticateResult.Fail("Invalid HMAC signature.");
}
}
var claimsIdentity = new ClaimsIdentity();
var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), this.Scheme.Name);
return AuthenticateResult.Success(ticket);
}
catch (Exception ex)
{
return AuthenticateResult.Fail($"{ex.HResult}, {ex.Message}");
}
}

Unauthorized status from mobile Xamarin Forms application with Axelor Rest web-services

I want to connect to the Axelor server with a Xamarin forms application, I use this method to test the rest services connectivity :
Login login = new()
{
UserName = Constants.Login,
Password = Constants.Password
};
// Build authentication string
byte[] bytes = Encoding.UTF8.GetBytes($"{login.UserName}:{login.Password}");
string authorizationString = Convert.ToBase64String(bytes);
// Add headers to the HTTP client
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationString);
// Build HTTP message and send, strRoot = login.jsp
string url = $"{Constants.RestBaseUrl}/{strRoot}";
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url);
var sessionResponse = await httpClient.SendAsync(message);
/* sessionResponse.IsSuccessStatusCode = true, and sessionResponse.Headers =
{
Set-Cookie: JSESSIONID=E5EA31C3A5CBDF0A1C4B05ED2230679E; Path=/Gradle___com_axelor___axelor_erp_6_3_0_war; HttpOnly
Date: Mon, 14 Nov 2022 19:40:04 GMT
}
*/
sessionResponse.Headers.ToList().ForEach(x =>
httpClient.DefaultRequestHeaders.Add(x.Key, x.Value.FirstOrDefault()));
var rt = $"{Constants.RestBaseUrl}/ws/rest/com.axelor.apps.poultryfarming.db.Hatchery";
var response = await httpClient.GetAsync(rt);
var returnValue = await response.Content.ReadAsStringAsync();
/* returnValue :
<!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The request has not been applied because it lacks valid authentication credentials for the target resource.</p><hr class="line" /><h3>Apache Tomcat/8.5.73</h3></body></html>
*/
var stat = response.StatusCode; // return: Unauthorized
var cc = response.IsSuccessStatusCode;
return returnValue;
}
catch (Exception e)
{
throw;
}
but i always get the Unauthorized status !
Can you help me please
When I test with navigator I have correct response like this :
It works well with RestSharp.RestRequest!
Call Axelor rest services with RestClient example : https://github.com/dalmed/Maui-Axelor-Mobile.git
This is my solution :
public class RestClientService : IRestClientService
{
//private readonly ILogger<RestClientService> _logger;
public RestClientService()
{
//_logger = logger;
}
public async Task<bool> LoginAsync(Login login)
{
string jsonString = JsonSerializer.Serialize(login);
try
{
Constants.Cookies = null;
Constants.IsServerConnected = false;
var uri = new Uri($"{Constants.RestBaseUrl}/login.jsp");
var _RestClient = new RestClient(uri);
var request = new RestRequest(uri, Method.Post);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", login, ParameterType.RequestBody);
RestResponse response = await _RestClient.ExecuteAsync(request);
if (response.IsSuccessStatusCode)
{
Constants.Cookies = response.Cookies;
Constants.IsServerConnected = true;
}
return response.IsSuccessStatusCode;
}
catch (Exception e)
{
// _logger.LogError(e, "LoginError");
throw;
}
}
public async Task<RootData<T>> RestServiceGetAsync<T>(string model, int offset = 0, int limit = 10)
{
try
{
var uri = $"{Constants.RestBaseUrl}/ws/rest/{model}";
var _RestClient = new RestClient(uri);
var request = new RestRequest(uri, Method.Get);
request.AddHeader("Content-Type", "application/json");
if (Constants.Cookies.Any() && Constants.IsServerConnected)
{
string cookie = "";
Constants.Cookies.ToList().ForEach(x => cookie += $"{x.Name}={x.Value}; ");
request.AddHeader("Cookie", cookie);
}
else
{
throw new Exception("The server is not connected !");
}
request.AddParameter("offset", offset, ParameterType.UrlSegment);
request.AddParameter("limit", limit, ParameterType.UrlSegment);
RestResponse response = await _RestClient.ExecuteAsync(request);
if (response.IsSuccessStatusCode)
{
var data = new MemoryStream(Encoding.UTF8.GetBytes(response.Content));
var rslt = await JsonSerializer.DeserializeAsync<RootData<T>>(data);
return rslt;
}
return null;
}
catch (Exception e)
{
//_logger.LogError(e, $"GetModelError: {model}");
throw;
}
}
public async Task<bool> LogoutAsync()
{
try
{
var uri = $"{Constants.RestBaseUrl}/logout";
var _RestClient = new RestClient(uri);
var request = new RestRequest(uri, Method.Get);
request.AddHeader("Content-Type", "application/json");
if (Constants.Cookies.Any() && Constants.IsServerConnected)
{
string cookie = "";
Constants.Cookies.ToList().ForEach(x => cookie += $"{x.Name}={x.Value}; ");
request.AddHeader("Cookie", cookie);
}
else
{
throw new Exception("The server is not connected !");
}
RestResponse response = await _RestClient.ExecuteAsync(request);
return response.IsSuccessStatusCode;
}
catch (Exception e)
{
//_logger.LogError(e, "LogoutError");
throw;
}
}
}

Get zip file from RestRequest

For REST calls I use RestSharp, I have to call a service that has to return me a zip file which I will then save on File System
public bool DownloadZip(int id)
{
while (true)
{
var request = new RestRequest("download/zip", DataFormat.Json);
request.AddHeader("authorization", _token);
request.AddHeader("ID", _id.ToString());
request.AddQueryParameter("id", id.ToString());
var response = new RestClient(url).Get(request);
response.ContentType = "application/zip";
_logFile.Debug($"downloaded result {id}: {response.IsSuccessful} {response.StatusCode}");
if (response.IsSuccessful && !string.IsNullOrWhiteSpace(response.Content))
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(response.Content)))
{
using (var zip = File.OpenWrite(path: #"C:\temp\temp.zip"))
{
zip.CopyTo(stream);
}
}
return true;
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
_logFile.Warn($"Download Unauthorized {id}: {response.IsSuccessful} {response.StatusCode} {response.Content}");
_authToken = null;
}
else
{
_logFile.Error($"Download {id}: {response.IsSuccessful} {response.StatusCode} {response.Content}");
throw new Exception("DownloadZip Failed");
}
}
}
The line of code "zip.CopyTo(stream);" returns "The stream does not support reading" as an error.
Is there any setting to set to ensure that it does not return an error?
Testing the call on Postman I noticed that in the response header I have Content-Disposition from this can I go back to the filename?
With RestSharp 107 you can use
var stream = await client.DownloadStreamAsync(request);
It will give you a stream and you can do whatever you want with it.

Unable to send large attachment using graph api

I am trying to add a large attachment to an email using Microsoft Graph.
Steps:
Get Token:
public static async Task<GraphServiceClient> GetAuthenticatedClientForApp(IConfidentialClientApplication app)
{
GraphServiceClient graphClient = null;
// Create Microsoft Graph client.
try
{
var token = await GetTokenForAppAsync(app);
graphClient = new GraphServiceClient(
"https://graph.microsoft.com/beta",
new DelegateAuthenticationProvider(async(requestMessage) =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", token);
}));
return graphClient;
}
catch (Exception ex)
{
Logger.Error("Could not create a graph client: " + ex.Message);
}
return graphClient;
}
/// <summary>
/// Get Token for App.
/// </summary>
/// <returns>Token for app.</returns>
public static async Task<string> GetTokenForAppAsync(IConfidentialClientApplication app)
{
AuthenticationResult authResult;
authResult = await app
.AcquireTokenForClient(new string[] { "https://graph.microsoft.com/.default" })
.ExecuteAsync(System.Threading.CancellationToken.None);
return authResult.AccessToken;
}
Create Draft:
Message draft = await client
.Users[emailDTO.FromEmail]
.Messages
.Request()
.AddAsync(msg);
Attach file:
if (emailDTO.FileAttachments != null && emailDTO.FileAttachments.Count() > 0)
{
foreach (EmailAttachment emailAttachment in emailDTO.FileAttachments)
{
if (emailAttachment.UploadFile != null && emailAttachment.UploadFile.Length > 0)
{
var attachmentItem = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = emailAttachment.FileName,
Size = emailAttachment.UploadFile.Length
};
var session = await client
.Users[emailDTO.FromEmail]
.MailFolders
.Drafts
.Messages[draft.Id]
.Attachments
.CreateUploadSession(attachmentItem)
.Request()
.PostAsync();
var stream = new MemoryStream(emailAttachment.UploadFile);
var maxChunkSize = 320 * 1024 * 1024;
var provider = new ChunkedUploadProvider(session, client, stream, maxChunkSize);
var readBuffer = new byte[maxChunkSize];
var chunkRequests = provider.GetUploadChunkRequests();
//var uploadedItem = await provider.UploadAsync();
var trackedExceptions = new List<Exception>();
foreach (var rq in chunkRequests)
{
var result = await provider.GetChunkRequestResponseAsync(rq, readBuffer, trackedExceptions);
}
}
}
}
Error:
{
Code: InvalidAudienceForResource
Message: The audience claim value is invalid for current resource.
Audience claim is 'https://graph.microsoft.com', request url is
'https://outlook.office.com/api/beta/User
I believe the problem here is that the session URL that gets created points to a resource that is not on Microsoft Graph. However, when you use the same client to call that endpoint it passes the bearer token that belongs to Graph. I believe the session URL has an access token in the URL that is sufficient.
You could update your DelegateAuthenticationProvider function to only add the Authorization header for hosts that are graph.microsoft.com. Or you could use our LargeFileUploadTask instead of the ChunkedUploadProvider and it will do much of this work for you. Sadly, I haven't finished the docs for it yet. I'll come back and update this post soon with a docs link.
var task = new Task(() =>
{
foreach(var attachment in attachments) {
using(MemoryStream stream = new MemoryStream()) {
var mimePart = (MimePart)attachment;
mimePart.Content.DecodeTo(stream);
var size = MeasureAttachmentSize(mimePart);
var attachmentItem = MapAttachmentItem(attachment, size);
// Use createUploadSession to retrieve an upload URL which contains the session identifier.
var uploadSession = client.Users[mailbox]
.Messages[addedMessage.Id]
.Attachments
.CreateUploadSession(attachmentItem)
.Request()
.PostAsync()
.GetAwaiter()
.GetResult();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession
,stream
,maxSliceSize
,client);
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog =>
{
Console.WriteLine($"Uploaded {prog} bytes of {stream.Length} bytes");
});
try {
// Upload the file
var uploadResult = fileUploadTask.UploadAsync(progress, 3).Result;
if(uploadResult.UploadSucceeded) {
// The result includes the location URI.
Console.WriteLine($"Upload complete, LocationUrl: {uploadResult.Location}");
}
else {
Console.WriteLine("Upload failed");
}
}
catch(ServiceException ex) {
Console.WriteLine($"Error uploading: {ex.ToString()}");
throw ex;
}
}
}
});
task.RunSynchronously();

Quickbooks Online sandbox returns Waiting for Activation, i have realmId, accesstoken aswell

My Code is as follow:-
i have no idea why i am receiving this message, please help. Right now
i am using sandbox account to test this. I have generated the data i.e. sample data from API explorer and i am passing it as a parameter as Json.
public bool GeneratePayment(string JsonData)
{
var principal = User as ClaimsPrincipal;
Session["realmId"] = "XXXXXX";
if (Session["realmId"] != null)
{
string realmId = Session["realmId"].ToString();
string qboBaseUrl = ConfigurationManager.AppSettings["QBOBaseUrl"];
//add qbobase url and query
string uri = string.Format("{0}/v3/company/{1}/invoice", qboBaseUrl, realmId);
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + "XXXX");
//Here i am getting waiting for activation
var result = client.PostAsync(uri, new StringContent(JsonData, System.Text.Encoding.UTF8, "application/json"));
return true;
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
Has to do with the Task associated with PostAsync.
The GeneratePayment method needs to be made async and client.PostAsync needs to be awaited as well
public async Task<bool> GeneratePayment(string JsonData) {
var principal = User as ClaimsPrincipal;
Session["realmId"] = "XXXXXX";
if (Session["realmId"] != null) {
string realmId = Session["realmId"].ToString();
string qboBaseUrl = ConfigurationManager.AppSettings["QBOBaseUrl"];
//add qbobase url and query
string uri = string.Format("{0}/v3/company/{1}/invoice", qboBaseUrl, realmId);
try {
var client = http.Value; //singleton http client
var result = await client.PostAsync(uri, new StringContent(JsonData, System.Text.Encoding.UTF8, "application/json"));
return true;
} catch (Exception ex) {
return false;
}
}
else
return false;
}
//Singleton lazy loaded HttpClieny
static Lazy<HttpClient> http = new Lazy<HttpClient>(() => {
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + "XXXX");
return client;
});

Categories