Wopi Office Online proof keys process - c#

I'm trying to Verifying that requests originate from Office Online by using proof keys
i'm implementing the wopi host with nodejs, and there are samples of code in the link above that implements the proof keys process using .NET
so i'm using the edge package to be able to run the .NET code with nodejs.
the problem is that the code is running fine, but it always returning that the proof keys in the request are not valid even when the requests are coming from the wopi client.
explanation: there are 3 functions:
validateProofKey - the main function - get all the requierd params to validate the proof key and return bool if they are valid using the functions : constructProofKey and TryProofkeyVerification
constructProofKey - declare .NET function (constructProofKey from sample code ) , run it and return the result of the constructed proofkey
TryProofkeyVerification - declare .NET function (TryProofkeyVerification from .NET sample code), run it and return the Boolean result
the code :
static async validateProofKey(access_token: string, request_url: string, X_WOPI_TimeStamp: string, X_WOPI_Proof: string, X_WOPI_Proof_old: string): Promise<boolean> {
//get public key provided in WOPI discovery:
if (!wopiDiscovery) { wopiDiscovery = await this.getWopiDiscovery() }
const public_Keys: iWopiDiscoveryProofKey = wopiDiscovery["wopi-discovery"]["proof-key"];
const public__Key: string = public_Keys.value;
const old_public_Key: string = public_Keys.oldvalue;
this.printProofkeyArgs(access_token, request_url, X_WOPI_TimeStamp, X_WOPI_Proof, X_WOPI_Proof_old, public__Key, old_public_Key, modulus_b64, exp_b64);
//1. Create the expected value of the proof headers.
let expectedProofKey: Buffer = await this.constructProofKey(access_token, request_url, X_WOPI_TimeStamp);
//2.Use the public key provided in WOPI discovery to decrypt the proof provided in the X-WOPI-Proof header.
const verified: boolean = await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof, public__Key) ||
await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof_old, public__Key) ||
await this.TryProofkeyVerification(expectedProofKey, X_WOPI_Proof, old_public_Key)
return verified;
}
constructProofKey
private static constructProofKey(access_token: string, request_url: string, X_WOPI_TimeStamp: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
const proofParams = {
access_token: access_token,
request_url: request_url,
X_WOPI_TimeStamp: X_WOPI_TimeStamp
}
const constructProofKey = edge.func(`
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class Startup
{
public async Task<object> Invoke(dynamic proofParams)
{
return Helper.constructProofKey(proofParams);
}
}
static class Helper
{
public static byte[] constructProofKey(dynamic proofParams)
{
// Encode values from headers into byte[]
long timestamp = Convert.ToInt64(proofParams.X_WOPI_TimeStamp);
var accessTokenBytes = Encoding.UTF8.GetBytes(proofParams.access_token);
var hostUrlBytes = Encoding.UTF8.GetBytes(proofParams.request_url.ToUpperInvariant());
var timeStampBytes = EncodeNumber(timestamp);
// prepare a list that will be used to combine all those arrays together
List < byte > expectedProof = new List<byte>(
4 + accessTokenBytes.Length +
4 + hostUrlBytes.Length +
4 + timeStampBytes.Length);
expectedProof.AddRange(EncodeNumber(accessTokenBytes.Length));
expectedProof.AddRange(accessTokenBytes);
expectedProof.AddRange(EncodeNumber(hostUrlBytes.Length));
expectedProof.AddRange(hostUrlBytes);
expectedProof.AddRange(EncodeNumber(timeStampBytes.Length));
expectedProof.AddRange(timeStampBytes);
// create another byte[] from that list
byte[] expectedProofArray = expectedProof.ToArray();
return expectedProofArray;
}
private static byte[] EncodeNumber(long value)
{
return BitConverter.GetBytes(value).Reverse().ToArray();
}
}
`)
constructProofKey(proofParams, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
});
})
}
TryProofkeyVerification
private static TryProofkeyVerification(expectedProofKey: Buffer, private_proofkey: string, public_proofkey: string): Promise<boolean> {
return new Promise((resolve, reject) => {
const proofParams = {
expectedProofKey: expectedProofKey,
private_proofkey: private_proofkey,
public_proofkey: public_proofkey
}
//declare .NET code
const TryVerification = edge.func(`
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class Startup
{
public async Task<object> Invoke(dynamic proofParams)
{
return Helper.TryVerification(proofParams);
}
}
static class Helper
{
public static bool TryVerification(dynamic proofParams)
{
byte[] expectedProofKey = (byte[])proofParams.expectedProofKey;
using(RSACryptoServiceProvider rsaAlg = new RSACryptoServiceProvider())
{
byte[] publicKey = Convert.FromBase64String(proofParams.public_proofkey);
byte[] signedProofBytes = Convert.FromBase64String(proofParams.private_proofkey);
try
{
rsaAlg.ImportCspBlob(publicKey);
return rsaAlg.VerifyData(expectedProofKey, "SHA256", signedProofBytes);
}
catch(FormatException)
{
Console.WriteLine("Format Exception");
return false;
}
catch(CryptographicException e)
{
Console.WriteLine("CryptographicException Exception");
return false;
}
}
}
}
`);
//Invoke .NET code
TryVerification(proofParams, (error, result) => {
if (error) {
reject(error);
}
resolve(result);
});
})
}
NOTE - I've already log the parameters to console to make sure the parameters transferred correctly through all the process

Related

How to convert azure vision output in string Json to previous object?

I have converted the input image into OCRResults by using these codes.
https://learn.microsoft.com/en-us/azure/cognitive-services/computer-vision/quickstarts-sdk/client-library?tabs=visual-studio&pivots=programming-language-csharp
using System;
using System.Collections.Generic;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading;
using System.Linq;
namespace ComputerVisionQuickstart
{
class Program
{
// Add your Computer Vision subscription key and endpoint
static string subscriptionKey = "PASTE_YOUR_COMPUTER_VISION_SUBSCRIPTION_KEY_HERE";
static string endpoint = "PASTE_YOUR_COMPUTER_VISION_ENDPOINT_HERE";
private const string READ_TEXT_URL_IMAGE = "https://raw.githubusercontent.com/Azure-Samples/cognitive-services-sample-data-files/master/ComputerVision/Images/printed_text.jpg";
static void Main(string[] args)
{
Console.WriteLine("Azure Cognitive Services Computer Vision - .NET quickstart example");
Console.WriteLine();
ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);
// Extract text (OCR) from a URL image using the Read API
ReadFileUrl(client, READ_TEXT_URL_IMAGE).Wait();
}
public static ComputerVisionClient Authenticate(string endpoint, string key)
{
ComputerVisionClient client =
new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
{ Endpoint = endpoint };
return client;
}
public static async Task ReadFileUrl(ComputerVisionClient client, string urlFile)
{
Console.WriteLine("----------------------------------------------------------");
Console.WriteLine("READ FILE FROM URL");
Console.WriteLine();
// Read text from URL
var textHeaders = await client.ReadAsync(urlFile);
// After the request, get the operation location (operation ID)
string operationLocation = textHeaders.OperationLocation;
Thread.Sleep(2000);
// Retrieve the URI where the extracted text will be stored from the Operation-Location header.
// We only need the ID and not the full URL
const int numberOfCharsInOperationId = 36;
string operationId = operationLocation.Substring(operationLocation.Length - numberOfCharsInOperationId);
// Extract the text
ReadOperationResult results;
Console.WriteLine($"Extracting text from URL file {Path.GetFileName(urlFile)}...");
Console.WriteLine();
do
{
results = await client.GetReadResultAsync(Guid.Parse(operationId));
}
while ((results.Status == OperationStatusCodes.Running ||
results.Status == OperationStatusCodes.NotStarted));
// Display the found text.
Console.WriteLine();
var textUrlFileResults = results.AnalyzeResult.ReadResults;
foreach (ReadResult page in textUrlFileResults)
{
foreach (Line line in page.Lines)
{
Console.WriteLine(line.Text);
}
}
Console.WriteLine();
}
}
}
If I converting the output from the azure vision into json.Is any possibilities convert the json to previous object?

How to generate a hash for my Android app?

I'm using this guide to implement an autocomplete funcionality in an app I'm working on, but when I try to test it in different computers it returns me a different hash https://www.c-sharpcorner.com/article/verify-otp-automatically-in-android-without-sms-read-permission-using-xamarin-fo/
The helper class used is the following:
using System.Text;
using Android.Content;
using Android.Content.PM;
using Android.Util;
using Java.Security;
using Java.Util;
namespace InteliMobile.App.Droid.Service
{
public class AppHashKeyHelper
{
private static string HASH_TYPE = "SHA-256";
private static int NUM_HASHED_BYTES = 9;
private static int NUM_BASE64_CHAR = 11;
private static string GetPackageSignature(Context context)
{
var packageManager = context.PackageManager;
var signatures = packageManager.GetPackageInfo(context.PackageName, PackageInfoFlags.Signatures).Signatures;
return signatures.First().ToCharsString();
}
public static string GetAppHashKey(Context context)
{
string keystoreHexSignature = GetPackageSignature(context);
string appInfo = context.PackageName + " " + keystoreHexSignature;
try
{
var messageDigest = MessageDigest.GetInstance(HASH_TYPE);
messageDigest.Update(Encoding.UTF8.GetBytes(appInfo));
byte[] hashSignature = messageDigest.Digest();
hashSignature = Arrays.CopyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
string base64Hash = Android.Util.Base64.EncodeToString(hashSignature, Base64Flags.NoPadding | Base64Flags.NoWrap);
base64Hash = base64Hash.Substring(0, NUM_BASE64_CHAR);
return base64Hash;
}
catch (NoSuchAlgorithmException e)
{
return null;
}
}
}
}
How can I get the hash without using a bash script, since the server runs in a Windows machine? Is it safe to deploy the app to production with the helper class in it?
You don't compute the hash client side. It's sent from the server, and the instructions to do so are at https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
You don't do it in code at all, you do it via command line tools.

How to read Brave Browser cookie database encrypted values in C# (.NET Core)?

I am attempting to read the encrypted values of cookies using a C# console app.
My cookie reader class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1.Models
{
public class ChromeCookieReader
{
public IEnumerable<Tuple<string, string>> ReadCookies(string hostName)
{
if (hostName == null) throw new ArgumentNullException("hostName");
using var context = new ChromeCookieDbContext();
var cookies = context
.Cookies
.Where(c => c.HostKey.Equals("localhost"))
.AsNoTracking();
foreach (var cookie in cookies)
{
var decodedData = ProtectedData
.Unprotect(cookie.EncryptedValue,
null,
DataProtectionScope.CurrentUser);
var decodedValue = Encoding.UTF8.GetString(decodedData);
yield return Tuple.Create(cookie.Name, decodedValue);
}
}
}
}
My EF DbContext
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1.Models
{
public class Cookie
{
[Column("host_key")]
public string HostKey { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("encrypted_value")]
public byte[] EncryptedValue { get; set; }
}
public class ChromeCookieDbContext : DbContext
{
public DbSet<Cookie> Cookies { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// var dbPath = Environment.GetFolderPath(
// Environment.SpecialFolder.LocalApplicationData)
// + #"\Google\Chrome\User Data\Default\Cookies";
var dbPath = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData)
+ #"\BraveSoftware\Brave-Browser\User Data\Default\Cookies";
if (!System.IO.File.Exists(dbPath)) throw new System.IO.FileNotFoundException("Cant find cookie store", dbPath); // race condition, but i'll risk it
var connectionString = "Data Source=" + dbPath + ";Mode=ReadOnly;";
optionsBuilder
.UseSqlite(connectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Cookie>().ToTable("cookies").HasNoKey();
}
}
}
My attempted solution was inspired by Encrypted cookies in Chrome however it doesn't look like it'll work the same despite Brave Browser being based on Chromium. Instead the Windows Data Protection API throws an exception.
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException
HResult=0x0000000D
Message=The data is invalid.
Source=System.Security.Cryptography.ProtectedData
StackTrace:
at System.Security.Cryptography.ProtectedData.ProtectOrUnprotect(Byte[] inputData, Byte[] optionalEntropy, DataProtectionScope scope, Boolean protect)
at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope)
at ConsoleApp1.Models.ChromeCookieReader.<ReadCookies>d__0.MoveNext()
Other known issues: If Brave is open EF Core "freaks out" that the SQLite database is locked and won't read anything.
In Chromium version 80 and up, Google modified the way that cookies are encrypted to provide additional security to users. You cannot pass cookies to the Windows DPAPI directly for decryption anymore. Rather Chrome's Local State stores an encryption key that is decrypted with the Windows DPAI, you have to use that key to decrypt the cookies. I am giving credit where it's due as I did not find this out on my own and used information from the answer at https://stackoverflow.com/a/60611673/6481581 to fix my issue.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace BraveBrowserCookieReaderDemo
{
public class BraveCookieReader
{
public IEnumerable<Tuple<string, string>> ReadCookies(string hostName)
{
if (hostName == null) throw new ArgumentNullException("hostName");
using var context = new BraveCookieDbContext();
var cookies = context
.Cookies
.Where(c => c.HostKey.Equals(hostName))
.AsNoTracking();
// Big thanks to https://stackoverflow.com/a/60611673/6481581 for answering how Chrome 80 and up changed the way cookies are encrypted.
string encKey = File.ReadAllText(System.Environment.GetEnvironmentVariable("LOCALAPPDATA") + #"\BraveSoftware\Brave-Browser\User Data\Local State");
encKey = JObject.Parse(encKey)["os_crypt"]["encrypted_key"].ToString();
var decodedKey = System.Security.Cryptography.ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, System.Security.Cryptography.DataProtectionScope.LocalMachine);
foreach (var cookie in cookies)
{
var data = cookie.EncryptedValue;
var decodedValue = _decryptWithKey(data, decodedKey, 3);
yield return Tuple.Create(cookie.Name, decodedValue);
}
}
private string _decryptWithKey(byte[] message, byte[] key, int nonSecretPayloadLength)
{
const int KEY_BIT_SIZE = 256;
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96;
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
if (message == null || message.Length == 0)
throw new ArgumentException("Message required!", "message");
using (var cipherStream = new MemoryStream(message))
using (var cipherReader = new BinaryReader(cipherStream))
{
var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce);
cipher.Init(false, parameters);
var cipherText = cipherReader.ReadBytes(message.Length);
var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
try
{
var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
cipher.DoFinal(plainText, len);
}
catch (InvalidCipherTextException)
{
return null;
}
return Encoding.Default.GetString(plainText);
}
}
}
}

Hadoop task is cancelled due to task timeout

I am writing a C# mapreduce program that runs on the cluster. The system returns an error of
System.Threading.Tasks.Task
The task is cancelled after a timeout of 32 milliseconds. The data is pretty huge .I even changed the data size to check but the error still occurs, so I am assuming it is not due to the data size.
Is there a way to change the task timeout of 32 milliseconds.
I am using Azure, Visual studio 2013 and a cluster with 4 nodes.
The system aggregate exception occured in mscorlib.dll
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Threading;
using Microsoft.Hadoop.MapReduce;
using Microsoft.WindowsAzure.Management.HDInsight;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Hadoop.Client;
namespace StackExtractor
{
//Our Mapper that takes a line of XML input and spits out the {OwnerUserId,ParentId,Score}
//i.e, {User,Question,Weightage}
public class UserQuestionsMapper : MapperBase
{
public override void Map(string inputLine, MapperContext context)
{
try
{
var obj = XElement.Parse(inputLine);
var postType = obj.Attribute("PostTypeId");
if (postType != null && postType.Value == "2")
{
var owner = obj.Attribute("OwnerUserId");
var parent = obj.Attribute("ParentId");
// Write output data. Ignore records will null values if any
if (owner != null && parent != null)
{
context.EmitLine(string.Format("{0},{1}", owner.Value, parent.Value));
}
}
}
catch
{
//Ignore this line if we can't parse
}
}
}
//Our Extraction Job using our Mapper
public class UserQuestionsExtractionJob : HadoopJob<UserQuestionsMapper>
{
public override HadoopJobConfiguration Configure(ExecutorContext context)
{
var config = new HadoopJobConfiguration();
config.DeleteOutputFolder = true;
config.InputPath = "/input/recommender";
config.OutputFolder = "/output/recommender";
return config;
}
}
//Driver that submits this to the cluster in the cloud
//And will wait for the result. This will push your executables to the Azure storage
//and will execute the command line in the head node (HDFS for Hadoop on Azure uses Azure storage)
public class Driver
{
public static void Main()
{
try
{
var azureCluster = new Uri("https://name r.azurehdinsight.net:563");
const string clusterUserName = "****";
const string clusterPassword = "****";
// This is the name of the account under which Hadoop will execute jobs.
// Normally this is just "Hadoop".
const string hadoopUserName = "Hadoop";
// Azure Storage Information.
const string azureStorageAccount = "name.blob.core.windows.net";
const string azureStorageKey = "id;
const string azureStorageContainer = "namecontainer";
const bool createContainerIfNotExist = true;
Console.WriteLine("Connecting : {0} ", DateTime.Now);
var hadoop = Hadoop.Connect(azureCluster,
clusterUserName,
hadoopUserName,
clusterPassword,
azureStorageAccount,
azureStorageKey,
azureStorageContainer,
createContainerIfNotExist);
Console.WriteLine("Starting: {0} ", DateTime.Now);
var result = hadoop.MapReduceJob.ExecuteJob <UserQuestionsExtractionJob>();
var info = result.Info;
Console.WriteLine("Done: {0} ", DateTime.Now);
Console.WriteLine("\nInfo From Server\n----------------------");
Console.WriteLine("StandardError: " + info.StandardError);
Console.WriteLine("\n----------------------");
Console.WriteLine("StandardOut: " + info.StandardOut);
Console.WriteLine("\n----------------------");
Console.WriteLine("ExitCode: " + info.ExitCode);
}
catch (Exception ex)
{
Console.WriteLine("Error: {0} ", ex.StackTrace.ToString(CultureInfo.InvariantCulture));
}
Console.WriteLine("Press Any Key To Exit..");
Console.ReadLine();
}
}
}

What is the best way to work with PHP and MySQL in C#

I'm building a simple application that requires login. At the moment I'm connecting to my database directly from my C# application however, the college network on which this will be used doesn't allow direct connections to MySQL for some reason. I decided to take a look at how I would do this from PHP instead. I've build a simple login form and tested it and it seems to work. However I have some questions and issues that need sorting out.
How would I first of all stop just anyone typing in the address of the PHP file and getting the data back?
Second, how will I be able to get multiple results back? Let's say I make a PHP file that gets all of the user's files and stores them in the C# application, how do I actually parse this from the PHP file?
Here is an example of a login.php file I would have on the server:
<?php
include("connect.php");
$username = mysql_escape_string($_GET['username']);
$password = mysql_escape_string($_GET['password']);
$squery = mysql_query("SELECT * FROM users WHERE username='$username'");
$query = mysql_fetch_array($squery);
$rowcount = mysql_num_rows($squery);
if($rowcount == 1)
{
if($password != $query['password'])
echo'Password errata';
else
echo 'Login avvenuto';
}
else
echo 'Account non registrato';
?>
And here is the code I'd use on C# to access the PHP file:
string Reply = new WebClient().DownloadString("http://127.0.0.1/ClipCloud.Service/account_auth/login.php?username=" + textBox1.Text + "&password=" + textBox2.Text);
switch (Reply.ToLower())
{
case "account non registrato":
{
MessageBox.Show("Account not registered!");
break;
}
case "password errata":
{
MessageBox.Show("Password error!");
break;
}
case "login avvenuto":
{
MessageBox.Show("Login happened!");
break;
}
default:
{
MessageBox.Show("Error with the remote server, please let try again later!");
break;
}
}
Sorry if this question is a bit confusing, I basically just need to know how to correctly manipulate a database with PHP using C# with correct security in place.
You can get C# communicating with PHP by implementing a simple JSON API Server.
Conside the following : http://yoursite.com/api_server.php
api_server.php
<?php
// Load Request
$api_method = isset($_POST['api_method']) ? $_POST['api_method'] : '';
$api_data = isset($_POST['api_data']) ? $_POST['api_data'] : '';
// Validate Request
if (empty($api_method) || empty($api_data)) {
API_Response(true, 'Invalid Request');
}
if (!function_exists($api_method)) {
API_Response(true, 'API Method Not Implemented');
}
// Call API Method
call_user_func($api_method, $api_data);
/* Helper Function */
function API_Response($isError, $errorMessage, $responseData = '')
{
exit(json_encode(array(
'IsError' => $isError,
'ErrorMessage' => $errorMessage,
'ResponseData' => $responseData
)));
}
/* API Methods */
function loginUser($api_data)
{
// Decode Login Data
$login_data = json_decode($api_data);
// Dummy Check
if ($login_data->username == 'test' &&
$login_data->password == '1234')
{
// Success
API_Response(false, '', 'SUCCESS');
}
else
{
// Error
API_Response(true, 'Invalid username and/or password.');
}
}
?>
Then you communicate with it via C# like this, making POST Requests:
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["api_method"] = "loginUser";
data["api_data"] = "{ \"username\":\"test\", \"password\":\"1234\" }";
var responseBytes = wb.UploadValues(
"http://yoursite.com/api_server.php", "POST", data);
string responseString = Encoding.Default.GetString(responseBytes);
}
Here, the responseString from the API server will the json string. To decode this, you can use this: http://james.newtonking.com/json
Here's a fully working example of how everything is put together in the C# app using a simple console app:
Note how I am generating json string (for api_data) via the json library, instead of manually typing it.
using System;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using Newtonsoft.Json;
namespace TemplateFive
{
public class API_Response
{
public bool IsError { get; set; }
public string ErrorMessage { get; set; }
public string ResponseData { get; set; }
}
public class Login_Request
{
public string username { get; set; }
public string password { get; set; }
}
class Program
{
static void Main(string[] args)
{
// request params
string apiUrl = "https://yoursite.com/api_server.php";
string apiMethod = "loginUser";
Login_Request myLogin_Request = new Login_Request()
{
username = "test",
password = "1234"
};
// make http post request
string response = Http.Post(apiUrl, new NameValueCollection()
{
{ "api_method", apiMethod },
{ "api_data", JsonConvert.SerializeObject(myLogin_Request) }
});
// decode json string to dto object
API_Response r =
JsonConvert.DeserializeObject<API_Response>(response);
// check response
if (!r.IsError && r.ResponseData == "SUCCESS")
{
Console.WriteLine("login success");
}
else
{
Console.WriteLine("login error, reason is: {0}",
r.ErrorMessage);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
public static class Http
{
public static String Post(string uri, NameValueCollection pairs)
{
byte[] response = null;
using (WebClient client = new WebClient())
{
response = client.UploadValues(uri, pairs);
}
return Encoding.Default.GetString(response);
}
}
}
finally, to secure the whole thing, run your site under SSL, so you'd access the api server via this URL: https://yoursite.com/api_server.php
Here's me testing the API server locally using a RESTClient pluggin on firefox.
Success Scenario: http://i.imgur.com/sw5yxvE.png
Error Scenario: http://i.imgur.com/HHmHlWX.png
The Solution is: remove the BOM :-)
static class Http
{
public static String Post(string uri, NameValueCollection pairs)
{
byte[] response = null;
using (WebClient client = new WebClient())
{
response = client.UploadValues(uri, pairs);
}
string ret = Encoding.UTF8.GetString(response);
ret = ret.Trim(new char[] { '\uFEFF', '\u200B' });//removes the BOM
return ret;
}
}

Categories