How do you use the public access token with Unity webrequest? - c#

I'm trying to get a JSON file from a website and I either get a 403 forbidden or a lot of HTML that doesn't include any useful data.
I might be doing it completely wrong because I have little to no understanding of WebRequest/networking and HTML.
public Text debugText;
public string websiteUrl;
IEnumerator GetRequest(string uri)
{
//UnityWebRequest.Get(uri)
var dictionary = new Dictionary<string, string>();
dictionary.Add("Authorization", "Bearer");
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Authorization", "Bearer yQrAQWWxTZ2jyKg9vLOU ");
yield return webRequest.SendWebRequest();
string[] pages = uri.Split('/');
int page = pages.Length - 1;
Debug.Log("Headers: " + webRequest.GetResponseHeaders());
if (webRequest.isNetworkError)
{
Debug.Log(pages[page] + ": Error: " + webRequest.error);
}
else
{
string jsonResult = webRequest.downloadHandler.text;
Debug.Log(jsonResult);
//Debug.Log(jsonResult);
debugText.text = jsonResult;
}
}
}

Related

How do I send a two string Dictionary to a server?

I'm new to Unity (and c#, along with PHP) and have been tasked with getting some old c# and PHP code working. The code below is supposed to send the dictionary (formData) to the PHP server that then converts it to json. The code is below:
...
//This code runs for each file that is uploaded, the file is a list of strings and integers.
Dictionary<string, string> formData = new Dictionary<string, string>();
using (StreamReader sr = file.OpenText())
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
string[] data = s.Split(';');
uploadResultText.setText(file.Name + ":" + data[0] + " " + data[0]);
if (data[1] == "") data[1] = " ";
formData[data[0]] = data[1];
}
}
UnityWebRequest uploadRequest = UnityWebRequest.Post(serverBaseURL, formData);
currentUploadRequest = uploadRequest;
yield return uploadRequest.SendWebRequest();
...
If this code is working, how will I need to receive it server-side?
It turns out it was a server side error, the code above should work.
The server was returning http error 500 because it was requesting data that did not exist (caused by switching names on the app side, but not the server side).
The server side code uses $_POST to reference the incoming data, and a sample can be seen below.
if (isSet($_POST["App"])) {
$dataArray = array();
foreach($expectedFormInputCommon as $input) {
if (isSet($_POST[$input])) {
if (seralizeString($_POST[$input]) !== false) {
$dataArray[$input] = seralizeString($_POST[$input]);
} else {
http_response_code(400);
exit;
}
} else {
http_response_code(400);
$_POST[$input] = "empty";
exit;
}
}
}
After you Yield Return you can access the result:
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
// Error
}
else
{
var result = UnityWebRequest.Result;
}

Cannot Shorten Long Dynamic Link

I want to generate a shorten dynamic link from a long dynamic link
I followed the steps shown here: https://firebase.google.com/docs/dynamic-links/unity/create
yet I get the same long URL when shortening process is completed.
public void CreateInviteLink()
{
var components = new Firebase.DynamicLinks.DynamicLinkComponents(
// The base Link.
new System.Uri("https://myapp.com"),
// The dynamic link URI prefix.
"https://myapp.page.link")
{
IOSParameters = new Firebase.DynamicLinks.IOSParameters("com.myapp.myapp"),
AndroidParameters = new Firebase.DynamicLinks.AndroidParameters("com.myapp.myApp"),
};
Debug.Log("Long Dynamic Link: " + components.LongDynamicLink);
var options = new Firebase.DynamicLinks.DynamicLinkOptions
{
PathLength = DynamicLinkPathLength.Unguessable
};
Firebase.DynamicLinks.DynamicLinks.GetShortLinkAsync(components, options).ContinueWith(task => {
if (task.IsCanceled)
{
Debug.LogError("GetShortLinkAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("GetShortLinkAsync encountered an error: " + task.Exception);
return;
}
// Short Link has been created.
Firebase.DynamicLinks.ShortDynamicLink link = task.Result;
Debug.LogFormat("Generated Short Dynamic Link: {0}", link.Url);
text.text = link.Url.ToString();
var warnings = new System.Collections.Generic.List<string>(link.Warnings);
if (warnings.Count > 0)
{
// Debug logging for warnings generating the short link.
}
});
}
result Logs
Long Dynamic Link: https://myapp.page.link/?afl=&amv=0&apn=com.myapp.myApp&ibi=com.myapp.myapp&ifl=&ipfl=&link=https://myapp.com
UnityEngine.Debug:Log(Object)
Generated Short Dynamic Link: https://myapp.page.link/?afl=&amv=0&apn=com.myapp.myApp&ibi=com.myapp.myapp&ifl=&ipfl=&link=https://myapp.com
UnityEngine.Debug:LogFormat(String, Object[])
EDIT:
How I did it:
You can't shorten dynamic links with Firebase SDK in the editor but I used REST API and UnityWebRequest for testing short links inside the editor and it worked
IEnumerator HTTPRequestShortLink(string longDynamicLink)
{
WWWForm form = new WWWForm();
form.AddField("longDynamicLink", longDynamicLink);
// trigger of function is HTTP request. this link is the trigger for that func
UnityWebRequest www = UnityWebRequest.Post("https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=[YOUR_API_KEY]", form);
//for getting response
www.downloadHandler = new DownloadHandlerBuffer();
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete! " + www.GetResponseHeader("response"));
string responseText = www.downloadHandler.text;
// Parse returned json
parsedJsonObject obj = parsedJsonObject.CreateFromJSON(responseText);
Debug.Log("shortLink: " + obj.shortLink);
Debug.Log("HTTP Response text: " + responseText);
}
}

Consuming webapi with two parameters

Hello I am learning WebApi and got this problem. Hours of search didn't yield any solution.
I am trying to call an api passing two parameters just for testing purposes. The one on which I am getting 404 error is GetBalance(param1, param2). I have another function exposed by the Api called GetOffice(param1) with one parameter which returns 200. The 404 I am getting is for the two parameter function.
public void GetBalance(string accountNumber,int officeId)
{
using (var client = new WebClient())
{
client.Headers.Add("Content-Type:application/json");
client.Headers.Add("Accept:application/json");
client.Headers.Add("API_KEY","1234CHECK");
var result = client.DownloadString("http://localhost/api/Accounts/GetBalance/" + accountNumber + officeId ); //URI
Console.WriteLine(Environment.NewLine + result);
}
}
static void Main(string[] args)
{
ConsumeApiSync objSync = new ConsumeApiSync();
objSync.GetBalance("01-13-00000595", 1);
}
Route
RouteTable.Routes.MapHttpRoute("OfficeApi", "api/{controller}/{action}/{accountNumber}/{officeId}");
I get 404 not found error. What must be wrong? Help Appreciated. Thanks
use a view model on your Web Api controller that contains both properties. So instead of:
public HttpresponseMessage GetBalance(string accountNumber,int officeId)
{
...
}
use:
public HttpresponseMessage Post(ViewModelName model)
{
...
}
var uri = string.Concat("http://localhost/api/Accounts/GetBalance",model);
Seems like your request URI is not correct.
var uri = string.Concat("http://localhost/api/Accounts/GetBalance/", accountNumber, "/", officeId);
Try the following code.
public void GetBalance(string accountNumber,int officeId)
{
using (var client = new WebClient())
{
client.Headers.Add("Content-Type:application/json");
client.Headers.Add("Accept:application/json");
client.Headers.Add("API_KEY","1234CHECK");
var uri = string.Concat("http://localhost/api/Accounts/GetBalance/", accountNumber, "/", officeId);
var result = client.DownloadString(uri); //URI
Console.WriteLine(Environment.NewLine + result);
}
}
static void Main(string[] args)
{
ConsumeApiSync objSync = new ConsumeApiSync();
objSync.GetBalance("01-13-00000595", 1);
}

C# Web API method returns 403 Forbidden

Solved!!! - See last edit.
In my MVC app I make calls out to a Web API service with HMAC Authentication Filterign. My Get (GetMultipleItemsRequest) works, but my Post does not. If I turn off HMAC authentication filtering all of them work. I'm not sure why the POSTS do not work, but the GETs do.
I make the GET call from my code like this (this one works):
var productsClient = new RestClient<Role>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
var getManyResult = productsClient.GetMultipleItemsRequest("api/Role").Result;
I make the POST call from my code like this (this one only works when I turn off HMAC):
private RestClient<Profile> profileClient = new RestClient<Profile>(System.Configuration.ConfigurationManager.AppSettings["WebApiUrl"],
"xxxxxxxxxxxxxxx", true);
[HttpPost]
public ActionResult ProfileImport(IEnumerable<HttpPostedFileBase> files)
{
//...
var postResult = profileClient.PostRequest("api/Profile", newProfile).Result;
}
My RestClient builds like this:
public class RestClient<T> where T : class
{
//...
private void SetupClient(HttpClient client, string methodName, string apiUrl, T content = null)
{
const string secretTokenName = "SecretToken";
client.BaseAddress = new Uri(_baseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (_hmacSecret)
{
client.DefaultRequestHeaders.Date = DateTime.UtcNow;
var datePart = client.DefaultRequestHeaders.Date.Value.UtcDateTime.ToString(CultureInfo.InvariantCulture);
var fullUri = _baseAddress + apiUrl;
var contentMD5 = "";
if (content != null)
{
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json); // <--- Javascript serialized version is hashed
}
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
var hmac = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
client.DefaultRequestHeaders.Add(secretTokenName, hmac);
}
else if (!string.IsNullOrWhiteSpace(_sharedSecretName))
{
var sharedSecretValue = ConfigurationManager.AppSettings[_sharedSecretName];
client.DefaultRequestHeaders.Add(secretTokenName, sharedSecretValue);
}
}
public async Task<T[]> GetMultipleItemsRequest(string apiUrl)
{
T[] result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "GET", apiUrl);
var response = await client.GetAsync(apiUrl).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T[]>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
public async Task<T> PostRequest(string apiUrl, T postObject)
{
T result = null;
try
{
using (var client = new HttpClient())
{
SetupClient(client, "POST", apiUrl, postObject);
var response = await client.PostAsync(apiUrl, postObject, new JsonMediaTypeFormatter()).ConfigureAwait(false); //<--- not javascript formatted
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync().ContinueWith((Task<string> x) =>
{
if (x.IsFaulted)
throw x.Exception;
result = JsonConvert.DeserializeObject<T>(x.Result);
});
}
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("401 (Unauthorized)"))
{
}
else if (exception.Message.Contains("403 (Forbidden)"))
{
}
}
catch (Exception)
{
}
return result;
}
//...
}
My Web API Controller is defined like this:
[SecretAuthenticationFilter(SharedSecretName = "xxxxxxxxxxxxxxx", HmacSecret = true)]
public class ProfileController : ApiController
{
[HttpPost]
[ResponseType(typeof(Profile))]
public IHttpActionResult PostProfile(Profile Profile)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
GuidValue = Guid.NewGuid();
Resource res = new Resource();
res.ResourceId = GuidValue;
var data23 = Resourceservices.Insert(res);
Profile.ProfileId = data23.ResourceId;
_profileservices.Insert(Profile);
return CreatedAtRoute("DefaultApi", new { id = Profile.ProfileId }, Profile);
}
}
Here is some of what SecretAuthenticationFilter does:
//now try to read the content as string
string content = actionContext.Request.Content.ReadAsStringAsync().Result;
var contentMD5 = content == "" ? "" : Hashing.GetHashMD5OfString(content); //<-- Hashing the non-JavaScriptSerialized
var datePart = "";
var requestDate = DateTime.Now.AddDays(-2);
if (actionContext.Request.Headers.Date != null)
{
requestDate = actionContext.Request.Headers.Date.Value.UtcDateTime;
datePart = requestDate.ToString(CultureInfo.InvariantCulture);
}
var methodName = actionContext.Request.Method.Method;
var fullUri = actionContext.Request.RequestUri.ToString();
var messageRepresentation =
methodName + "\n" +
contentMD5 + "\n" +
datePart + "\n" +
fullUri;
var expectedValue = Hashing.GetHashHMACSHA256OfString(messageRepresentation, sharedSecretValue);
// Are the hmacs the same, and have we received it within +/- 5 mins (sending and
// receiving servers may not have exactly the same time)
if (messageSecretValue == expectedValue
&& requestDate > DateTime.UtcNow.AddMinutes(-5)
&& requestDate < DateTime.UtcNow.AddMinutes(5))
goodRequest = true;
Any idea why HMAC doesn't work for the POST?
EDIT:
When SecretAuthenticationFilter tries to compare the HMAC sent, with what it thinks the HMAC should be they don't match. The reason is the MD5Hash of the content doesn't match the MD5Hash of the received content. The RestClient hashes the content using a JavaScriptSerializer.Serialized version of the content, but then the PostRequest passes the object as JsonMediaTypeFormatted.
These two types don't get formatted the same. For instance, the JavaScriptSerializer give's us dates like this:
\"EnteredDate\":\"\/Date(1434642998639)\/\"
The passed content has dates like this:
\"EnteredDate\":\"2015-06-18T11:56:38.6390407-04:00\"
I guess I need the hash to use the same data that's passed, so the Filter on the other end can confirm it correctly. Thoughts?
EDIT:
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the sent content (formatted via JSON) will match the hashed content.
I was not the person who wrote this code originally. :)
Found the answer, I needed to change the SetupClient code from using this line:
var json = new JavaScriptSerializer().Serialize(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
To using this:
var json = JsonConvert.SerializeObject(content);
contentMD5 = Hashing.GetHashMD5OfString(json);
Now the content used for the hash will be formatted as JSON and will match the sent content (which is also formatted via JSON).

Force re-authentication using OAuthWebSecurity with Facebook

My website is using facebook as it's oauth provider. Users will be able to buy things through my site so I want to force them to authenticate even if they already have an active session with facebook.
I found this link in facebook's api documentation that discusses reauthentication but I can't get it to work with my mvc app. Anyone know if this is possible?
var extra = new Dictionary<string, object>();
extra.Add("auth_type", "reauthenticate");
OAuthWebSecurity.RegisterFacebookClient(
appId: "**********",
appSecret: "**********************",
displayName: "",
extraData: extra);
Found the solution. I had to create my own client instead of using the default one provided by OAuthWebSecurity.RegisterFacebookClient
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
namespace Namespace.Helpers
{
public class MyFacebookClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{
private const string AuthorizationEP = "https://www.facebook.com/dialog/oauth";
private const string TokenEP = "https://graph.facebook.com/oauth/access_token";
private readonly string _appId;
private readonly string _appSecret;
public MyFacebookClient(string appId, string appSecret)
: base("facebook")
{
this._appId = appId;
this._appSecret = appSecret;
}
protected override Uri GetServiceLoginUrl(Uri returnUrl)
{
return new Uri(
AuthorizationEP
+ "?client_id=" + this._appId
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&scope=email,user_about_me"
+ "&display=page"
+ "&auth_type=reauthenticate"
);
}
protected override IDictionary<string, string> GetUserData(string accessToken)
{
WebClient client = new WebClient();
string content = client.DownloadString(
"https://graph.facebook.com/me?access_token=" + accessToken
);
dynamic data = Json.Decode(content);
return new Dictionary<string, string> {
{
"id",
data.id
},
{
"name",
data.name
},
{
"photo",
"https://graph.facebook.com/" + data.id + "/picture"
},
{
"email",
data.email
}
};
}
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
NameValueCollection nameValueCollection = HttpUtility.ParseQueryString(content);
if (nameValueCollection != null)
{
string result = nameValueCollection["access_token"];
return result;
}
return null;
}
}
}
and then in AuthConfig.cs...
OAuthWebSecurity.RegisterClient(
new MyFacebookClient(
appId: "xxxxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxx"),
"facebook", null
);
As a note to those that happen upon this if your Facebook Authentication stopped working when v2.3 became the lowest version you have access to (non versioned calls get the lowest version an app has access to). The API now returns JSON and not name value pairs so you have to update the QueryAccessToken method shown above by #Ben Tidman
Below is the updated method
protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
{
WebClient client = new WebClient();
string content = client.DownloadString(
TokenEP
+ "?client_id=" + this._appId
+ "&client_secret=" + this._appSecret
+ "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString())
+ "&code=" + authorizationCode
);
dynamic json = System.Web.Helpers.Json.Decode(content);
if (json != null)
{
string result = json.access_token;
return result;
}
return null;
}
There's one issue in using the MyFacebookClient implementation.
Probably someone tryin to implement it came across the error:
The given key was not present in the dictionary
attempting to call the ExternalLoginCallback method in ActionController.
The error raises when the method
OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
is called.
In order to get it working the method VerifyAuthentication has to be overridden.
In particular the
public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl);
overload of the abstract class OAuth2Client.
If you use the following:
public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(QueryAccessToken(returnPageUrl, code));
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
AuthenticationResult result = new AuthenticationResult(true, ProviderName, userData["id"], userData["name"], userData);
userData.Remove("id");
userData.Remove("name");
return result;
}
}
finally you get the method called in the right way and no excpetion is thrown.

Categories

Resources