diff --git a/.gitignore b/.gitignore
index c385c37..654bc81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.nuget
+*.csproj.user
*.opensdf
*.sdf
*.suo
diff --git a/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj b/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj
index 4fdb50c..d452fe9 100644
--- a/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj
+++ b/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj
@@ -318,6 +318,7 @@ popd
+
..\..\FBWinSDK\FBWinSDK.Shared\FacebookProfilePictureControl.xaml
@@ -353,6 +354,7 @@ popd
+
..\..\FBWinSDK\FBWinSDK.Shared\FacebookProfilePictureControl.xaml
diff --git a/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj.filters b/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj.filters
index c7d9a48..e973c65 100644
--- a/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj.filters
+++ b/FBWinSDK/FBSDK-UWP/FBSDK-UWP/FBSDK-UWP.vcxproj.filters
@@ -120,6 +120,9 @@
Source Files
+
+ Source Files
+
@@ -205,6 +208,9 @@
Header Files
+
+ Header Files
+
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems
index b0ed8d6..42f035c 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems
@@ -27,6 +27,7 @@
+
$(MSBuildThisFileDirectory)FacebookProfilePictureControl.xaml
@@ -61,6 +62,7 @@
+
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems.filters b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems.filters
index 11153ed..f50211c 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems.filters
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FBWinSDK.Shared.vcxitems.filters
@@ -81,6 +81,9 @@
Header Files
+
+ Header Files
+
@@ -154,6 +157,9 @@
Source Files
+
+ Source Files
+
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookError.h b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookError.h
index 85f7572..b9c5284 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookError.h
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookError.h
@@ -31,6 +31,7 @@ namespace Facebook
*/
public enum class ErrorSubcode : int
{
+ ErrorSubcodeAppNotAuthorized = 458,
ErrorSubcodeSessionInvalidated = 466
};
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.cpp b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.cpp
index 265c466..07232ed 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.cpp
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.cpp
@@ -17,6 +17,7 @@
#include "pch.h"
#include "FacebookLoginButton.h"
#include "FacebookSession.h"
+#include "FacebookPermissions.h"
using namespace Concurrency;
using namespace Facebook;
@@ -32,7 +33,6 @@ FBLoginButton::FBLoginButton() :
{
String^ styleKey = FBLoginButton::typeid->FullName;
this->DefaultStyleKey = styleKey;
- m_permissions = ref new Vector();
}
void FBLoginButton::OnApplyTemplate(
@@ -58,61 +58,21 @@ void FBLoginButton::OnApplyTemplate(
//}
//
-IVector^ FBLoginButton::Permissions::get()
+FBPermissions^ FBLoginButton::Permissions::get()
{
return m_permissions;
}
-void FBLoginButton::Permissions::set(IVector^ Values)
+void FBLoginButton::Permissions::set(FBPermissions^ Permissions)
{
- m_permissions->Clear();
- IIterator^ it = nullptr;
- for (it = Values->First(); it->HasCurrent; it->MoveNext())
- {
- String^ value = it->Current;
- m_permissions->Append(value);
- }
+ m_permissions = Permissions;
}
void FBLoginButton::InitWithPermissions(
- IVector^ permissions
+ FBPermissions^ Permissions
)
{
- if (!m_permissions)
- {
- m_permissions = ref new Vector(0);
- }
-
- m_permissions->Clear();
-
- for (IIterator^ iter = permissions->First();
- iter->HasCurrent;
- iter->MoveNext())
- {
- m_permissions->Append(iter->Current);
- }
-}
-
-String^ FBLoginButton::GetPermissions(
- )
-{
- String^ permissions = nullptr;
- if (m_permissions)
- {
- permissions = ref new String();
-
- for (unsigned int i = 0; i < m_permissions->Size; i++)
- {
- if (i)
- {
- permissions += ",";
- }
-
- permissions += m_permissions->GetAt(i);
- }
- }
-
- return permissions;
+ m_permissions = Permissions;
}
void FBLoginButton::OnClick(
@@ -128,14 +88,7 @@ void FBLoginButton::OnClick(
}
else
{
- String^ permissions = GetPermissions();
- PropertySet^ parameters = ref new PropertySet();
- if (permissions != nullptr)
- {
- parameters->Insert(L"scope", permissions);
- }
-
- create_task(s->LoginAsync(parameters))
+ create_task(s->LoginAsync(m_permissions))
.then([=](FBResult^ result)
{
if (result->Succeeded)
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.h b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.h
index 8319684..686124c 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.h
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookLoginButton.h
@@ -17,6 +17,7 @@
#pragma once
#include "FacebookSession.h"
+#include "FacebookPermissions.h"
namespace Facebook
{
@@ -58,16 +59,16 @@ namespace Facebook
//}
//! Publish permissions for user
- property Windows::Foundation::Collections::IVector^
+ property Facebook::FBPermissions^
Permissions
{
- Windows::Foundation::Collections::IVector^ get();
- void set(Windows::Foundation::Collections::IVector^);
+ Facebook::FBPermissions^ get();
+ void set(Facebook::FBPermissions^);
}
//! Ask for read permissions at login
void InitWithPermissions(
- Windows::Foundation::Collections::IVector^ permissions
+ Facebook::FBPermissions^ permissions
);
event FBLoginErrorHandler^ FBLoginError;
@@ -85,6 +86,6 @@ namespace Facebook
);
// SessionLoginBehavior m_loginBehavior;
- Platform::Collections::Vector^ m_permissions;
+ Facebook::FBPermissions^ m_permissions;
};
}
\ No newline at end of file
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.cpp b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.cpp
new file mode 100644
index 0000000..a1155b6
--- /dev/null
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.cpp
@@ -0,0 +1,44 @@
+#include
+
+#include "pch.h"
+#include "FacebookPermissions.h"
+
+using namespace Facebook;
+using namespace Platform;
+using namespace Windows::Foundation::Collections;
+
+IVectorView^ FBPermissions::Values::get()
+{
+ return _values;
+};
+
+void FBPermissions::Values::set(
+ IVectorView^ value
+ )
+{
+ _values = value;
+}
+
+Platform::String^ FBPermissions::ToString(
+ )
+{
+ String^ permissions = nullptr;
+ if (_values)
+ {
+ permissions = ref new String();
+
+ for (unsigned int i = 0; i < _values->Size; i++)
+ {
+ if (i)
+ {
+ permissions += ",";
+ }
+
+ permissions += _values->GetAt(i);
+ }
+ }
+
+ return permissions;
+}
+
+
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.h b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.h
new file mode 100644
index 0000000..5a46d85
--- /dev/null
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookPermissions.h
@@ -0,0 +1,22 @@
+#pragma once
+
+namespace Facebook
+{
+ public ref class FBPermissions sealed
+ {
+ public:
+
+ property Windows::Foundation::Collections::IVectorView^ Values
+ {
+ Windows::Foundation::Collections::IVectorView^ get();
+ void set(Windows::Foundation::Collections::IVectorView^ value);
+ };
+
+ virtual Platform::String^ ToString(
+ ) override;
+
+ private:
+ Windows::Foundation::Collections::IVectorView^ _values;
+ };
+}
+
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.cpp b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.cpp
index 8973b3f..df3f0e6 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.cpp
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.cpp
@@ -501,7 +501,7 @@ Windows::Foundation::IAsyncOperation^ FBSession::ShowRequestsDialog(
// the concurrency event object was deprecated in the Win10 SDK tools.
// Switched to plane old Windows event, but that didn't work at all,
// so polling for now.
- while (m_showingDialog && !dialogResponse);
+ while (m_showingDialog && !dialogResponse)
{
dialogResponse = m_dialog->GetDialogResponse();
Sleep(0);
@@ -674,9 +674,6 @@ String^ FBSession::GetRedirectUriString(
)
{
Uri^ endURI = WebAuthenticationBroker::GetCurrentApplicationCallbackUri();
- String^ blerg = endURI->DisplayUri;
- OutputDebugString(blerg->Data());
- OutputDebugString(L"\n");
return endURI->DisplayUri;
}
@@ -856,22 +853,33 @@ task FBSession::RunWebViewLoginOnUIThread(
}
IAsyncOperation^ FBSession::LoginAsync(
- PropertySet^ Parameters
+ FBPermissions^ Permissions
)
{
m_dialog = ref new FacebookDialog();
return create_async([=]()
{
+ PropertySet^ parameters = ref new PropertySet();
+ if (Permissions)
+ {
+ parameters->Insert(L"scope", Permissions->ToString());
+ }
+
+ if (LoggedIn)
+ {
+ parameters->Insert(L"auth_type", L"rerequest");
+ }
+
return create_task([=]() -> FBResult^
{
FBResult^ result = nullptr;
- task authTask = TryLoginViaWebView(Parameters);
+ task authTask = TryLoginViaWebView(parameters);
result = authTask.get();
if (!result)
{
- authTask = TryLoginViaWebAuthBroker(Parameters);
+ authTask = TryLoginViaWebAuthBroker(parameters);
result = authTask.get();
}
diff --git a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.h b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.h
index 9a4009c..cdee94c 100644
--- a/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.h
+++ b/FBWinSDK/FBWinSDK/FBWinSDK.Shared/FacebookSession.h
@@ -20,6 +20,7 @@
#include "FacebookResult.h"
#include "FBUser.h"
#include "FacebookDialog.xaml.h"
+#include "FacebookPermissions.h"
namespace Facebook
{
@@ -125,7 +126,7 @@ namespace Facebook
);
Windows::Foundation::IAsyncOperation^ LoginAsync(
- Windows::Foundation::Collections::PropertySet^ Parameters
+ Facebook::FBPermissions^ Permissions
);
private:
diff --git a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj
index e276808..d26b68b 100644
--- a/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj
+++ b/samples/LoginCpp-UWP/LoginCpp/LoginCpp.vcxproj
@@ -223,19 +223,5 @@
-<<<<<<< HEAD
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-=======
->>>>>>> 26_change_sample_sdk_references
diff --git a/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.cpp b/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.cpp
index a9f7e3a..dacf4de 100644
--- a/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.cpp
+++ b/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.cpp
@@ -21,13 +21,13 @@
#include "pch.h"
#include "MainPage.xaml.h"
-#include "UserInfo.xaml.h"
#include "OptionsPage.xaml.h"
using namespace concurrency;
using namespace Facebook;
using namespace LoginCpp;
using namespace Platform;
+using namespace Platform::Collections;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::ApplicationModel::Core;
@@ -85,21 +85,19 @@ void MainPage::SetSessionAppIds()
s->WinAppId = winAppId;
}
-String^ MainPage::BuildPermissionsString(
+FBPermissions^ MainPage::BuildPermissions(
)
{
- String^ result = ref new String();
+ FBPermissions^ result = ref new FBPermissions();
+ Vector^ v = ref new Vector();
for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
{
- if (i)
- {
- result += L",";
- }
-
- result += ref new String(requested_permissions[i]);
+ v->Append(ref new String(requested_permissions[i]));
}
+ result->Values = v->GetView();
+
return result;
}
@@ -134,46 +132,95 @@ BOOL MainPage::DidGetAllRequestedPermissions(
return success;
}
+BOOL MainPage::WasAppPermissionRemovedByUser(
+ FBResult^ result
+ )
+{
+ return (result &&
+ (!result->Succeeded) &&
+ (result->ErrorInfo->Code == (int)Facebook::ErrorCode::ErrorCodeOauthException));
+}
+
+BOOL MainPage::ShouldRerequest(
+ FBResult^ result
+ )
+{
+ return (result &&
+ result->Succeeded &&
+ !DidGetAllRequestedPermissions());
+}
+
void MainPage::NavigateToOptionsPage()
{
LoginButton->Content = L"Logout";
- // We're redirecting to a page that shows simple user info, so
- // have to dispatch back to the UI thread.
- CoreWindow^ wind = CoreApplication::MainView->CoreWindow;
-
- if (wind)
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([this]()
{
- CoreDispatcher^ disp = wind->Dispatcher;
- if (disp)
- {
- disp->RunAsync(
- Windows::UI::Core::CoreDispatcherPriority::Normal,
- ref new Windows::UI::Core::DispatchedHandler([this]()
- {
- LoginCpp::App^ a = dynamic_cast(Application::Current);
- Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
- f->Navigate(OptionsPage::typeid);
- }));
- }
- }
+ LoginCpp::App^ a = dynamic_cast(Application::Current);
+ Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
+ f->Navigate(OptionsPage::typeid);
+ }));
}
-task MainPage::LoginViaRerequest(
- PropertySet^ Parameters
- )
+void MainPage::TryRerequest(
+ BOOL retry
+ )
{
- Parameters->Insert(L"auth_type", L"rerequest");
- return create_task(FBSession::ActiveSession->Logout())
- .then([=]()
- {
- SetSessionAppIds();
+ // If we're logged out, the session has cleared the FB and Windows app IDs,
+ // so they need to be set again. We load these IDs via the ResourceLoader
+ // class, which must be accessed on the UI thread, which is why this call
+ // is outside the task context.
+ SetSessionAppIds();
- return FBSession::ActiveSession->LoginAsync(Parameters);
- });
+ create_task(FBSession::ActiveSession->LoginAsync(BuildPermissions()))
+ .then([=](FBResult^ result)
+ {
+ if (result->Succeeded)
+ {
+ if (retry && (!DidGetAllRequestedPermissions()))
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(false);
+ }));
+ }
+ else
+ {
+ NavigateToOptionsPage();
+ }
+ }
+ });
}
-void MainPage::login_OnClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+void MainPage::LogoutAndRetry(
+ )
+{
+ // It's necessary to logout prior to reattempting login, because it could
+ // be that the session has cached an access token that is no longer valid,
+ // e.g. if the user revoked the app in Settings. FBSession::Logout clears
+ // the session's cached access token, among other things.
+ create_task(FBSession::ActiveSession->Logout())
+ .then([=]()
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(TRUE);
+ }));
+ });
+}
+
+void MainPage::login_OnClicked(
+ Object^ sender,
+ RoutedEventArgs^ e
+ )
{
FBSession^ sess = FBSession::ActiveSession;
@@ -187,48 +234,32 @@ void MainPage::login_OnClicked(Platform::Object^ sender, Windows::UI::Xaml::Rout
}
else
{
- PropertySet^ parameters = ref new PropertySet();
-
- parameters->Insert(L"scope", BuildPermissionsString());
-
- create_task(sess->LoginAsync(parameters)).then([=](FBResult^ result)
+ create_task(sess->LoginAsync(BuildPermissions())).then([=](FBResult^ result)
{
- task nextResult = create_task([=]()
+ // There may be other cases where an a failed login request should
+ // prompt the app to retry login, but this one is common enough that
+ // it's helpful to demonstrate handling it here. If the predicate
+ // returns TRUE, the user has gone to facebook.com in the browser,
+ // and removed our app from their list off allowed apps in Settings.
+ if (WasAppPermissionRemovedByUser(result))
{
- return result;
- });
-
- if ((!result->Succeeded) &&
- ((result->ErrorInfo->Code == 190) && (result->ErrorInfo->Subcode == 466)))
- {
- nextResult = LoginViaRerequest(parameters);
- }
-
- return nextResult;
- })
- .then([=](FBResult^ loginResult)
- {
- task finalResult = create_task([=]()
- {
- return loginResult;
- });
-
- if (loginResult->Succeeded)
- {
- if (!DidGetAllRequestedPermissions())
- {
- finalResult = LoginViaRerequest(parameters);
- }
- }
-
- return finalResult;
- })
- .then([=](FBResult^ finalResult)
- {
- if (finalResult->Succeeded)
+ LogoutAndRetry();
+ }
+ else if (ShouldRerequest(result))
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(FALSE);
+ }));
+ }
+ else if (result->Succeeded)
{
+ // Got a token and all our permissions.
NavigateToOptionsPage();
}
- });
+ });
}
}
diff --git a/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.h b/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.h
index c341207..f28ea90 100644
--- a/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.h
+++ b/samples/LoginCpp-UWP/LoginCpp/MainPage.xaml.h
@@ -37,18 +37,29 @@ namespace LoginCpp
void MainPage::SetSessionAppIds(
);
- Platform::String^ BuildPermissionsString(
+ Facebook::FBPermissions^ BuildPermissions(
);
BOOL DidGetAllRequestedPermissions(
);
-
+
+ BOOL MainPage::WasAppPermissionRemovedByUser(
+ Facebook::FBResult^ result
+ );
+
+ BOOL ShouldRerequest(
+ Facebook::FBResult^ result
+ );
+
void NavigateToOptionsPage(
);
- concurrency::task MainPage::LoginViaRerequest(
- Windows::Foundation::Collections::PropertySet^ Parameters
- );
+ void MainPage::TryRerequest(
+ BOOL retry
+ );
+
+ void MainPage::LogoutAndRetry(
+ );
void login_OnClicked(
Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.cpp b/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.cpp
index f419b00..8c29010 100644
--- a/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.cpp
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.cpp
@@ -23,148 +23,198 @@
#include "MainPage.xaml.h"
#include "OptionsPage.xaml.h"
-using namespace LoginCpp;
-
using namespace concurrency;
+using namespace Facebook;
+using namespace LoginCpp;
using namespace Platform;
+using namespace Platform::Collections;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
-using namespace Windows::UI::Xaml;
+using namespace Windows::Security::Authentication::Web;
using namespace Windows::UI::Core;
+using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
-using namespace Facebook;
-// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
+// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
#define FBAppIDName L"FBApplicationId"
#define FBStoreAppIDName L"FBWindowsAppId"
#define PermissionGranted L"granted"
-const wchar_t* requested_permissions[] =
+const wchar_t* requested_permissions[] =
{
- L"public_profile",
- L"user_friends",
- L"user_likes",
- L"user_groups",
- L"user_location"
+ L"public_profile",
+ L"user_friends",
+ L"user_likes",
+ L"user_groups",
+ L"user_location"
};
MainPage::MainPage()
{
InitializeComponent();
- SetSessionAppIds();
+
+ String^ whatever = WebAuthenticationBroker::GetCurrentApplicationCallbackUri()->DisplayUri + L"\n";
+ OutputDebugString(whatever->Data());
+
+ SetSessionAppIds();
}
void MainPage::SetSessionAppIds()
{
- FBSession^ s = FBSession::ActiveSession;
+ FBSession^ s = FBSession::ActiveSession;
- // Assumes the Facebook App ID and Windows Phone Store ID have been saved
- // in the default resource file.
- ResourceLoader^ rl = ResourceLoader::GetForCurrentView();
+ // Assumes the Facebook App ID and Windows Phone Store ID have been saved
+ // in the default resource file.
+ ResourceLoader^ rl = ResourceLoader::GetForCurrentView();
- String^ appId = rl->GetString(FBAppIDName);
- String^ winAppId = rl->GetString(FBStoreAppIDName);
+ String^ appId = rl->GetString(FBAppIDName);
+ String^ winAppId = rl->GetString(FBStoreAppIDName);
- // IDs are both sent to FB app, so it can validate us.
- s->FBAppId = appId;
- s->WinAppId = winAppId;
+ // IDs are both sent to FB app, so it can validate us.
+ s->FBAppId = appId;
+ s->WinAppId = winAppId;
}
-String^ MainPage::BuildPermissionsString(
- )
+FBPermissions^ MainPage::BuildPermissions(
+ )
{
- String^ result = ref new String();
+ FBPermissions^ result = ref new FBPermissions();
+ Vector^ v = ref new Vector();
- for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
- {
- if (i)
- {
- result += L",";
- }
+ for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
+ {
+ v->Append(ref new String(requested_permissions[i]));
+ }
- result += ref new String(requested_permissions[i]);
- }
+ result->Values = v->GetView();
- return result;
+ return result;
}
BOOL MainPage::DidGetAllRequestedPermissions(
+ )
+{
+ BOOL success = FALSE;
+ FBAccessTokenData^ data = FBSession::ActiveSession->AccessTokenData;
+ unsigned int grantedCount = 0;
+
+ if (data)
+ {
+ for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
+ {
+ String^ perm = ref new String(requested_permissions[i]);
+ if (data->Permissions && (data->Permissions->HasKey(perm)))
+ {
+ String^ Value = data->Permissions->Lookup(perm);
+ if (!String::CompareOrdinal(Value, PermissionGranted))
+ {
+ grantedCount++;
+ }
+ }
+ }
+
+ if (grantedCount == ARRAYSIZE(requested_permissions))
+ {
+ success = TRUE;
+ }
+ }
+
+ return success;
+}
+
+BOOL MainPage::WasAppPermissionRemovedByUser(
+ FBResult^ result
)
{
- BOOL success = FALSE;
- FBAccessTokenData^ data = FBSession::ActiveSession->AccessTokenData;
- unsigned int grantedCount = 0;
+ return (result &&
+ (!result->Succeeded) &&
+ ((result->ErrorInfo->Code == (int)Facebook::ErrorCode::ErrorCodeOauthException) &&
+ (result->ErrorInfo->Subcode == (int)Facebook::ErrorSubcode::ErrorSubcodeSessionInvalidated)));
+}
- if (data)
- {
- for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
- {
- String^ perm = ref new String(requested_permissions[i]);
- if (data->Permissions && (data->Permissions->HasKey(perm)))
- {
- String^ Value = data->Permissions->Lookup(perm);
- if (!String::CompareOrdinal(Value, PermissionGranted))
- {
- grantedCount++;
- }
- }
- }
-
- if (grantedCount == ARRAYSIZE(requested_permissions))
- {
- success = TRUE;
- }
- }
-
- return success;
+BOOL MainPage::ShouldRerequest(
+ FBResult^ result
+ )
+{
+ return (result &&
+ result->Succeeded &&
+ !DidGetAllRequestedPermissions());
}
void MainPage::NavigateToOptionsPage()
{
- LoginButton->Content = L"Logout";
+ LoginButton->Content = L"Logout";
- // We're redirecting to a page that shows simple user info, so
- // have to dispatch back to the UI thread.
- CoreWindow^ wind = CoreApplication::MainView->CoreWindow;
-
- if (wind)
- {
- CoreDispatcher^ disp = wind->Dispatcher;
- if (disp)
- {
- disp->RunAsync(
- Windows::UI::Core::CoreDispatcherPriority::Normal,
- ref new Windows::UI::Core::DispatchedHandler([this]()
- {
- LoginCpp::App^ a = dynamic_cast(Application::Current);
- Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
- f->Navigate(OptionsPage::typeid);
- }));
- }
- }
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([this]()
+ {
+ LoginCpp::App^ a = dynamic_cast(Application::Current);
+ Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
+ f->Navigate(OptionsPage::typeid);
+ }));
}
-task MainPage::LoginViaRerequest(
- PropertySet^ Parameters
+void MainPage::TryRerequest(
+ BOOL retry
)
{
- Parameters->Insert(L"auth_type", L"rerequest");
- return create_task(FBSession::ActiveSession->Logout())
+ // If we're logged out, the session has cleared the FB and Windows app IDs,
+ // so they need to be set again. We load these IDs via the ResourceLoader
+ // class, which must be accessed on the UI thread, which is why this call
+ // is outside the task context.
+ SetSessionAppIds();
+
+ create_task(FBSession::ActiveSession->LoginAsync(BuildPermissions()))
+ .then([=](FBResult^ result)
+ {
+ if (result->Succeeded)
+ {
+ if (retry && (!DidGetAllRequestedPermissions()))
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(false);
+ }));
+ }
+ else
+ {
+ NavigateToOptionsPage();
+ }
+ }
+ });
+}
+
+void MainPage::LogoutAndRetry(
+ )
+{
+ // It's necessary to logout prior to reattempting login, because it could
+ // be that the session has cached an access token that is no longer valid,
+ // e.g. if the user revoked the app in Settings. FBSession::Logout clears
+ // the session's cached access token, among other things.
+ create_task(FBSession::ActiveSession->Logout())
.then([=]()
{
- SetSessionAppIds();
-
- return FBSession::ActiveSession->LoginAsync(Parameters);
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(TRUE);
+ }));
});
}
@@ -182,49 +232,32 @@ void MainPage::login_OnClicked(Platform::Object^ sender, Windows::UI::Xaml::Rout
}
else
{
- PropertySet^ parameters = ref new PropertySet();
-
- parameters->Insert(L"scope", BuildPermissionsString());
-
- create_task(sess->LoginAsync(parameters)).then([=](FBResult^ result)
- {
- task nextResult = create_task([=]()
- {
- return result;
- });
-
- if ((!result->Succeeded) &&
- ((result->ErrorInfo->Code == 190) && (result->ErrorInfo->Subcode == 466)))
- {
- nextResult = LoginViaRerequest(parameters);
+ create_task(sess->LoginAsync(BuildPermissions())).then([=](FBResult^ result)
+ {
+ // There may be other cases where an a failed login request should
+ // prompt the app to retry login, but this one is common enough that
+ // it's helpful to demonstrate handling it here. If the predicate
+ // returns TRUE, the user has gone to facebook.com in the browser,
+ // and removed our app from their list off allowed apps in Settings.
+ if (WasAppPermissionRemovedByUser(result))
+ {
+ LogoutAndRetry();
}
-
- return nextResult;
- })
- .then([=](FBResult^ loginResult)
- {
- task finalResult = create_task([=]()
+ else if (ShouldRerequest(result))
{
- return loginResult;
- });
-
- if (loginResult->Succeeded)
- {
- if (!DidGetAllRequestedPermissions())
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
{
- finalResult = LoginViaRerequest(parameters);
- }
- }
-
- return finalResult;
- })
- .then([=](FBResult^ finalResult)
- {
- if (finalResult->Succeeded)
- {
- NavigateToOptionsPage();
+ TryRerequest(FALSE);
+ }));
}
+ else if (result && result->Succeeded)
+ {
+ // Got a token, and all the permissions we wanted - go ahead to the options page
+ NavigateToOptionsPage();
+ }
});
}
}
-
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.h b/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.h
index 2cdc116..f28ea90 100644
--- a/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.h
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.Windows/MainPage.xaml.h
@@ -33,23 +33,36 @@ namespace LoginCpp
public:
MainPage();
- private:
- void MainPage::SetSessionAppIds(
+ private:
+ void MainPage::SetSessionAppIds(
+ );
+
+ Facebook::FBPermissions^ BuildPermissions(
+ );
+
+ BOOL DidGetAllRequestedPermissions(
+ );
+
+ BOOL MainPage::WasAppPermissionRemovedByUser(
+ Facebook::FBResult^ result
);
- Platform::String^ BuildPermissionsString(
+ BOOL ShouldRerequest(
+ Facebook::FBResult^ result
);
- BOOL DidGetAllRequestedPermissions(
+ void NavigateToOptionsPage(
+ );
+
+ void MainPage::TryRerequest(
+ BOOL retry
);
- void NavigateToOptionsPage(
+ void MainPage::LogoutAndRetry(
);
- concurrency::task MainPage::LoginViaRerequest(
- Windows::Foundation::Collections::PropertySet^ Parameters
- );
-
- void login_OnClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
+ void login_OnClicked(
+ Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e
+ );
};
}
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/LoginCpp.WindowsPhone.vcxproj.user b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/LoginCpp.WindowsPhone.vcxproj.user
new file mode 100644
index 0000000..3f8a826
--- /dev/null
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/LoginCpp.WindowsPhone.vcxproj.user
@@ -0,0 +1,7 @@
+
+
+
+ WindowsPhoneEmulatorDebugger
+ 8BDF218D-FDBB-4A97-90F9-3AA33B559A92;Mobile Emulator 10.0.10240.0 WVGA 4 inch 512MB
+
+
\ No newline at end of file
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml
index 38752b8..d4decc8 100644
--- a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml
@@ -9,7 +9,7 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
-
+
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.cpp b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.cpp
index 140cb98..a11c59b 100644
--- a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.cpp
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.cpp
@@ -23,13 +23,18 @@
#include "MainPage.xaml.h"
#include "OptionsPage.xaml.h"
-using namespace LoginCpp;
-
using namespace concurrency;
+using namespace Facebook;
+using namespace LoginCpp;
using namespace Platform;
+using namespace Platform::Collections;
+using namespace Windows::ApplicationModel;
+using namespace Windows::ApplicationModel::Activation;
using namespace Windows::ApplicationModel::Core;
+using namespace Windows::ApplicationModel::Resources;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
+using namespace Windows::Security::Authentication::Web;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
@@ -38,225 +43,221 @@ using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
-using namespace Windows::ApplicationModel::Resources;
-using namespace Windows::Globalization;
-using namespace Facebook;
-// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
+// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
#define FBAppIDName L"FBApplicationId"
-#define FBPhoneAppIDName L"FBWinPhoneAppId"
+#define FBStoreAppIDName L"FBWindowsAppId"
#define PermissionGranted L"granted"
const wchar_t* requested_permissions[] =
{
- L"public_profile",
- L"user_friends",
- L"user_likes",
- L"user_groups",
- L"user_location"
+ L"public_profile",
+ L"user_friends",
+ L"user_likes",
+ L"user_groups",
+ L"user_location"
};
-
MainPage::MainPage()
{
- InitializeComponent();
- SetSessionAppIds();
-}
+ InitializeComponent();
-///
-/// Invoked when this page is about to be displayed in a Frame.
-///
-/// Event data that describes how this page was reached. The Parameter
-/// property is typically used to configure the page.
-void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
-{
- (void)e; // Unused parameter
+ String^ whatever = WebAuthenticationBroker::GetCurrentApplicationCallbackUri()->DisplayUri + L"\n";
+ OutputDebugString(whatever->Data());
- // TODO: Prepare page for display here.
-
- // TODO: If your application contains multiple pages, ensure that you are
- // handling the hardware Back button by registering for the
- // Windows::Phone::UI::Input::HardwareButtons.BackPressed event.
- // If you are using the NavigationHelper provided by some templates,
- // this event is handled for you.
- FBSession^ sess = FBSession::ActiveSession;
-
- if (sess->LoggedIn)
- {
- LoginButton->Content = L"Logout";
- Calendar^ cal = ref new Calendar();
- cal->SetDateTime(sess->AccessTokenData->ExpirationDate);
-
- ResponseText->Text = sess->AccessTokenData->AccessToken;
-
- ExpirationDate->Text = cal->DayOfWeekAsString() + "," + cal->YearAsString() + "/" +
- cal->MonthAsNumericString() + "/" + cal->DayAsString() + ", " +
- cal->HourAsPaddedString(2) + ":" + cal->MinuteAsPaddedString(2) + ":" +
- cal->SecondAsPaddedString(2);
- }
+ SetSessionAppIds();
}
void MainPage::SetSessionAppIds()
{
- FBSession^ s = FBSession::ActiveSession;
+ FBSession^ s = FBSession::ActiveSession;
- // Assumes the Facebook App ID and Windows Phone Store ID have been saved
- // in the default resource file.
- ResourceLoader^ rl = ResourceLoader::GetForCurrentView();
+ // Assumes the Facebook App ID and Windows Phone Store ID have been saved
+ // in the default resource file.
+ ResourceLoader^ rl = ResourceLoader::GetForCurrentView();
- String^ appId = rl->GetString(FBAppIDName);
- String^ winAppId = rl->GetString(FBPhoneAppIDName);
+ String^ appId = rl->GetString(FBAppIDName);
+ String^ winAppId = rl->GetString(FBStoreAppIDName);
- // IDs are both sent to FB app, so it can validate us.
- s->FBAppId = appId;
- s->WinAppId = winAppId;
+ // IDs are both sent to FB app, so it can validate us.
+ s->FBAppId = appId;
+ s->WinAppId = winAppId;
}
-String^ MainPage::BuildPermissionsString(
- )
+FBPermissions^ MainPage::BuildPermissions(
+ )
{
- String^ result = ref new String();
+ FBPermissions^ result = ref new FBPermissions();
+ Vector^ v = ref new Vector();
- for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
- {
- if (i)
- {
- result += L",";
- }
+ for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
+ {
+ v->Append(ref new String(requested_permissions[i]));
+ }
- result += ref new String(requested_permissions[i]);
- }
+ result->Values = v->GetView();
- return result;
+ return result;
}
BOOL MainPage::DidGetAllRequestedPermissions(
- )
+ )
{
- BOOL success = FALSE;
- FBAccessTokenData^ data = FBSession::ActiveSession->AccessTokenData;
- unsigned int grantedCount = 0;
+ BOOL success = FALSE;
+ FBAccessTokenData^ data = FBSession::ActiveSession->AccessTokenData;
+ unsigned int grantedCount = 0;
- if (data)
- {
- for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
- {
- String^ perm = ref new String(requested_permissions[i]);
- if (data->Permissions && (data->Permissions->HasKey(perm)))
- {
- String^ Value = data->Permissions->Lookup(perm);
- if (!String::CompareOrdinal(Value, PermissionGranted))
- {
- grantedCount++;
- }
- }
- }
+ if (data)
+ {
+ for (unsigned int i = 0; i < ARRAYSIZE(requested_permissions); i++)
+ {
+ String^ perm = ref new String(requested_permissions[i]);
+ if (data->Permissions && (data->Permissions->HasKey(perm)))
+ {
+ String^ Value = data->Permissions->Lookup(perm);
+ if (!String::CompareOrdinal(Value, PermissionGranted))
+ {
+ grantedCount++;
+ }
+ }
+ }
- if (grantedCount == ARRAYSIZE(requested_permissions))
- {
- success = TRUE;
- }
- }
+ if (grantedCount == ARRAYSIZE(requested_permissions))
+ {
+ success = TRUE;
+ }
+ }
- return success;
+ return success;
+}
+
+BOOL MainPage::WasAppPermissionRemovedByUser(
+ FBResult^ result
+ )
+{
+ return (result &&
+ (!result->Succeeded) &&
+ ((result->ErrorInfo->Code == (int)Facebook::ErrorCode::ErrorCodeOauthException) &&
+ (result->ErrorInfo->Subcode == (int)Facebook::ErrorSubcode::ErrorSubcodeSessionInvalidated)));
+}
+
+BOOL MainPage::ShouldRerequest(
+ FBResult^ result
+ )
+{
+ return (result &&
+ result->Succeeded &&
+ !DidGetAllRequestedPermissions());
}
void MainPage::NavigateToOptionsPage()
{
- LoginButton->Content = L"Logout";
+ LoginButton->Content = L"Logout";
- // We're redirecting to a page that shows simple user info, so
- // have to dispatch back to the UI thread.
- CoreWindow^ wind = CoreApplication::MainView->CoreWindow;
-
- if (wind)
- {
- CoreDispatcher^ disp = wind->Dispatcher;
- if (disp)
- {
- disp->RunAsync(
- Windows::UI::Core::CoreDispatcherPriority::Normal,
- ref new Windows::UI::Core::DispatchedHandler([this]()
- {
- LoginCpp::App^ a = dynamic_cast(Application::Current);
- Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
- f->Navigate(OptionsPage::typeid);
- }));
- }
- }
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([this]()
+ {
+ LoginCpp::App^ a = dynamic_cast(Application::Current);
+ Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
+ f->Navigate(OptionsPage::typeid);
+ }));
}
-task MainPage::LoginViaRerequest(
- PropertySet^ Parameters
- )
+void MainPage::TryRerequest(
+ BOOL retry
+ )
{
- Parameters->Insert(L"auth_type", L"rerequest");
- return create_task(FBSession::ActiveSession->Logout())
- .then([=]()
- {
- SetSessionAppIds();
+ // If we're logged out, the session has cleared the FB and Windows app IDs,
+ // so they need to be set again. We load these IDs via the ResourceLoader
+ // class, which must be accessed on the UI thread, which is why this call
+ // is outside the task context.
+ SetSessionAppIds();
- return FBSession::ActiveSession->LoginAsync(Parameters);
- });
+ create_task(FBSession::ActiveSession->LoginAsync(BuildPermissions()))
+ .then([=](FBResult^ result)
+ {
+ if (result->Succeeded)
+ {
+ if (retry && (!DidGetAllRequestedPermissions()))
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(false);
+ }));
+ }
+ else
+ {
+ NavigateToOptionsPage();
+ }
+ }
+ });
}
-void MainPage::LoginButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+void MainPage::LogoutAndRetry(
+ )
{
- FBSession^ sess = FBSession::ActiveSession;
-
- if (sess->LoggedIn)
- {
- sess->Logout();
- LoginButton->Content = L"Logout";
- LoginCpp::App^ a = dynamic_cast(Application::Current);
- Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
- f->Navigate(MainPage::typeid);
- }
- else
- {
- PropertySet^ parameters = ref new PropertySet();
-
- parameters->Insert(L"scope", BuildPermissionsString());
-
- create_task(sess->LoginAsync(parameters)).then([=](FBResult^ result)
- {
- task nextResult = create_task([=]()
- {
- return result;
- });
-
- if ((!result->Succeeded) &&
- ((result->ErrorInfo->Code == 190) && (result->ErrorInfo->Subcode == 466)))
- {
- nextResult = LoginViaRerequest(parameters);
- }
-
- return nextResult;
- })
- .then([=](FBResult^ loginResult)
- {
- task finalResult = create_task([=]()
- {
- return loginResult;
- });
-
- if (loginResult->Succeeded)
- {
- if (!DidGetAllRequestedPermissions())
- {
- finalResult = LoginViaRerequest(parameters);
- }
- }
-
- return finalResult;
- })
- .then([=](FBResult^ finalResult)
- {
- if (finalResult->Succeeded)
- {
- NavigateToOptionsPage();
- }
- });
- }
+ // It's necessary to logout prior to reattempting login, because it could
+ // be that the session has cached an access token that is no longer valid,
+ // e.g. if the user revoked the app in Settings. FBSession::Logout clears
+ // the session's cached access token, among other things.
+ create_task(FBSession::ActiveSession->Logout())
+ .then([=]()
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(TRUE);
+ }));
+ });
+}
+
+void MainPage::login_OnClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
+{
+ FBSession^ sess = FBSession::ActiveSession;
+
+ if (sess->LoggedIn)
+ {
+ sess->Logout();
+ LoginButton->Content = L"Logout";
+ LoginCpp::App^ a = dynamic_cast(Application::Current);
+ Windows::UI::Xaml::Controls::Frame^ f = a->CreateRootFrame();
+ f->Navigate(MainPage::typeid);
+ }
+ else
+ {
+ create_task(sess->LoginAsync(BuildPermissions())).then([=](FBResult^ result)
+ {
+ // There may be other cases where an a failed login request should
+ // prompt the app to retry login, but this one is common enough that
+ // it's helpful to demonstrate handling it here. If the predicate
+ // returns TRUE, the user has gone to facebook.com in the browser,
+ // and removed our app from their list off allowed apps in Settings.
+ if (WasAppPermissionRemovedByUser(result))
+ {
+ LogoutAndRetry();
+ }
+ else if (ShouldRerequest(result))
+ {
+ // Login call has to happen on UI thread, so circle back around to it
+ CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Normal,
+ ref new Windows::UI::Core::DispatchedHandler([=]()
+ {
+ TryRerequest(FALSE);
+ }));
+ }
+ else if (result && result->Succeeded)
+ {
+ // Got a token, and all the permissions we wanted - go ahead to the options page
+ NavigateToOptionsPage();
+ }
+ });
+ }
}
diff --git a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.h b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.h
index 9a8f75c..fb0e2c3 100644
--- a/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.h
+++ b/samples/LoginCpp/LoginCpp/LoginCpp.WindowsPhone/MainPage.xaml.h
@@ -25,45 +25,45 @@
namespace LoginCpp
{
- ///
- /// An empty page that can be used on its own or navigated to within a Frame.
- ///
- public ref class MainPage sealed
- {
- public:
- MainPage();
-
- protected:
- virtual void OnNavigatedTo(
- Windows::UI::Xaml::Navigation::NavigationEventArgs^ e
- ) override;
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public ref class MainPage sealed
+ {
+ public:
+ MainPage();
private:
- void StartLogin(
- Facebook::FBSession^ Session
- );
-
- void MainPage::SetSessionAppIds(
- );
-
- Platform::String^ BuildPermissionsString(
- );
-
- BOOL DidGetAllRequestedPermissions(
- );
-
- void NavigateToOptionsPage(
- );
-
- concurrency::task MainPage::LoginViaRerequest(
- Windows::Foundation::Collections::PropertySet^ Parameters
- );
-
- void LoginButton_Click(
- Platform::Object^ sender,
- Windows::UI::Xaml::RoutedEventArgs^ e
+ void MainPage::SetSessionAppIds(
);
- Windows::Foundation::EventRegistrationToken m_cookie;
+ Facebook::FBPermissions^ BuildPermissions(
+ );
+
+ BOOL DidGetAllRequestedPermissions(
+ );
+
+ BOOL MainPage::WasAppPermissionRemovedByUser(
+ Facebook::FBResult^ result
+ );
+
+ BOOL ShouldRerequest(
+ Facebook::FBResult^ result
+ );
+
+ void NavigateToOptionsPage(
+ );
+
+ void MainPage::TryRerequest(
+ BOOL retry
+ );
+
+ void MainPage::LogoutAndRetry(
+ );
+
+ void login_OnClicked(
+ Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e
+ );
+ void LoginButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
};
}
diff --git a/samples/LoginCs/LoginCs/LoginCs.Windows/LoginCs.Windows.csproj b/samples/LoginCs/LoginCs/LoginCs.Windows/LoginCs.Windows.csproj
index e0a3b96..d057826 100644
--- a/samples/LoginCs/LoginCs/LoginCs.Windows/LoginCs.Windows.csproj
+++ b/samples/LoginCs/LoginCs/LoginCs.Windows/LoginCs.Windows.csproj
@@ -1,10 +1,5 @@
-<<<<<<< HEAD
-
-
-=======
->>>>>>> 26_change_sample_sdk_references
Debug
diff --git a/samples/LoginCs/LoginCs/LoginCs.Windows/MainPage.xaml.cs b/samples/LoginCs/LoginCs/LoginCs.Windows/MainPage.xaml.cs
index 792f297..ccddfec 100644
--- a/samples/LoginCs/LoginCs/LoginCs.Windows/MainPage.xaml.cs
+++ b/samples/LoginCs/LoginCs/LoginCs.Windows/MainPage.xaml.cs
@@ -32,6 +32,8 @@ using Windows.UI.Xaml.Navigation;
using Facebook;
using Windows.Globalization;
+using Windows.ApplicationModel.Resources;
+using System.Threading.Tasks;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
@@ -45,11 +47,14 @@ namespace LoginCs
public MainPage()
{
this.InitializeComponent();
+ SetSessionAppIds();
}
- private const string _granted = "granted";
+ private const string FBAppIDName = "FBApplicationId";
+ private const string FBPhoneAppIDName = "FBWinPhoneAppId";
+ private const string PermissionGranted = "granted";
- private string[] requested_permissions =
+ private readonly string[] requested_permissions =
{
"public_profile",
"email",
@@ -57,39 +62,55 @@ namespace LoginCs
"publish_actions"
};
- private string BuildPermissionsString()
+ void SetSessionAppIds(
+ )
{
- string permissions = "";
+ FBSession s = FBSession.ActiveSession;
+
+ // Assumes the Facebook App ID and Windows Phone Store ID have been saved
+ // in the default resource file.
+ ResourceLoader rl = ResourceLoader.GetForCurrentView();
+
+ String appId = rl.GetString(FBAppIDName);
+ String winAppId = rl.GetString(FBPhoneAppIDName);
+
+ // IDs are both sent to FB app, so it can validate us.
+ s.FBAppId = appId;
+ s.WinAppId = winAppId;
+ }
+
+ FBPermissions BuildPermissions(
+ )
+ {
+ FBPermissions result = new FBPermissions();
+ List perms = new List();
for (uint i = 0; i < requested_permissions.Length; i++)
{
- if (i > 0)
- {
- permissions += ",";
- }
-
- permissions += requested_permissions[i];
+ perms.Add(requested_permissions[i]);
}
- return permissions;
+ result.Values = perms;
+
+ return result;
}
- private bool DidGetRequestedPermissions(
+ bool DidGetAllRequestedPermissions(
)
{
- bool gotPermissions = false;
+ bool success = false;
+ FBAccessTokenData data = FBSession.ActiveSession.AccessTokenData;
uint grantedCount = 0;
- if (FBSession.ActiveSession.AccessTokenData != null)
+ if (data != null)
{
- IReadOnlyDictionary Permissions =
- FBSession.ActiveSession.AccessTokenData.Permissions;
-
for (uint i = 0; i < requested_permissions.Length; i++)
{
- if (Permissions.ContainsKey(requested_permissions[i]))
+ string perm = requested_permissions[i];
+ if ((data.Permissions != null) && (data.Permissions.ContainsKey(perm)))
{
- if (String.CompareOrdinal(Permissions[requested_permissions[i]], _granted) == 0)
+ string Value = data.Permissions[perm];
+ if (string.CompareOrdinal(Value, PermissionGranted) == 0)
{
grantedCount++;
}
@@ -98,49 +119,53 @@ namespace LoginCs
if (grantedCount == requested_permissions.Length)
{
- gotPermissions = true;
+ success = true;
}
}
- return gotPermissions;
+ return success;
}
- private async void LoginToFB()
+ bool WasAppPermissionRemovedByUser(
+ FBResult Result
+ )
{
- Uri endURI =
- WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
- string uriString = endURI.ToString();
- PropertySet parameters = new PropertySet();
+ return ((Result != null) &&
+ (!Result.Succeeded) &&
+ (Result.ErrorInfo.Code == (int)Facebook.ErrorCode.ErrorCodeOauthException));
+ }
- parameters.Add(new KeyValuePair("scope",
- BuildPermissionsString()));
+ bool ShouldRerequest(
+ FBResult Result
+ )
+ {
+ return ((Result != null) &&
+ Result.Succeeded &&
+ !DidGetAllRequestedPermissions());
+ }
- FBResult result = await FBSession.ActiveSession.LoginAsync(parameters);
- if ((!result.Succeeded) &&
- (((ErrorCode)result.ErrorInfo.Code == ErrorCode.ErrorCodeOauthException) &&
- ((ErrorSubcode)result.ErrorInfo.Subcode == ErrorSubcode.ErrorSubcodeSessionInvalidated)))
- {
- parameters.Add(new KeyValuePair("auth_type", "rerequest"));
- result = await FBSession.ActiveSession.LoginAsync(parameters);
- }
+ async Task TryRerequest(
+ bool Retry
+ )
+ {
+ // If we're logged out, the session has cleared the FB and Windows app IDs,
+ // so they need to be set again. We load these IDs via the ResourceLoader
+ // class, which must be accessed on the UI thread, which is why this call
+ // is outside the task context.
+ SetSessionAppIds();
+
+ FBResult result = await FBSession.ActiveSession.LoginAsync(BuildPermissions());
if (result.Succeeded)
{
- if (DidGetRequestedPermissions())
+ if (Retry && (!DidGetAllRequestedPermissions()))
{
- Frame.Navigate(typeof(UserInfo));
+ await TryRerequest(false);
}
else
{
- if (!parameters.ContainsKey("auth_type"))
- {
- parameters.Add(new KeyValuePair("auth_type", "rerequest"));
- }
- result = await FBSession.ActiveSession.LoginAsync(parameters);
- if (result.Succeeded)
- {
- Frame.Navigate(typeof(UserInfo));
- }
+ //Navigate back to same page, to clear out logged in info.
+ App.RootFrame.Navigate(typeof(UserInfo));
}
}
}
@@ -194,7 +219,27 @@ namespace LoginCs
else
{
LoginButton.Content = "Logout";
- LoginToFB();
+ FBResult result = await sess.LoginAsync(BuildPermissions());
+
+ // There may be other cases where an a failed login request should
+ // prompt the app to retry login, but this one is common enough that
+ // it's helpful to demonstrate handling it here. If the predicate
+ // returns TRUE, the user has gone to facebook.com in the browser,
+ // and removed our app from their list off allowed apps in Settings.
+ if (WasAppPermissionRemovedByUser(result))
+ {
+ await sess.Logout();
+ await TryRerequest(true);
+ }
+ else if (ShouldRerequest(result))
+ {
+ await TryRerequest(false);
+ }
+ else if (result.Succeeded)
+ {
+ //Navigate back to same page, to clear out logged in info.
+ App.RootFrame.Navigate(typeof(UserInfo));
+ }
}
}
}
diff --git a/samples/LoginCs/LoginCs/LoginCs.WindowsPhone/MainPage.xaml.cs b/samples/LoginCs/LoginCs/LoginCs.WindowsPhone/MainPage.xaml.cs
index cd9c3da..4bc20d4 100644
--- a/samples/LoginCs/LoginCs/LoginCs.WindowsPhone/MainPage.xaml.cs
+++ b/samples/LoginCs/LoginCs/LoginCs.WindowsPhone/MainPage.xaml.cs
@@ -20,6 +20,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
+using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.Foundation.Collections;
@@ -41,45 +42,133 @@ namespace LoginCs
///
public sealed partial class MainPage : Page
{
- const string FBAppIDName = "FBApplicationId";
- const string FBPhoneAppIDName = "FBWinPhoneAppId";
+ const string FBAppIDName = "FBApplicationId";
+ const string FBPhoneAppIDName = "FBWinPhoneAppId";
+ const string PermissionGranted = "granted";
+
+ readonly string[] requested_permissions =
+ {
+ "public_profile",
+ "user_friends",
+ "user_likes",
+ "user_groups",
+ "user_location"
+ };
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
+ SetSessionAppIds();
}
-
- public async void StartLogin(
+
+ void SetSessionAppIds(
)
{
FBSession s = FBSession.ActiveSession;
- // Assumes the Facebook App ID and Windows Phone Store ID have been
- // saved in the default resource file.
+ // Assumes the Facebook App ID and Windows Phone Store ID have been saved
+ // in the default resource file.
ResourceLoader rl = ResourceLoader.GetForCurrentView();
String appId = rl.GetString(FBAppIDName);
String winAppId = rl.GetString(FBPhoneAppIDName);
-
// IDs are both sent to FB app, so it can validate us.
s.FBAppId = appId;
s.WinAppId = winAppId;
+ }
- // These are the default permissions, needed to retrieve user info.
- //s.AddPermission("public_profile");
- //s.AddPermission("user_friends");
- //s.AddPermission("user_likes");
+ FBPermissions BuildPermissions(
+ )
+ {
+ FBPermissions result = new FBPermissions();
+ List perms = new List();
- PropertySet parameters = new PropertySet();
- parameters.Add(
- new KeyValuePair("scope",
- "public_profile,user_friends,user_likes"));
- // Launches a URI to redirect to the FB app, which will log us in
- // and return the result via our registered protocol.
- FBResult result = await s.LoginAsync();
+ for (uint i = 0; i < requested_permissions.Length; i++)
+ {
+ perms.Add(requested_permissions[i]);
+ }
+
+ result.Values = perms;
+
+ return result;
+ }
+
+ bool DidGetAllRequestedPermissions(
+ )
+ {
+ bool success = false;
+ FBAccessTokenData data = FBSession.ActiveSession.AccessTokenData;
+ uint grantedCount = 0;
+
+ if (data != null)
+ {
+ for (uint i = 0; i < requested_permissions.Length; i++)
+ {
+ string perm = requested_permissions[i];
+ if ((data.Permissions != null) && (data.Permissions.ContainsKey(perm)))
+ {
+ string Value = data.Permissions[perm];
+ if (string.CompareOrdinal(Value, PermissionGranted) == 0)
+ {
+ grantedCount++;
+ }
+ }
+ }
+
+ if (grantedCount == requested_permissions.Length)
+ {
+ success = true;
+ }
+ }
+
+ return success;
+ }
+
+ bool WasAppPermissionRemovedByUser(
+ FBResult Result
+ )
+ {
+ return ((Result != null) &&
+ (!Result.Succeeded) &&
+ (Result.ErrorInfo.Code == (int)Facebook.ErrorCode.ErrorCodeOauthException));
+ }
+
+ bool ShouldRerequest(
+ FBResult Result
+ )
+ {
+ return ((Result != null) &&
+ Result.Succeeded &&
+ !DidGetAllRequestedPermissions());
+ }
+
+ async Task TryRerequest(
+ bool Retry
+ )
+ {
+ // If we're logged out, the session has cleared the FB and Windows app IDs,
+ // so they need to be set again. We load these IDs via the ResourceLoader
+ // class, which must be accessed on the UI thread, which is why this call
+ // is outside the task context.
+ SetSessionAppIds();
+
+ FBResult result = await FBSession.ActiveSession.LoginAsync(BuildPermissions());
+
+ if (result.Succeeded)
+ {
+ if (Retry && (!DidGetAllRequestedPermissions()))
+ {
+ await TryRerequest(false);
+ }
+ else
+ {
+ //Navigate back to same page, to clear out logged in info.
+ App.RootFrame.Navigate(typeof(UserInfo));
+ }
+ }
}
///
@@ -127,7 +216,27 @@ namespace LoginCs
else
{
LoginButton.Content = "Logout";
- StartLogin();
+
+ FBResult result = await sess.LoginAsync(BuildPermissions());
+
+ // There may be other cases where an a failed login request should
+ // prompt the app to retry login, but this one is common enough that
+ // it's helpful to demonstrate handling it here. If the predicate
+ // returns TRUE, the user has gone to facebook.com in the browser,
+ // and removed our app from their list off allowed apps in Settings.
+ if (WasAppPermissionRemovedByUser(result))
+ {
+ await sess.Logout();
+ }
+ else if (ShouldRerequest(result))
+ {
+ await TryRerequest(false);
+ }
+ else if (result.Succeeded)
+ {
+ //Navigate back to same page, to clear out logged in info.
+ App.RootFrame.Navigate(typeof(UserInfo));
+ }
}
}
}