Using the Windows Facebook SDK the FBUser class misses information - c#

Using the Windows SDK for Facebook I've managed to log in and set the correct permissions to retrieve some info.
This was done using this code
FBSession sess = FBSession.ActiveSession;
sess.FBAppId = FBAppId;
sess.WinAppId = WinAppId;
// Add permissions
sess.AddPermission("public_profile");
// Do the login
await sess.LoginAsync();
Now the problem is that I can only retrieve a small bit of information from the FBUser and not all the fields that exists within it, for example:
sess.User.Name; // This WORKS
sess.User.Id; // This WORKS
sess.User.FirstName; // DOESN'T WORK
sess.User.LastName; // DOESN'T WORK
sess.User.Locale; // DOESN'T WORK
...

The FBUser associated with the ActiveSession is from the beginning ONLY populated with the data that you get from the initial "did login work"-request (which is a standard request to the /me Uri).
This only includes:
Full Name
Facebook ID
To get more data about the user you need to make use of the Graph API, and include the fields you're interested in as a parameter in the request.
This is one example how it could look like to update the current FBUser with the first_name, last_name and locale of the user.
// This is the current user that we're going to add info to
var currentUser = FBSession.ActiveSession.User;
// Specify that we want the First Name, Last Name and Locale that is connected to the user
PropertySet parameters = new PropertySet();
parameters.Add("fields", "locale,first_name,last_name");
// Set Graph api path to get data about this user
string path = "/me/";
// Create the request to send to the Graph API, also specify the format we're expecting (In this case a json that can be parsed into FBUser)
FBSingleValue sval = new FBSingleValue(path, parameters,
new FBJsonClassFactory(FBUser.FromJson));
// Do the actual request
FBResult fbresult = await sval.Get();
if (fbresult.Succeeded)
{
// Extract the FBUser with new data
var extendedUser = ((FBUser)fbresult.Object);
// Attach the newly fetched data to the current user
currentUser.FirstName = extendedUser.FirstName;
currentUser.LastName = extendedUser.LastName;
currentUser.Locale= extendedUser.Locale;
}
Please note that the sample code only includes a very minimum amount of verification. Since this includes network calls more should be added.

Related

Amazon Pay SDK CompleteCheckoutSession Error

I'm integrating Amazon pay with my website, using the v2 c# SDK from amazon (MVC App), in the sandbox. Setup is all good, I created the keys, return urls, etc.
In my checkout process, I create the CheckoutSession, which is successful. I click on the Amazon Pay button, log in using my test buyer account, and "pay" for the item using their valid test credit card.
Amazon redirects to my return URL with the checkout session id as expected.
When I try to CompleteCheckoutSession, the result is an error back from the Amazon API
error: InvalidCheckoutSessionStatus
message: You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed
I put in a test line of code to retrieve the CheckoutSession to look at it before I try to complete it, and it shows that the current status is "Open", which is the correct status when trying to complete it, so I'm at a loss at why the checkout session status is invalid.
EDIT:
Note I'm using this flow for my transaction, so there is no "review" of the transaction. Buyer chooses his items on my site.
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/additional-payment-button-overview.html
Also note, I'm creating the payload dynamically according to this:
https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-checkout/amazon-pay-script.html#render-button-using-a-checkout-session-object
So when the amazonpay button is clicked, it calls a method on my site which builds the payload which begins the CheckoutSession. I then have the Amazon Session ID in my cache which I save it, and save the total. The payload is returned to the Amazon pay script which then takes me to the Amazon Site. I choose the payment type and click "continue to checkout", which sends me back to my site with the SessionId to do the "complete" step.
My request to CompleteCheckoutSession(sessionId)
{"chargeAmount":{"amount":99,"currencyCode":"USD"}}
result.RawResponse from the client.CompleteCheckoutSession(sessionId) method:
{
"reasonCode":"InvalidCheckoutSessionStatus",
"message":"You tried to call an operation on a Checkout Session that is in a state where that operation is not allowed"
}
EDIT SAMPLE CODE:
I created a brand new test MVC app with basic functionality:
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: "http://localhost:44300/home/completecheckout",
storeId: "amzn1.application-oa2-client.mystoreid"
);
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
In the Index.cshtml file:
<div id="AmazonPayButton"></div>
<script src="https://static-na.payments-amazon.com/checkout.js"></script>
<script type="text/javascript" charset="utf-8">
amazon.Pay.renderButton('#AmazonPayButton', {
merchantId: 'mymerchantid',
ledgerCurrency: 'USD',
sandbox: true,
checkoutLanguage: 'en_US',
productType: 'PayOnly',
placement: 'Checkout',
buttonColor: 'Gold',
createCheckoutSessionConfig: {
payloadJSON: '#Html.Raw(ViewBag.Payload)', // string generated in step 2
signature: '#Html.Raw(ViewBag.Signature)', // signature generated in step 3
publicKeyId: 'AGPTYXGL5VH6PSYLJUSHTKW6'
}
});
</script>
And finally, the completecheckout code, which is unsuccessful:
public ActionResult CompleteCheckout(string amazonCheckoutSessionId)
{
var client = InitiateClient(); //hidden for security
var request = new CompleteCheckoutSessionRequest(99.00M, Currency.USD);
// send the request
var result = client.CompleteCheckoutSession(amazonCheckoutSessionId, request);
// check if API call was successful
if (!result.Success)
{
throw new Exception("API Call unsuccessful");
}
return View();
}
NOTE: certain keys obfuscated, but actual keys are in the sample code.
Manually creating the CheckoutSession doesn't work with the "no review page" flow that you are following. If you want to integrate this flow, you'll have to let Amazon Pay create the CheckoutSession for you using the createCheckoutSessionConfig parameter in the button code, see here: https://amazonpaycheckoutintegrationguide.s3.amazonaws.com/amazon-pay-apb-checkout/add-the-amazon-pay-button.html#4-render-the-button
EDIT:
The code sample below shows how to construct the payload and signature for the 'no order review' payment flow. Please note the CheckoutMode parameter, that instructs Amazon Pay to immediately process the payment, and the checkoutResultReturnUrl, that defines the URL where the buyer is sent to for completing the checkout. When the user arrives at that URL, you will have to call Complete CheckoutSession, which should now succeed.
public ActionResult Index()
{
var client = InitiateClient(); //hidden for security
// prepare the request
var request = new CreateCheckoutSessionRequest
(
checkoutReviewReturnUrl: null,
storeId: "amzn1.application-oa2-client.mystoreid"
);
// instructs Amazon Pay to immediately process the payment
request.WebCheckoutDetails.CheckoutMode = CheckoutMode.ProcessOrder;
request.WebCheckoutDetails.CheckoutResultReturnUrl = "http://localhost:44300/home/completecheckout";
// payment details
request.PaymentDetails.PaymentIntent = Amazon.Pay.API.WebStore.Types.PaymentIntent.AuthorizeWithCapture;
request.PaymentDetails.ChargeAmount.Amount = 99;
request.PaymentDetails.ChargeAmount.CurrencyCode = Currency.USD;
// generate the signature and payload string that is passed back to the frontend
ViewBag.Signature = client.GenerateButtonSignature(request);
ViewBag.Payload = request.ToJson();
return View();
}
I had a similar issue and it takes me a very long time to figure it out. This is what happened to me.
I was not intent to create production code. I just want to create a test page with only Amazon Pay button. I used several node js script to getCheckoutSession/updateCheckoutSession. However, when I tried completeCheckoutSession, I got "InvalidCheckoutSessionStatus".
In the end, I found, after I setup all the required parameter and remove all constraint, the updateCheckoutSession gives me a "amazonPayRedirectUrl" in API return. That URL is really important. You need to go that URL in order to complete the review...
This is from Amazon official website:
"Once there are no constraints, the response will return a unique
amazonPayRedirectUrl. Redirect the buyer to that URL to complete
checkout."
The other way to solve this issue is mentioned by "Daniel Lemke". In that way, shoppers don't need to confirm the session. However, you need to make sure to provide all useful information in webCheckoutDetails (paymentDetails, addressDetails). If you only give checkoutMode, you will get "MissingParameterValue".
This is from Amazon official website:
"paymentDetails is required when using 'ProcessOrder'. addressDetails
is also required if you use 'ProcessOrder' with productType set to
'PayAndShip'"

Okta 'request body was not well-formed' during very simple property update

I am calling the okta API from my .Net back-end. It works fine when creating the user (the first part of the snippet below), so I know the token is correct etc. and I can confirm the new user exists.
I also, subsequently, update a property I've added to the user called 'Site'. When attempting to assign this property a value it throws an exception on the 'UpdateAsync' line that the request body was not well formed
var user = await _oktaClient.Users.CreateUserAsync(new CreateUserWithPasswordOptions
{
Profile = new UserProfile
{
FirstName = value.FirstName,
LastName = value.Surname,
Email = value.Email,
Login = value.Email,
},
Password = value.Password,
Activate = true,
});
var newUser = await _oktaClient.Users.GetUserAsync(user.Id);
newUser["site"] = Site;
await newUser.UpdateAsync(); // This line throws!
Note: I wasn't always doing it this way, I was previously assigning to the user object and then calling UpdateAsync() once I'd updated 'site' on the user object. This is an alternative approach as I'm trying different things (i.e. getting the user from the API again). Neither approach works.
What's very strange is that this has been working, I am 100% certain of that. I'm looking through my okta users directory 'as we speak', and there are a load of users, all with their site property populated. There has been no problem previously. I believe something must've been updated okta's end that now doesn't like my request for some reason but in either case I need a work-around ideally.
I can confirm Site is neither null or empty, it's simply a string.
UPDATE
I just discovered a SetProperty extension on okta's IResource, calling this before attempting to update the user causes the same issue.
Any help on this greatly appreciated.
Looks like custom attributes can't be assigned to the User object itself anymore, but need to be in the users profile: newUser.Profile["site"] = Site;

City and Country from Microsoft Graph equals null

I am using microsoft graph to get user data from active directory. When I use sdk I got only basic informations about the users eg. "displayName", "mail", "userPrincipalName", "id". Everything else have null value. My app in azure has permissions to see informations. When I turned on all permissions on azure the result was the same. How can I get city and country informations?
This is expected behavior since Microsoft Graph API endpoint returns a default set of properties for a User resource.
To return an additional properties, they need to be explicitly requested via the $select query option. In case of msgraph-sdk-dotnet an additional properties could be specified like this:
var users = await graphClient.Users.Request().Select("companyName,city,country,contacts,contactFolders").GetAsync();
Another option would be to target Microsoft Graph beta endpoint. In case of endpoint https://graph.microsoft.com/beta/users, along with default properties, at least companyName,city,country will be included in the result.
The below snippet shows how to initializes msgraph-sdk-dotnet to target API beta version:
_graphClient = new GraphServiceClient("https://graph.microsoft.com/beta",
new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Passing tenant ID to the sample auth provider to use as a cache key
string accessToken = await _authProvider.GetUserAccessTokenAsync(userId);
// Append the access token to the request
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}));

Saving Tweetinvi credentials without re-authenticating every time

Basically what I'm trying to do is to get recent tweets from a user and do stuff with them. I'm using Tweetinvi with PIN-based authentication, as described on the website, like this:
// Create a new set of credentials for the application
var appCredentials = new TwitterCredentials("CONSUMER_KEY", "CONSUMER_SECRET");
// Go to the URL so that Twitter authenticates the user and gives him a PIN code
var url = CredentialsCreator.GetAuthorizationURL(appCredentials);
// This line is an example, on how to make the user go on the URL
Process.Start(url);
// Ask the user to enter the pin code given by Twitter
var pinCode = Console.ReadLine();
// With this pin code it is now possible to get the credentials back from Twitter
var userCredentials = CredentialsCreator.GetCredentialsFromVerifierCode(pinCode, appCredentials);
// Use the user credentials in your application
Auth.SetCredentials(userCredentials);
Now the problem is that I have to sign in and connect to Twitter every time I launch my application via browser, which is mildly annoying. I've tried to save my authentication details in a text file (Consumer Key, Consumer Secret, Access Token, Access Token Secret), and then just insert the info into appCredentials and userCredentials, but with no results, as I keep getting TwitterNullCredentialsException. So how do I save my credentials so that I don't have to reconnect on every launch?
I am the main developer of Tweetinvi.
If you store the 4 credentials information you can then reuse them with 2 different solutions :
Auth.SetUserCredentials("CONSUMER_KEY", "CONSUMER_SECRET", "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET");
// OR
var creds = new TwitterCredentials("CONSUMER_KEY", "CONSUMER_SECRET", "ACCESS_TOKEN", "ACCESS_TOKEN_SECRET");
Auth.SetCredentials(creds);
The documentation might help you set up your application : https://github.com/linvi/tweetinvi/wiki/Introduction

"Encoded" login names in SharePoint Client Object Model

I'm writing a small .NET proof-of-concept console app that performs a series of actions on a SharePoint document library. I noticed that the following methods expect an "encoded" login name - that is, login name including provider information, e.g. i:0#.w|DOMAIN\user.
context.Web.EnsureUser(encodedLoginName);
context.Web.SiteUsers.GetByLoginName(encodedLoginName);
How do I reliably convert a user name such as DOMAIN\user to this encoded format in the SharePoint Client Object Model?
I've read a couple of blog posts that address this issue with the SPClaimProviderManager, which is not available in the client API.
I am able to get the encoded login name using the ResolvePrincipal utility method:
using SP = Microsoft.SharePoint.Client;
//...
// resolve user principal using regular login name or e-mail:
var userPrincipal = SP.Utilities.Utility.ResolvePrincipal(
context,
context.Web,
"DOMAIN\\user", // normal login name
SP.Utilities.PrincipalType.User,
SP.Utilities.PrincipalSource.All,
context.Web.SiteUsers,
false);
context.ExecuteQuery();
// ensure that the user principal was resolved:
if (userPrincipal.Value == null)
throw new Exception("The specified user principal could not be resolved");
// get a User instance based on the encoded login name from userPrincipal
var user = context.Web.SiteUsers.GetByLoginName(userPrincipal.LoginName);
context.Load(user);
context.ExecuteQuery();
This seems to work as intended. However, if there is a better way or caveats that I should be aware of, please let me know.

Categories

Resources