Hi All
I have been trying now for a bit to get some joy connecting from my Unity Application to the Cloud Target VWS. I have written a class that does (I think) all the required hoops and jumps to properly sign the API requests as documented. I'm obviously doing something wrong since its not working! My code is loosely based on the Java code supplied in the documentation. I'm going to post my code here in the hope that someone else is interested in achieving the same thing and will see what I have missed since I now have my frustration blinkers on.
NOTE: The following class relies on UniWeb on the Unity Asset store (sorry thats a slight barrier to helping me since its $55). If you have that then you can drop this script directly onto a gameobject and fill in your access and private keys, assign a texture and then hit play.
Problems:
- For uploading Targets I get a json response ok, but I can;t get it to authenticate. eg. {"result_code":"AuthenticationFailure","transaction_id":"aa28a843f79546c7b27d222ebbada893"}
- For get requests - the first time I run one I get an empty response from VWS but a status of OK on the web request? Subsequent calls without waiting a period of time result in a "System.Net.Sockets.SocketException: Connection refused" error. Note this may be to do with UniWeb, I'm not sure.
Hopefully someone can help.
Code:
//****************************************************************//
//CloudConnector.cs //Tom Mulder. //4/2/2013 // //This code is loosely based on the java sample code provided in the vuforia VWS documentation. // //NOTE: This class references classes and functions provided by the UniWeb plugin available on the Unity Asset Store. using UnityEngine; using System.Collections; using System; using System.Text; using System.Security.Cryptography; using System.IO; //Simple class to describe a target to submit to the Cloud. public class TargetData { public TargetData(Texture2D tex, int width, string name) { SetImage(tex); this.width = width.ToString(); this.name = name; } public string image = ""; public string width = "500"; public string name = "target_tom"; public bool active_flag = true; public void SetImage(Texture2D tex) { image = System.Convert.ToBase64String(tex.EncodeToPNG()); } } // Delegate template for callback functions passed to CloudConnector.Request public delegate void CloudRequestCallback(HTTP.Response response); //Singleton Cloud Connector Class. public class CloudConnector : MonoBehaviour { private static CloudConnector instance = null; public static CloudConnector Instance {get {return instance;}} public string accessKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Assign Access Key in the inspector public string secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Assign Secret Key in the inspector private const string md5Empty = "d41d8cd98f00b204e9800998ecf8427e"; public string host = "vws.vuforia.com"; // Host public Rect buttonRect = new Rect(0,0,200,50); public Texture2D target; //Assign an RGB24 texture here public string targetName = "testTarget"; public string demoTargetID = "1b439839e42d4d2e8e3d70a82f80afec"; // Start void Start () { if(instance != null) { Destroy(this); return; } instance = this; } //Simple GUI Implementation to fire interaction with VWS void OnGUI() { if(GUI.Button (buttonRect, "Upload New Target")) { if(target) AddTarget(target); else Debug.Log ("Assign an RGB24 image to 'target' in the inspector"); } Rect b2 = buttonRect; b2.y += b2.height; if(GUI.Button (b2, "Get Summary")) GetSummary(); b2.y += b2.height; if(GUI.Button (b2, "Get List")) GetTargetList(); b2.y += b2.height; if(GUI.Button (b2, "Get Target Summary")) GetTargetSummary(demoTargetID); } //Call for adding a target to the cloud public void AddTarget(Texture2D targetTex) { //Create a Target Data Object //Conversion of the imageTarget to Base64 String is handled in the TargetData class. TargetData td = new TargetData(target, 500, "new_target"); //Create the HTTP request - Serialize the TargetData and convert to json bytes with CreateRequestData() HTTP.Request request = new HTTP.Request("POST", "https://" + host + "/targets", CreateRequestData(td)); //Start the Http Request //Headers are Assigned in the Request Coroutine //A callback is also passed to the Request Call to be called with the HTTP Response StartCoroutine(Request(request, AddTargetResponse)); } //Pass in a class object. //Out comes a Byte[] of the json serialisation of the class private byte[] CreateRequestData(object toSerialize) { //Serialize the object referenced string json = HTTP.JsonSerializer.Encode(toSerialize); #if UNITY_EDITOR Debug.Log ("json:" + json); #endif //Encode to byte[] return Encoding.UTF8.GetBytes(json); } //Callback for the AddTarget call public void AddTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } /*public void UpdateTarget(Texture2D targetTex, string targetId) { } public void UpdateTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } public void GetTarget(string targetID) { } public void GetTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif }*/ public void GetTargetList() { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/targets"); StartCoroutine(Request(request, GetTargetListResponse)); } public void GetTargetListResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } /*public void DeleteTarget(string targetID) { } public void DeleteTargetResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif }*/ public void GetSummary() { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/summary"); StartCoroutine(Request(request, GetSummaryResponse)); } public void GetSummaryResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } public void GetTargetSummary(string targetID) { HTTP.Request request = new HTTP.Request("GET", "http://" + host + "/targets/" + targetID); StartCoroutine(Request(request, GetTargetSummaryResponse)); } public void GetTargetSummaryResponse(HTTP.Response response) { #if UNITY_EDITOR Debug.Log (response.Text); Debug.Log (response.message); #endif } //Make a request using an Http Request //The callback will be called once the request is fullfilled successfully private IEnumerator Request(HTTP.Request request, CloudRequestCallback callback) { //Set the headers... SetHeaders(ref request); //Execute the request request.Send(); //Wait until finished while (!request.isDone) yield return null; //Was the request successful? if(request.exception != null) { yield break; } //Success ... maybe ... callback with the response callback(request.response); } //Set the headers for the HTTP request private void SetHeaders(ref HTTP.Request req) { string hash = md5Empty; // Deault to the hash for an empty string //Create a correctly formatted date and time string string dateTimeString = DateTime.UtcNow.ToString("R"); #if UNITY_EDITOR Debug.Log (dateTimeString); #endif //Set the host header req.AddHeader("Host", host); req.AddHeader("Date", dateTimeString); if(req.method.Equals("POST")) { hash = GetMd5Hash(req.bytes); req.AddHeader("Content-Type", "application/json"); } else req.AddHeader("Content-Type", ""); string stringToSign = req.method + "\n" + hash + "\n" + req.GetHeader("Content-Type") + "\n" + req.GetHeader("Date") + "\n" + req.uri; #if UNITY_EDITOR Debug.Log ("String to sign: " + stringToSign); #endif string authorisation = "VWS " + accessKey + ":" + BuildSignature(secretKey, stringToSign); #if UNITY_EDITOR Debug.Log ("Authorization: " + authorisation); #endif req.AddHeader("Authorization", authorisation); } //Build a signature given the private key and a string to sign. private string BuildSignature(string keyString, string stringToSign) { //Convert key and data to byte[] byte[] key = Encoding.UTF8.GetBytes(keyString); byte[] data = Encoding.UTF8.GetBytes(stringToSign); //Create the encryption thingee HMACSHA1 myhmacsha1 = new HMACSHA1(key); myhmacsha1.Initialize(); MemoryStream stream = new MemoryStream(data); //Compute the sha1 hash byte[] hash = myhmacsha1.ComputeHash(stream); //Convert to Base64 string string signature = System.Convert.ToBase64String(hash); #if UNITY_EDITOR Debug.Log ("SIG: " + signature); #endif return signature; } //Get the MD5 hash string for a Byte[] static string GetMd5Hash(byte[] toHash) { byte[] data; using (MD5 md5Hash = MD5.Create()) { data = md5Hash.ComputeHash(toHash); } // compute the hash. // Create a new Stringbuilder to collect the bytes // and create a string. StringBuilder sBuilder = new StringBuilder(); // Loop through each byte of the hashed data // and format each one as a hexadecimal string. for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } // Return the hexadecimal string. string hash = sBuilder.ToString(); #if UNITY_EDITOR Debug.Log ("MD5Hash: " + hash); #endif return hash; } }
VWS Requests in C# from Unity - Authentication
Hi,
currently we only support the Java sample implementation for VWS API, but you could be interested in having a look at this thread, where C# sample code (working) is shared / discussed among other devs:
VWS Requests in C# from Unity - Authentication
Hi, @mulder_ltd.
I just found this amazing post and I am willing to test your code, but before purchasing the UniWeb asset, I just wanted to ask if you know wether your code is working with the latest versions of Vuforia (5.0.5) and Unity (5.2). Could you please give me a hand?
VWS Requests in C# from Unity - Authentication
Hi @alvaroem. I've not tried this on the latest Vuforia sorry. I moved away from using VWS in my application and I haven't used that code since I wrote it way back then so can't give you any guarantees:)
Cheers
Tom
VWS Requests in C# from Unity - Authentication
Thank you, Mulder.
I will keep working on it and update this thread with my conclusions once I test it.
PS. [Totally off-topic, so apologies in advance] I am curious, what AR engine are you working with now? (assuming you are still working with AR)
VWS Requests in C# from Unity - Authentication
Hi, I still use Vuforia! - I just don't access VWS from the app anymore, it makes more sense to have that server based (for what i use it for).
Cheers
Tom
VWS Requests in C# from Unity - Authentication
Thanks AllesandoB - Much appreciated!
Regards
Tom
VWS Requests in C# from Unity - Authentication
You're welcome.
VWS Requests in android from Unity - Authentication
VWS Requests in C# from Unity - Authentication
It means that you have a bad signature. Check that you use exactly the same date both in the header and in the signature, that is usually the most common error.
VWS Requests in C# from Unity - Authentication
Hi,
Would you explain it in briefly, which type of changes I have to do in in
VWS Requests in C# from Unity - Authentication
It's not a copy pate code, but this should give an idea on how to do it.
During the initialization of vuforia client i initialize some header values
Authentication error
Hi,
One thing I want to ask from you, when we don't use vws, I just want to access my own cloud database, would you tell me how can I do it
VWS Requests in C# from Unity - Authentication
The only way to access the cloud database other than the vws is to use the web interface or the target finder on the client.
VWS Requests in C# from Unity - Authentication
Woops posted comment as the wrong user:)
VWS Requests in C# from Unity - Authentication
Ok so the issue with the target uploading part of my script was as simple as this:
Where the StringToSign was built I was using:
Request-Path = vws.vuforia.com/targets
instead of:
Request-Path = /targets
VWS Requests in C# from Unity - Authentication
Sounds great! Glad that you figured out the issue. (And thanks for sharing the udpated code).
VWS Requests in C# from Unity - Authentication
I made a copy code from here, but it couldn't work.
Pls help me ,thx in advance.
VWS Requests in C# from Unity - Authentication
Just a note - It would be helpful in the documentation if the Request-Path was documented - Looks like there was an omission there.
on the page:
VWS Requests in C# from Unity - Authentication
One last thing - the GET requests are not working yet in the code below. I'll update once I have sorted those.
VWS Requests in C# from Unity - Authentication
VWS Requests in C# from Unity - Authentication
Hi, have you checked our VWS Java samples ?
I would invite you to use them to access your Cloud Database; you can find them here:
https://developer.vuforia.com/resources/dev-guide/managing-targets-cloud-database-using-developer-api