Facebook 社群身分驗證
這個專案一開始執行,只會有一個按鈕,當按下這個按鈕,就會使用原生平台的功能,進行 OAuth2 的身分驗證;當驗證過程結束之後,會將取得的 Token 顯示在螢幕上。
建立社群身分驗證頁面方案
- 首先,開啟您的 Visual Studio 2015
 - 接著透過 Visual Studio 2015 功能表,選擇這些項目
檔案>新增>專案準備新增一個專案。 - 接著,Visual Studio 2015 會顯示
新增專案對話窗,請在這個對話窗上,進行選擇Visual C#>Cross-Platform>Blank Xaml App (Xamarin.Forms Portable) - 接著,在最下方的
名稱文字輸入盒處,輸入XFAuth這個名稱,最後使用滑鼠右擊右下方的確定按鈕。 - 當專案建立到一半,若您的開發環境還沒有建置與設定完成 Mac 電腦與 Xamarin Studio for Mac 系統,此時會看到
Xamarin Mac Agent Instructions對話窗出現,這個對話窗是要提醒您進行與 Mac 電腦連線設定,這是因為,您無法在 Windows 作業系統進行 iOS 相關應用程式的建立與設計工作,而是需要透過 Mac 電腦安裝的 XCode 來協助您完成這些 iOS 應用程式的建立工作。不過,這不影響您繼續開發 Xamarin.Forms 的應用程式,只不過此時,您無法編譯與執行 iOS 的應用程式。 - 接著會看到
新的通用Windows專案對話視窗,此時,您只需要按下確定按鈕即可,此時,專案精靈會繼續完成相關平台的專案建立工作。 - 最後,整個新的 Xamarin.Forms 專案就建立完成了。
 
安裝所需 NuGet 套件
- 請在
XFAuth.Droid專案的參考節點上,滑鼠右擊,接著點選管理 NuGet 套件選項,接著點選瀏覽標籤頁次,在搜尋文字輸入盒中,輸入Xamarin.Auth這個套件,當找到這個套件之後,請將這個套件安裝到這個專案裡面。 - 請在
XFAuth.iOS專案的參考節點上,滑鼠右擊,接著點選管理 NuGet 套件選項,接著點選瀏覽標籤頁次,在搜尋文字輸入盒中,輸入Xamarin.Auth這個套件,當找到這個套件之後,請將這個套件安裝到這個專案裡面。 
核心PCL頁面新增
OAuthMessage
這個檔案是用來作為訊息中心的指定型別之用
- 滑鼠右擊核心PCL
XFAuth專案,點選加入>類別>Visual C#>類別 - 在底下名稱欄位內輸入
OAuthMessage,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 
OAuthMessage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XFAuth
{
    public class OAuthMessage
    {
    }
}
LoginPage
現在要新增的檔案,是用於當要進行 Facebook 身分驗證的時候,所要顯示的頁面;雖然,這個頁面內沒有任何定義,不過,當執行時期,這個頁面將會由原生平台定義的視覺內容,注入到這個頁面中。
- 滑鼠右擊核心PCL
XFAuth專案,點選加入>類別>Visual C#>類別 - 在底下名稱欄位內輸入
LoginPage,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 - 這個建構式接受一個列舉參數,用來指名所要進行的身分驗證是要採用哪個社群,相關社群的驗證參數,都會儲存在
ProviderOAuthSettings物件內。 
LoginPage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XFAuth
{
    public class LoginPage : ContentPage
    {
        public OAuthSettings ProviderOAuthSettings { get; set; }
        public LoginPage(Provider provider)
        {
            ProviderOAuthSettings = ProviderManager.GetProviderOAuthSettings(provider);
        }
    }
}
OAuthSettings
這個檔案將會定義 
OAuthSettings 類別,裡面的屬性將會用來記錄要進行 OAuth2 身分驗證時,會用到的內容。- 滑鼠右擊核心PCL
XFAuth專案,點選加入>類別>Visual C#>類別 - 在底下名稱欄位內輸入
OAuthSettings,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 
.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XFAuth
{
    public class OAuthSettings
    {
        public string ClientId { get; set; }
        public string ClientSecret { get; set; }
        public string AuthorizeUrl { get; set; }
        public string RedirectUrl { get; set; }
        public string AccessTokenUrl { get; set; }
        public List<string> Scopes { get; set; }
        public string ScopesString
        {
            get
            {
                return Scopes.Aggregate((current, next) => string.Format("{0}+{1}", current, next));
            }
        }
        public OAuthSettings()
        {
            Scopes = new List<string>();
        }
    }
}
ProviderManager
這個類別提供一個靜態方法 
GetProviderOAuthSettings,根據所指定的社群定義,傳回該社群已經定義好的相關參數- 滑鼠右擊核心PCL
XFAuth專案,點選加入>類別>Visual C#>類別 - 在底下名稱欄位內輸入
ProviderManager,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 - 若要使用 Facebook 來進行身分驗證,您需要在 Facebook 中產生一個應用程式,並且取得該應用程式上的
應用程式編號,應用程式密鑰;當然,要到到時候可以讓使用者充分識別這是哪個Facebook應用程式的身分驗證,可以在顯示名稱欄位輸入明確說明這個應用程式的文字。
 
ProviderManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XFAuth
{
    public enum Provider
    {
        Unknown = 0,
        Facebook,
        Google
    }
    public static class ProviderManager
    {
        private static OAuthSettings FacebookOAuthSettings
        {
            get
            {
                return new OAuthSettings
                {
                    ClientId = "1651918068462476",
                    ClientSecret = "b9e46e4c7257b8c6e108dad79df603d3",
                    AuthorizeUrl = "https://m.facebook.com/dialog/oauth/",
                    RedirectUrl = "http://www.facebook.com/connect/login_success.html",
                    AccessTokenUrl = "https://graph.facebook.com/v2.3/oauth/access_token",
                    Scopes = new List<string> {
                        ""
                    }
                };
            }
        }
        private static OAuthSettings GoogleOAuthSettings
        {
            get
            {
                return new OAuthSettings
                {
                    ClientId = "",
                    ClientSecret = "",
                    AuthorizeUrl = "https://accounts.google.com/o/oauth2/auth",
                    RedirectUrl = "https://www.googleapis.com/plus/v1/people/me",
                    AccessTokenUrl = "https://accounts.google.com/o/oauth2/token",
                    Scopes = new List<string> {
                        "openid"
                    }
                };
            }
        }
        public static OAuthSettings GetProviderOAuthSettings(Provider provider)
        {
            switch (provider)
            {
                case Provider.Facebook:
                    {
                        return FacebookOAuthSettings;
                    }
                case Provider.Google:
                    {
                        return GoogleOAuthSettings;
                    }
            }
            return new OAuthSettings();
        }
    }
}
MainPage
修改應用程式首頁頁面內容與 code behind 的程式碼
- 在核心PCL
XFAuth專案,打開MainPage.xaml - 請將底下程式碼覆蓋掉這個檔案所有內容
 - 在這個 XAML 檔案內,定義了一個按鈕,按下後,會切換到 Facebook 的身分驗證頁面,而其他的
Label控制項,則是用來顯示身分驗證的成功與取消的內容。 
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XFAuth"
             x:Class="XFAuth.MainPage">
  <ContentPage.Padding>30</ContentPage.Padding>
  <StackLayout
    Orientation="Vertical"
    VerticalOptions="Center" HorizontalOptions="Center"
  >
    <Button
      x:Name="button登入Facebook"
      Text="登入Facebook"
      />
    <Label
      x:Name="labelToken"
      Text="Token"
      IsVisible="False"
      />
    <Label
      x:Name="labelTokenValue"
      Text=""
      LineBreakMode="WordWrap"
      IsVisible="False"
      />
    <Label
      x:Name="labelTokenError"
      Text=""
      TextColor="Red"
      LineBreakMode="WordWrap"
      IsVisible="False"
      />
  </StackLayout>
</ContentPage>
- 在核心PCL
XFAuth專案,打開MainPage.xaml.cs - 請將底下程式碼覆蓋掉這個檔案所有內容
 - 底下程式碼定義了一個按鈕處理事件,另外,也使用訊息中心的
MessagingCenter.Subscribe訂閱了一個訊息事件,用來聆聽身分驗證的結果,並且做出相關處理。 
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XFAuth
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            button登入Facebook.Clicked += async (s, e) =>
             {
                 await Navigation.PushModalAsync(new LoginPage(Provider.Facebook));
             };
            MessagingCenter.Subscribe<OAuthMessage, string>(this, "Authenticated", (sender, e) =>
            {
                var foo = new Button();
                button登入Facebook.IsVisible = false;
                labelToken.IsVisible = true;
                var fooResult = e as string;
                if (fooResult == "Canceled!" )
                {
                    labelToken.Text = "使用者中斷";
                    labelTokenError.Text = "使用者取消登入Facebook";
                    labelTokenError.IsVisible = true;
                    labelTokenValue.IsVisible = false;
                }
                else if (fooResult.Contains("Authentication Exception"))
                {
                    labelToken.Text = "Exception";
                    labelTokenError.Text = fooResult;
                    labelTokenError.IsVisible = true;
                    labelTokenValue.IsVisible = false;
                }
                else
                {
                    labelToken.Text = "Token";
                    labelTokenValue.Text = fooResult;
                    labelTokenValue.IsVisible = true;
                    labelTokenError.IsVisible = true;
                }
            });
        }
    }
}
Android 原生專案
您需要在原生專案內做些修正。
LoginPageRenderer
- 滑鼠右擊
XFAuth.Droid專案,點選加入>類別>Visual C#>Class - 在底下名稱欄位內輸入
LoginPageRenderer,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 - 這個類別使用了
ExportRenderer屬性,用來定在當LoginPage頁面要出現在 Android 平台的時候,需要客製化的相關畫面內容。 - 若需要使用
Xamarin.Auth套件來進行 OAuth2 身分驗證,您需要產生OAuth2Authenticator這個物件,定義相關處理事件,最後,呼叫auth.GetUI方法,將驗證畫面顯示在螢幕上。 - 在不同驗證處理結果的事件上,將會透過訊息中心提供的
MessagingCenter.Send<OAuthMessage, string>方法,將處理結果透過訊息中心傳送出去;而此時,在核心PCL專案內的首頁頁面內的 code behind 程式碼,就會接收到這個訊息事件。 
LoginPageRenderer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using XFAuth;
using Xamarin.Forms.Platform.Android;
using XFAuth.Droid;
using Xamarin.Auth;
[assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]
namespace XFAuth.Droid
{
    public class LoginPageRenderer : PageRenderer
    {
        LoginPage page;
        bool loginInProgress;
        public LoginPageRenderer()
        {
        }
        protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null ||
                Element == null)
                return;
            page = e.NewElement as LoginPage;
            if (page == null ||
                loginInProgress)
                return;
            loginInProgress = true;
            try
            {
                OAuth2Authenticator auth = new OAuth2Authenticator(
                    page.ProviderOAuthSettings.ClientId, // your OAuth2 client id
                    page.ProviderOAuthSettings.ClientSecret, // your OAuth2 client secret
                    page.ProviderOAuthSettings.ScopesString, // scopes
                    new Uri(page.ProviderOAuthSettings.AuthorizeUrl), // the scopes, delimited by the "+" symbol
                    new Uri(page.ProviderOAuthSettings.RedirectUrl), // the redirect URL for the service
                    new Uri(page.ProviderOAuthSettings.AccessTokenUrl));
                auth.AllowCancel = true;
                auth.Completed += async (sender, args) => {
                    if (args.IsAuthenticated)
                    {
                        var token = args.Account.Properties["access_token"];
                        MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", token);
                    }
                    else
                    {
                        MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", "Canceled!");
                    }
                    await page.Navigation.PopModalAsync();
                    loginInProgress = false;
                };
                auth.Error += (sender, args) =>
                {
                    Console.WriteLine("Error: {0}", args.Exception);
                };
                var activity = this.Context as Activity;
                activity.StartActivity(auth.GetUI(activity));
            }
            catch (Exception ex)
            {
                MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", $"Authentication Exception: {ex.Message}");
            }
        }
    }
}
iOS 原生專案
您需要在原生專案內做些修正。
LoginPageRenderer
- 滑鼠右擊
XFAuth.iOS專案,點選加入>類別>Apple>類別 - 在底下名稱欄位內輸入
LoginPageRenderer,之後,點選新增按鈕 - 請將底下程式碼覆蓋掉這個檔案所有內容
 
LoginPageRenderer.cs
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Auth;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using XFAuth;
using XFAuth.iOS;
[assembly: ExportRenderer(typeof(LoginPage), typeof(LoginPageRenderer))]
namespace XFAuth.iOS
{
    public class LoginPageRenderer : PageRenderer
    {
        LoginPage page;
        bool loginInProgress;
        public LoginPageRenderer()
        {
        }
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null ||
                Element == null)
                return;
            page = e.NewElement as LoginPage;
        }
        public override async void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);
            if (page == null ||
                loginInProgress)
                return;
            loginInProgress = true;
            try
            {
                OAuth2Authenticator auth = new OAuth2Authenticator(
                    page.ProviderOAuthSettings.ClientId, // your OAuth2 client id
                    page.ProviderOAuthSettings.ClientSecret, // your OAuth2 client secret
                    page.ProviderOAuthSettings.ScopesString, // scopes
                    new Uri(page.ProviderOAuthSettings.AuthorizeUrl), // the scopes, delimited by the "+" symbol
                    new Uri(page.ProviderOAuthSettings.RedirectUrl), // the redirect URL for the service
                    new Uri(page.ProviderOAuthSettings.AccessTokenUrl));
                auth.AllowCancel = true;
                // If authorization succeeds or is canceled, .Completed will be fired.
                auth.Completed += async (sender, args) => {
                    if (args.IsAuthenticated)
                    {
                        var token = args.Account.Properties["access_token"];
                        MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", token);
                    }
                    else
                    {
                        MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", "Canceled!");
                    }
                    await DismissViewControllerAsync(true);
                    await page.Navigation.PopModalAsync();
                    loginInProgress = false;
                };
                auth.Error += (sender, args) =>
                {
                    Console.WriteLine("Error: {0}", args.Exception);
                };
                await PresentViewControllerAsync(auth.GetUI(), true);
            }
            catch (Exception ex)
            {
                MessagingCenter.Send<OAuthMessage, string>(new OAuthMessage(), "Authenticated", $"Authentication Exception: {ex.Message}");
            }
        }
    }
}
實際執行畫面
Android 執行結果
請在方案總管內,滑鼠右擊 
XFAuth.Droid 專案,選擇 設定為起始專案,接著按下 F5 開始執行。




iOS 執行結果
請在方案總管內,滑鼠右擊 
XFAuth.iOS 專案,選擇 設定為起始專案,接著按下 F5 開始執行。




Xamarin.Auth,進行社群身分驗證,在這裡,將會使用 Facebook 作為身分驗證的來源。