I am making an app with Xamarin Forms and Azure. I am authenticating with Google and Facebook.
When requesting user's data, I use a HttpClient and make a request. Requesting from Google works fine but Facebook always returns null.
I have tested the Url in Postman with the same access token, it returns the correct data.
The Url is:
https://graph.facebook.com/v2.8/me/?fields=name,picture,locale,link,devices,email,first_name,last_name&access_token=
This returns:
{
"name": "Reece Russell",
"picture": {
"data": {
"height": 50,
"is_silhouette": false,
"url": "https://lookaside.facebook.com/platform/profilepic/?asid=2139651619601242&height=50&width=50&ext=1526558014&hash=AeSKoNrLS6O4UmMV",
"width": 50
}
},
"locale": "en_US",
"link": "https://www.facebook.com/app_scoped_user_id/YXNpZADpBWEhTLXBnRko4LWlBRUVzc0RBZAXhsc3R1eXpNNGZAxNmJMaXhWT013WTFCMVNnOFpLNE1jblh3dWJjLUwyRDhLQ0QyOHh6NmxnVGNyX2REMU9vUzNYTHFKNjlfN0J5R0lVbkV5ZA1V4aDRBZAVNDMnpwS0EZD/",
"devices": [
{
"hardware": "iPhone",
"os": "iOS"
}
],
"first_name": "Reece",
"last_name": "Russell",
"id": "2139651619601242"
}
But when making this request from Xamarin Forms using a HttpClient it always returns null.
The code I am using is below. I have also tried many ways of making the request including making the request from my server, which gets the correct data from FaceBook but returns null when I request from my server it still returns null.
using (var client = new HttpClient(new NativeMessageHandler()))
{
string url = "https://graph.facebook.com/v2.8/me/?fields=name,picture,locale,link,devices,email,first_name,last_name&access_token=" + Settings.AccessToken;
string json = await client.GetStringAsync(url);
FacebookUserProfile profile = JsonConvert.DeserializeObject<FacebookUserProfile>(json);
profile.PhotoUrl = profile.Picture.Data.Url;
profile.Name = profile.FirstName + " " + profile.LastName;
return profile;
}
Related
After 3 days I am finished configurations pub/sub push notification from my android app, now my backend server catch every subscription notification made from android app, but now I'm stuck in this stage, all I need is how read full subscription details like(Who make this payment, expire date, ...etc)
I don't know how to do it and what necessary steps to make it happen.
this is what notification look like from my backend server:
{
"message" :
{
"attributes":{ "key": null },
"data": "JSON_CODE",
"messageId": "6542662753109422",
"message_id": "6546652753109422",
"publishTime": "2023-01-01T15:53:42.962Z",
"publish_time": "2023-01-01T15:53:42.962Z"
},
"subscription": "projects/{api_id}/subscriptions/{service_name}-sub"
}
message.data (JSON_CODE) decoded into this:
{
"version": "1.0",
"packageName": "com.expamle.android",
"eventTimeMillis": "1672588422635",
"subscriptionNotification":
{
"version": "1.0",
"notificationType": 4,
"purchaseToken": "lkgkfeofbmfnnalianjdppej.AO-J1OxNcztkkzntpvQk4nttaBiqHJ5WMD58tb_KxCdsyPooE_QPvqXdtnoEpqD0t96j5V4lxol3_FfpuRNDvBeRTYKo_ixJtw",
"subscriptionId": "12_month_plan"
}
}
I have a keys.json file.
And I made all permission required in my pub/sub service accounts.
How to read the full subscription details?
I solved it, to any new coming users this is how to solve it .
first I've used simple .NET library "Google.Apis.Auth.OAuth2" to get access token to any query request.
by this simple code I can get token using key.json i've downloaded from my google cloud console from "**https://console.cloud.google.com/iam-admin/serviceaccounts**" and by manage keys you can generate json secret key.
Auth:
string keyFile = HttpContext.Server.MapPath("~/bin/key.json");
var credential = GoogleCredential.FromFile(keyFile).CreateScoped(new[] { "https://www.googleapis.com/auth/androidpublisher" });
string token = await credential.UnderlyingCredential.GetAccessTokenForRequestAsync();
}
Now you can request google query GET url to get full purchased subscription info .
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{PACKAGE_NAME}/purchases/subscriptionsv2/tokens/{purchaseTokenComesWithMessage.data}
any requests must be included with Authorization Bearer accessToken we'ave generated from Auth. section above.
the result:
{
"kind": "androidpublisher#subscriptionPurchaseV2",
"startTime": "2023-01-03T14:56:24.940Z",
"regionCode": "US",
"subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
"latestOrderId": "GPA.3394-8372-7793-03673",
"testPurchase": {},
"acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
"externalAccountIdentifiers": {
"obfuscatedExternalAccountId": "92d8f330-8d2a-45ab-b6a1-6720b3d15999"
},
"lineItems": [
{
"productId": "12_month_plan",
"expiryTime": "2023-01-03T15:28:21.928Z",
"autoRenewingPlan": {
"autoRenewEnabled": true
},
"offerDetails": {
"basePlanId": "360day",
"offerTags": [
"tools"
]
}
}
] }
Thanks
I'm trying to use the Create Datasource REST endpoint from Microsoft's Power BI API, but I'm getting a 400 error when I send the encrypted string of my Windows credentials. My API is written in nodejs, so I'm using node-rsa to replicate the functionality from the C# solution in their docs.
I generated an encrypted string of my credentials with this C# code and it works fine and returns 201 for creating the Datasource:
var credentials = new WindowsCredentials(username: "myusername", password: "mypassword");
var publicKey = new GatewayPublicKey("<exponent>", "<modulus>");
var credentialsEncryptor = new AsymmetricKeyEncryptor(publicKey);
var credentialDetails = new CredentialDetails(credentials, PrivacyLevel.Organizational, EncryptedConnection.Encrypted, credentialsEncryptor);
This is the encrypted string from the Credentials prop of the credentialDetails object in the code above: oavtaHCi2Nkn0euRQxtRvZ2aDPnLA5/T1B6YHHeQzfBDXLJsU4JOx3ZOm31nEWqPRUh2DLARKI0y+C106wgOhWKgxI4nIz5jmJCQqPWRzOYlJlvdPQCOrTtuvIZb3IRy2OAhBfKOcOA8xqHIkKsqvTX1u4LUgfNuwxmU16FB/d3mzZOtc3+ld2MUp15HEPBCen6qTOkdEOwOm2AqxSidx46xoahLrqjkMtLZqLimooVGeTmuXm7bwoRQa1VKUwPaAuKcBBfAuRJulXyeSOMkwKz90pA30y8g7aB2NvT+92SzzjpuHcs1bzPrbPR1J+WXSRTN7xzlCOyQmC5YiQKn6A==AADPIC5L5rjd7WWThtCNAPunINW330ka1ts53SsBkaelIrZ8f9msgE4JjliaeDM91Qq3KDqm1DUjLw6Fg92LzZhMkjcwdEBued/piCdvIpP0eA9rhX0kjWWaMzQF6T3fHr798OkL7yMPEGi+m7Z3cOz22HP2Ot1ORxf6RH/JUNJmLEepEYCRVVubmKL04IyEtx8dShtSG+upeOaBOaD/GOS5
This is my nodejs solution using node-rsa. Having trouble understanding why the encrypted string from the C# code above works, but this one doesn't.
const nodersa = require('node-rsa');
const credentials =
'{"credentialData":[{"name":"username", "value":"myusername"},{"name":"password", "value":"mypassword"}]}';
const exponentString = '<exponent>';
const modulusString = '<modulus>';
const key = new nodersa();
key.setOptions({
encryptionScheme: 'pkcs1_oaep',
});
const pubKey = key.importKey({
n: Buffer.from(modulusString, 'base64'),
e: Buffer.from(exponentString, 'base64'),
});
const encrypted = pubKey.encrypt(credentials, 'base64');
This is the encrypted string from this code above: UCLIIGF8u6HMdlLAwVeZcM0iLks5YLEWhRfpywnNBrAdSoPlP8/vRqe4knMCnAFiSimHiX8fu/CQlAP0b98Xlzx6sHnW4sQHV5KgUErLQfBP5i+5LGj7lBDB0/nsuf2hLDSrXXUb3F+XXv1mlUTgNZzwanizUuRcqYRxcdMaYOjI1vCaQbW++kHXMgOQPjMBj8hrJ1gW1WznS3zCYy6v8oUwEzJtp2aQEP8Pycvx5KwjVdy9KxkB675+TfddauMlz0B+EpQjC1Z+k87uiuFpGZxwA5FAi+4r8ztZ+9/su+8eieCSBK/8ZokAUNcrLmU0sPuDewaojW1MBdxvJiv7YA==
This is the error that Power BI API returns using string encrypted via node solution. Everything else about my request is identical except the encrypted creds, so I'm confident that that is the issue.
{
"error": {
"code": "DM_GWPipeline_UnknownError",
"pbi.error": {
"code": "DM_GWPipeline_UnknownError",
"parameters": {},
"details": [
{
"code": "DM_ErrorDetailNameCode_UnderlyingErrorMessage",
"detail": {
"type": 1,
"value": "The parameter is incorrect.\r\n"
}
},
{
"code": "DM_ErrorDetailNameCode_UnderlyingHResult",
"detail": {
"type": 1,
"value": "-2146893785"
}
}
],
"exceptionCulprit": 1
}
}
}
You can fix this by upgrading to the latest RSA encryption that supports 2048 bits.
I'm trying to send an AJAX PATCH request to a Web API method and have the patched object recognised by Marvin.JsonPatch.
So far, everything I've sent to the server has resulted in an empty request being received.
The Web API controller method looks like this:
public async Task<IHttpActionResult> Update(long orderId, JsonPatchDocument<Order> patchedOrder)
I'm POSTing using an HttpClient like this (can't use async in this application)...
var patchDoc = new JsonPatchDocument<Order>();
patchDoc.Replace(e => e.Price, newPrice);
patchDoc.Replace(e => e.WordCount, newWordCount);
var request = new HttpRequestMessage(new HttpMethod("PATCH"), uri)
{
Content = new StringContent(JsonConvert.SerializeObject(patchDoc),
System.Text.Encoding.Unicode,
"application/json")
};
HttpResponseMessage response;
using (var client = new HttpClient(...))
{
response = client.SendAsync(request).GetAwaiter().GetResult();
}
But when the controller is it, the patchedOrder argument is null.
While debugging on the controller I've also tried
var s = await Request.Content.ReadAsStringAsync();
But this returns an empty string - can anyone explain why?
UPDATE:
This is what the contents of the JsonPatch document look like when passed in to the HttpClient...
{
"Operations": [{
"OperationType": 2,
"value": 138.7,
"path": "/price",
"op": "replace"
},
{
"OperationType": 2,
"value": 1320,
"path": "/wordcount",
"op": "replace"
}],
"ContractResolver": {
"DynamicCodeGeneration": true,
"DefaultMembersSearchFlags": 20,
"SerializeCompilerGeneratedMembers": false,
"IgnoreSerializableInterface": false,
"IgnoreSerializableAttribute": true,
"NamingStrategy": null
},
"CaseTransformType": 0
}
Somewhere during the development of Marvin.JsonPatch, the JsonPatchDocument<T> was annotated with an attribute that applied a custom JSON serializer:
[JsonConverter(typeof(JsonPatchDocumentConverter))]
This converter enables you to call JsonConvert.SerializeObject() on such a patch document and actually generate a patch document, as opposed to a representation of the JsonPatchDocument<T> CLR object.
Upgrade Marvin.JsonPatch and Newtonsoft.Json to the latest verison, and serialization should succeed.
I am using Azure Data factory HTTP connector as a linked service to read data from the REST endpoint using basic authentication.
{
"name": "LS_HTTP",
"properties": {
"hubName": "Hub name",
"type": "Http",
"typeProperties": {
"url": "http://*****.azurewebsites.net",
"authenticationType": "Basic",
"gatewayName": "",
"encryptedCredential": "",
"username": "test",
"password": "**********",
"enableServerCertificateValidation": true
}
}
}
Following code snippet is written to fetch the username and password from the headerin my web API
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
if (username == "test" && password == "****")
{
await _next.Invoke(context);
}
else
{
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
else
{
// no authorization header
context.Response.StatusCode = 401; //Unauthorized
return;
}
When I run Azure data factory pipeline with this setup, I am not able to get username and password from the request header in the web api, basically Authorization header itself is null.
Help me to fetch the username and password passed from my ADF connector service in my web API.
Looking at your definition, you're working with Data Factory v1. I see you configure some properties that are not required for basic authentication.
encryptedCredential, is not required. The documentation states:
Description: Encrypted credential to access the HTTP endpoint. Auto-generated when you configure the authentication information in copy wizard or the ClickOnce popup dialog.
Required: No. Apply only when copying data from an on-premises HTTP server.
gatewayName, is not required since you're not using an on-premises HTTP server
Description: Name of the Data Management Gateway to connect to an on-premises HTTP source.
enableServerCertificateValidation, already defaults to true
Documentation gives this basic example:
{
"name": "HttpLinkedService",
"properties":
{
"type": "Http",
"typeProperties":
{
"authenticationType": "basic",
"url" : "https://en.wikipedia.org/wiki/",
"userName": "user name",
"password": "password"
}
}
}
Short version
Logged in as a Facebook user, I use my oAuth token to assume an IAM role on AWS. It returns what looks to be valid credentials, e.g. there is an AccessKeyId, SecretAccessKey that are similar length to our master keys.
When I try to use these credentials to access a DynamoDB table, I get one of two exceptions:
"The remote server returned an error: (400) Bad Request."; or
"The security token included in the request is invalid.".
I'm using the AWS C# SDK version 1.5.25.0
Long version
As I said above, I'm trying to access a DynamoDB table on AWS using credentials supplied by AmazonSecurityTokenServiceClient authorized by Facebook Identity as described in this AWS guide.
The policy for the IAM role that I've created is:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Sid": "Stmt1372350145000",
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}
How I get the credentials:
The user logs in with Facebook using oAuth.
Using the access token, I assume the IAM role using a AmazonSecurityTokenServiceClient.AssumeRoleWithWebIdentity with a request.
This returns what looks like to be valid credentials, e.g. a AccessKeyId, SecretAccessKey that are similar length to our master keys.
using (var tokenServiceClient = new AmazonSecurityTokenServiceClient(RegionEndpoint.USEast1))
{
var request = new AssumeRoleWithWebIdentityRequest
{
DurationSeconds = (int)TimeSpan.FromHours(1).TotalSeconds,
ProviderId = "graph.facebook.com",
RoleArn = "arn:aws:iam::193557284355:role/Facebook-OAuth",
RoleSessionName = result.id,
WebIdentityToken = FBClient.AccessToken
};
var response = tokenServiceClient.AssumeRoleWithWebIdentity(request);
AWSAssumedRoleUser = response.AssumeRoleWithWebIdentityResult.AssumedRoleUser;
AWSCredentials = response.AssumeRoleWithWebIdentityResult.Credentials;
}
How I use these credentials:
Using the returned credentials, I then try to access a AWS DynamoDB resource.
using (var client = new AmazonDynamoDBClient(AWSCredentials.AccessKeyId, AWSCredentials.SecretAccessKey, AWSCredentials.SessionToken, RegionEndpoint.USEast1))
{
var context = new DynamoDBContext(client);
var data = context.Scan<SomeData>();
}
This returns "The remote server returned an error: (400) Bad Request." when trying to Scan the table.
This is where the variation in the exception message is; if I omit the AWSCredentials.SessionToken from the above AmazonDynamoDBClient
using (var client = new AmazonDynamoDBClient(AWSCredentials.AccessKeyId, AWSCredentials.SecretAccessKey, RegionEndpoint.USEast1))
{
var context = new DynamoDBContext(client);
var data = context.Scan<SomeData>();
}
This returns "The security token included in the request is invalid." when trying to Scan the table.
Question
At this point I cannot tell what is wrong, are the credentials invalid or that I'm not passing everything through that is needed to AWS.
Can anyone offer any insight to what is wrong or how I could debug this further?
I cross-posted my question to the AWS forums and received an answer from an Amazon engineer.
https://forums.aws.amazon.com/message.jspa?messageID=465057
DynamoDBContext object invokes DescribeTable on the target table (and caches this data, so for optimal performance you would want to keep the context object around for as long as possible, so this call is only done once per target table). Modify your policy as follows:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:BatchGetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem",
"dynamodb:DescribeTable"
],
"Sid": "Stmt1372350145000",
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}