Save and Load Facebook user access token - c#

I know this is a silly question but how can I easily save the user access token after a user logs in?
Here is a part of my code that is triggered by a button:
public class FacebookLogin : MonoBehaviour {
void Awake()
{
if (!FB.IsInitialized)
{
FB.Init(InitCallback, OnHideUnity);
}
else
{
FB.ActivateApp();
}
}
private void InitCallback()
{
if (FB.IsInitialized)
{
FB.ActivateApp();
}
else
{
Debug.Log("Failed to Initialize the Facebook SDK");
}
}
private void OnHideUnity(bool isGameShown)
{
if (!isGameShown)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
public void FBLogin()
{
List<string> perms = new List<string>() { "public_profile", "email", "user_friends" };
FB.LogInWithReadPermissions(perms, AuthCallback);
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
FB.API("/me?fields=first_name", HttpMethod.GET, getName);
FB.API("/me/picture?type=square&height=128&width=128", HttpMethod.GET, getProfilePic);
SceneManager.LoadScene("Main_Menu");
}
else
{
Debug.Log("User cancelled login");
}
}
private void getName(IResult result)
{
LocalDataBase.Name = result.ResultDictionary["first_name"].ToString();
}
private void getProfilePic(IGraphResult result)
{
LocalDataBase.profilePicture = result;
}
}
Thank you in advance for all your answers.

You can save the token by making a custom serializable class that wraps around all the variables inside Facebook's AccessToken class. This custom class should contain a function that can be used to convert its data into Facebook's AccessToken class.
You can then save and load that custom class as Json.
Grab DataSaver class from this post to simplify saving and loading the data. We will call our custom class FaceBookToken.
Your Facebook Token:
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
Save:
FaceBookToken faceBookToken = FaceBookToken.createFaceBookToken(aToken);
DataSaver.saveData(faceBookToken, "FB_Token");
Load:
FaceBookToken loadedFaceBookToken = DataSaver.loadData<FaceBookToken>("FB_Token");
//AccessToken loadedToken = loadedFaceBookToken.toAccessToken(); //OR
AccessToken loadedToken = loadedFaceBookToken;
Apply Loaded Data:
Facebook.Unity.AccessToken.CurrentAccessToken = loadedToken;
Don't know if applying it is possible or allowd but it seems to compile without problems.
Delete:
DataSaver.deleteData("FB_Token");
The FaceBookToken custom class:
[Serializable]
public class FaceBookToken
{
public static AccessToken CurrentAccessToken;
public DateTime ExpirationTime;
public DateTime? LastRefresh;
public IEnumerable<string> Permissions;
public string TokenString;
public string UserId;
//Function that let's you easily create new instance of FaceBookToken from AccessToken
public static FaceBookToken createFaceBookToken(AccessToken currentToken)
{
FaceBookToken faceBookToken = new FaceBookToken();
FaceBookToken.CurrentAccessToken = AccessToken.CurrentAccessToken;
faceBookToken.ExpirationTime = currentToken.ExpirationTime;
faceBookToken.LastRefresh = currentToken.LastRefresh;
faceBookToken.Permissions = currentToken.Permissions;
faceBookToken.TokenString = currentToken.TokenString;
faceBookToken.UserId = currentToken.UserId;
return faceBookToken;
}
//Converts our custom FaceBookToken to AccessToken
public AccessToken toAccessToken()
{
AccessToken loadedToken = new AccessToken(TokenString, UserId,
ExpirationTime, Permissions, LastRefresh);
return loadedToken;
}
//Converts our custom FaceBookToken to AccessToken(Implicit Cast)
public static implicit operator AccessToken(FaceBookToken currentToken)
{
AccessToken loadedToken = new AccessToken(currentToken.TokenString, currentToken.UserId,
currentToken.ExpirationTime, currentToken.Permissions, currentToken.LastRefresh);
return loadedToken;
}
}

Related

NET MAUI SecureStorage GetAsync not returning the value and no exception

I am playing with net maui on Android emulator.
I have created a wrapper around the SecureStorage:
public static class StorageService
{
public static async Task SaveAsync<T>(string key, T data)
{
var value = JsonSerializer.Serialize(data);
await SecureStorage.Default.SetAsync(key, value);
}
public static async Task<T> GetAsync<T>(string key)
{
try
{
var value = await SecureStorage.Default.GetAsync(key);
if (string.IsNullOrWhiteSpace(value))
return (T)default;
var data = JsonSerializer.Deserialize<T>(value);
return data;
}
catch(Exception ex)
{
return (T)default;
}
}
public static bool Remove(string key)
{
return SecureStorage.Default.Remove(key);
}
public static void RemoveAll()
{
SecureStorage.Default.RemoveAll();
}
}
On the login page, when I press the login button I receive the response from the server and I store the response in the SecureStorage
private async void LoginClickHandler(object sender, EventArgs e)
{
var response = await securityClient.LoginAsync(viewModel);
if (response is null)
{
await DisplayAlert("", "Login faild, or unauthorized", "OK");
StorageService.Secure.Remove(StorageKeys.Secure.JWT);
return;
}
await StorageService.Secure.SaveAsync<JWTokenModel>(StorageKeys.Secure.JWT, response);
await Shell.Current.GoToAsync(PageRoutes.HomePage, true);
}
No errors, and I got redirected to the HomePage.
The MainPage constructor (bellow) get called and the DI kick in trying to create an instance of the clients.
public MainPage(IPlayerClient playerClient, IPositionClient positionClient, IMemoryCache memoryCache)
{
InitializeComponent();
this.playerClient = playerClient;
this.positionClient = positionClient;
this.memoryCache = memoryCache;
SubScribeOnDelte();
}
public class PlayerClient : BaseClient, IPlayerClient
{
public PlayerClient(HttpClient httpClient, MobileAppSettings settings) : base(httpClient, settings)
{}
}
public class PositionClient : BaseClient, IPositionClient
{
public PositionClient(HttpClient httpClient, MobileAppSettings settings) : base(httpClient, settings)
{
}
}
I the process it calls the base class constructor where I setup up the httpClient.
public abstract class BaseClient : IAsyncInitialization
{
private HttpClient httpClient;
private readonly MobileAppSettings settings;
public Task Initialization { get; private set; }
private string BaseURL
{
get
{
return DeviceInfo.Platform == DevicePlatform.Android ?
this.settings.AndroidBaseURL :
this.settings.IosBaseURL;
}
}
protected BaseClient(HttpClient httpClient, MobileAppSettings settings)
{
this.settings = settings;
Initialization = InitializeAsync(httpClient);
}
private async Task InitializeAsync(HttpClient httpClient)
{
this.httpClient = await BuildHttpClient(httpClient);
}
private async Task<HttpClient> BuildHttpClient(HttpClient httpClient)
{
#if DEBUG
var handler = new HttpsClientHandlerService();
httpClient = new HttpClient(handler.GetPlatformMessageHandler());
#endif
httpClient.BaseAddress = new Uri(BaseURL);
httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
httpClient.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, br");
httpClient.DefaultRequestHeaders.Add("Host", "amazonsofvolleyball");
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
var jwt = await StorageService.Secure.GetAsync<JWTokenModel>(StorageKeys.Secure.JWT);
if(jwt is not null)
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwt.Token);
return httpClient;
}
}
But when it hits the StorageService GetAsync(string key) method, the line
var value = await SecureStorage.Default.GetAsync(key);
did not return anything, no exception, and my injected httpClient stays null (obiously there are some kind of error), and it continues with the DI and try to create a new instance for the next interface where the same happens.
I set in the AndroidManifest.xml file the recommended setting"
<application android:allowBackup="false" ... >

Http Post never returns from Twilio but Twilio action is performed

I can't use the Twilio SDK in Microsoft Dynamics 365 (Twilio library is not installed in Dynamics and can't include the dll in my plugin registration) so I've had to do a http post using the HttpClient. The call to Twilio happens successfully because Twilio is able to send me an verification email but the breakpoint after PostAsync never gets hit, nor does an exception get caught. I need to capture the output from the PostAsync. What am I doing wrong?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class TwilioMessageInput
{
public string To { get; set; }
public string Channel { get; set; }
}
public class TwilioMessageOutput
{
public string Message { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
// https://www.twilio.com/docs/verify
// https://www.twilio.com/docs/verify/email
string url = "https://verify.twilio.com/v2/Services/VA********************************/Verifications/";
string authToken = "AC********************************:********************************"; //-u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
string email = "***************#************.com";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("To", email),
new KeyValuePair<string, string>("Channel", "email")
});
using (var client = new Rest(url))
{
var response = client.PostAsync<TwilioMessageOutput>(url, formContent, authToken).Result;
}
}
}
public class Rest : IDisposable
{
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private const string ClientUserAgent = "twillio-client-v1";
private const string MediaTypeJson = "application/json";
public Rest(string baseUrl, TimeSpan? timeout = null)
{
_baseUrl = NormalizeBaseUrl(baseUrl);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
//_timeout = TimeSpan.FromSeconds(1);
}
private async Task<string> PostAsyncInternal(string url, FormUrlEncodedContent input, string authToken)
{
try
{
EnsureHttpClientCreated();
var byteArray = Encoding.ASCII.GetBytes(authToken);
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
using (var response = await _httpClient.PostAsync(url, input))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<TResult> PostAsync<TResult>(string url, FormUrlEncodedContent input, string authToken) where TResult : class, new()
{
var strResponse = await PostAsyncInternal(url, input, authToken);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public void Dispose()
{
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
}
private void CreateHttpClient()
{
_httpClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
_httpClient = new HttpClient(_httpClientHandler, false)
{
Timeout = _timeout
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(_baseUrl))
{
_httpClient.BaseAddress = new Uri(_baseUrl);
}
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}
private void EnsureHttpClientCreated()
{
if (_httpClient == null)
{
CreateHttpClient();
}
}
private static string ConvertToJsonString(object obj)
{
if (obj == null)
{
return string.Empty;
}
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
private static string NormalizeBaseUrl(string url)
{
return url.EndsWith("/") ? url : url + "/";
}
}
Twilio developer evangelist here.
I'm not a C# or Dynamics developer, so sorry if this doesn't help. When you make the request:
var response = client.PostAsync<TwilioMessageOutput>(url, formContent, authToken).Result;
it is an asynchronous request, but you do not seem to be waiting for the asynchronous response at all. Should that be?
var response = await client.PostAsync<TwilioMessageOutput>(url, formContent, authToken).Result;

Unity crashes on async programming with Firebase database

When I try to do this it crashes:
I want to get the user data async, if I don't use async task it returns null
public class Database : MonoBehaviour
{
private DatabaseReference m_database;
private const string DATA_URL = "hidden";
public static Database singleton;
void Awake ()
{
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl(DATA_URL);
m_database = FirebaseDatabase.DefaultInstance.RootReference;
DontDestroyOnLoad(this);
singleton = this;
}
void Start ()
{
User user = new User();
user = GetUserAsync("213asdasd").Result;
Debug.Log(user.email);
}
public void RegisterNewUser (User user)
{
string jsonData = JsonUtility.ToJson(user);
m_database.Child("Users").Child(user.id).SetRawJsonValueAsync(jsonData);
m_database.Child("Users").Child(user.id).Child("id").SetValueAsync(user.id);
m_database.Child("Users").Child(user.id).Child("email").SetValueAsync(user.email);
}
public async Task<User> GetUserAsync (string id)
{
User user = new User();
await FirebaseDatabase.DefaultInstance.GetReference("Users").Child(id)
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
string rawUserData = snapshot.GetRawJsonValue();
Debug.Log(rawUserData);
user = JsonUtility.FromJson<User>(rawUserData);
}
});
return user;
}
}
Mixing async-await and blocking calls can cause problems.
Reference Async/Await - Best Practices in Asynchronous Programming
Use an async event handler if the code is unable to be refactored to be async all the way
void Start () {
started += onStarted;
started(this, EventArgs.Empty);
}
event EventHandler started = delegate { }
private async void onStarted(object sender, EventArgs args) {
started -= onStarted;
User user = await GetUserAsync("213asdasd");
Debug.Log(user.email);
}
Also code should follow async all the way pattern
public async Task<User> GetUserAsync (string id) {
User user = new User();
try {
DataSnapshot snapshot = await FirebaseDatabase.DefaultInstance
.GetReference("Users").Child(id).GetValueAsync();
string rawUserData = snapshot.GetRawJsonValue();
Debug.Log(rawUserData);
user = JsonUtility.FromJson<User>(rawUserData);
} catch(Exception ex) {
// Handle the error...
}
return user;
}

Why does HttpClient GetAsync() not return in Xamarin?

I'm new to Xamarin and I'm trying to create a cross-platform app where users can login using a JSON API call. A token is then returned on a successful login attempt which I can use in other API's to display user data.
It works when I use the same code in a console application, but when I run it in Xamarin the code after await client.GetAsync(url) is never reached and after a while the application breaks and I get an unknown error. Am I experiencing a deadlock?
private async void loginButton_Click(object sender, EventArgs e)
{
var login = await loginAPI(LoginPage.nameEntry.Text, LoginPage.passEntry.Text);
if (login.state == "success")
{
...
}
else
{
...
}
}
public static async Task<LoginData> loginAPI(String username, String password)
{
try
{
using (var client = new HttpClient())
{
var loginUrl = new Uri("https://my-api/login?username=" + username + "&password=" + password);
var result = await client.GetAsync(loginUrl);
return JsonConvert.DeserializeObject<LoginData>(await result.Content.ReadAsStringAsync());
}
}
catch (Exception e)
{
return null;
}
}
public class LoginData
{
[JsonProperty("state")]
public String state { get; set; }
[JsonProperty("token")]
public String token { get; set; }
}

OAuthException: (#200) The user hasn't authorized the application to perform this action

Using the Facebook C# SDK, I'm getting the following error when I try to post a status update:
OAuthException: (#200) The user hasn't authorized the application to perform this action
I am getting this error only with some users. For some other,status is updating fine. App is successfully getting access for all users.
This is the full code :
public partial class Authorize : Form
{
public Authorize()
{
InitializeComponent();
}
public string ApplicationId
{
get
{
return ConfigurationManager.AppSettings["ApplicationId"];
}
}
public string ExtendedPermissions
{
get
{
return ConfigurationManager.AppSettings["ExtendedPermissions"];
}
}
public string AppSecret
{
get
{
return ConfigurationManager.AppSettings["ApplicationSecret"];
}
}
public string AccessToken { get; set; }
private void LoadAuthorize(object sender, EventArgs e)
{
var destinationURL = String.Format(
#"https://www.facebook.com/dialog/oauth?client_id={0}&scope={1}&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token",
this.ApplicationId,
this.ExtendedPermissions);
webBrowser.Navigated += WebBrowserNavigated;
webBrowser.Navigate(destinationURL);
}
private void WebBrowserNavigated(object sender, WebBrowserNavigatedEventArgs e)
{
// get token
var url = e.Url.Fragment;
if (url.Contains("access_token") && url.Contains("#"))
{
this.Hide();
url = (new Regex("#")).Replace(url, "?", 1);
this.AccessToken = System.Web.HttpUtility.ParseQueryString(url).Get("access_token");
//MessageBox.Show(facebookCore.AccessToken);
try
{
//var facebooking = new FacebookingTest(facebookCore.AccessToken);
//facebooking.UpdateStatus();
var fb = new FacebookClient(this.AccessToken);
dynamic result = fb.Post("me/feed", new { message = "Hi..Test33" });
var newPostId = result.id;
}
catch (Exception exception)
{
Console.Write(exception);
}
}
}
}
Try opening the file App.Config and modify the last line of the
<appsettings>
section as follows:
<add key="ExtendedPermissions" value="offline_access,publish_stream,publish_actions" />

Categories