Monday, May 7, 2012

[FB] Facebook C# SDK Server-Side Authentication

fb_c_sharp之前寫了新版Facebook C# SDK的介紹,但是是使用Java Script去取得Access Token,在某些情況下很能會因為JS支援性而產生問題,於是還是需要有server side的認證方式取得access token,才比較穩當的在各種情況下都可以取得所需資訊,於是就發了這篇啦。


要避開使用Facebook JavaScript SDK,我找了一下,看來就是使用Server-Side Authentication這個方式才有辦法,這個server-side認證方式基本上是走OAuth 2.0的協定去跑,在2010時我已寫過文章"[Facebook] C# SDK Authentication"介紹過,串接方式蠻類似的,所以我就不會在這邊做過多介紹,不過上次沒貼code,這次會貼一下方便大家參考。



1. Redirect the user to the OAuth Dialog
第一步是將網頁轉到下面這個網址
https://www.facebook.com/dialog/oauth
並帶下列參數
client_id : 應用程式的App ID。
redirect_uri : 認證後要導回的Url。
scope : 向使用者取得的授權。
state : (非必要)可自行運用的不重複字串。

2. The user is prompted to authorize your application
若使用者尚未授權此APP,或是scope有修改,則會出現下面畫面讓使用者確認。
FBSDK06


3. The user is redirected back to your app
使用者授權的話,會向 redirect_uri 丟出下面參數回傳
state : 剛剛傳去的state(如果你有傳)
code : Facebook傳回產生的code

使用者拒絕的話,則會丟這些參數
error_reason=user_denied
error=access_denied
error_description=The+user+denied+your+request.
state : 剛剛傳去的state(如果你有傳)

像是這樣
FBSDK08


4. Exchange the code for a user access token
取得code後,再將code連同下列參數丟到這網址去(不是redirect)
https://graph.facebook.com/oauth/access_token
client_id : 應用程式的App ID。
redirect_uri : 認證後要導回的Url(需與Step 1 一樣)。
client_secret : 應用程式的App Secret。
code : Facebook傳回產生的code

成功的話,該頁面會生出下列資訊,裡面就是我們所要的access_token與這個token的有效時間expries
FBSDK07


5. Make requests to the Graph API
有Access Token後,就可以去撈facebook上使用者有授權的資訊,如同範例上的連結這樣使用。
https://graph.facebook.com/me?access_token=YOUR_USER_ACCESS_TOKEN



最後就是範例啦,請記得要有Facebook C# SDK才能用

FBTest.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="FBTest.aspx.cs" Inherits="FBTest" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
<script src='http://www.google.com/jsapi' type='text/javascript'></script>
<script language='javascript' type='text/javascript'>    google.load("jquery", "1.7.1");</script>
<script type='text/javascript'>
    $(document).ready(function () {
        window.fbAsyncInit = function () {
            FB.init({
                appId: 'YOUR_APP_ID', // App ID
                status: true, // check login status
                cookie: true, // enable cookies to allow the server to access the session
                xfbml: true  // parse XFBML
            });
        };
        // Load the SDK Asynchronously
        (function (d, s, id) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) return;
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=YOUR_APP_ID";
        fjs.parentNode.insertBefore(js, fjs);
        } (document, 'script', 'facebook-jssdk'));
    });
</script>
</head>
<body>
<div id="fb-root"></div>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="Button1" runat="server" Text="Auth" onclick="Button1_Click" />
        <div class="fb-login-button" data-show-faces="true" data-width="400" data-max-rows="1"></div>
        <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>


FBTest.aspx.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Web;
using Facebook;

public partial class FBTest : System.Web.UI.Page
{
    #region Props
    public string AppId = "YOUR_APP_ID";
    public string AppSecret = "YOUR_APP_SECRET";
    public string RedirectUri = "http://localhost:45353/WebTest/FBTest.aspx";
    public string Scope = "email,publish_stream,user_photos";
    public string ReturnCode
    {
        get
        {
            return (string.IsNullOrEmpty(Request.QueryString["code"])) ? null : Request.QueryString["code"];
        }
    }
    public string ReturnError
    {
        get
        {
            return (string.IsNullOrEmpty(Request.QueryString["error"])) ? null : Request.QueryString["error"];
        }
    }
    public int Stages
    {
        get
        {
            if (Session["AuthStage"] != null)
                return int.Parse(Session["AuthStage"].ToString());
            else
                return (int)AuthStage.Initial;
        } 
        set { Session["AuthStage"] = value; }
    }
    #endregion
    protected void Page_Load(object sender, EventArgs e)
    {
        if(Stages==(int)AuthStage.Initial)
        {
            if(!string.IsNullOrEmpty(ReturnCode))
            {
                Stages = (int) AuthStage.GotCode;
                Session["AccessToken"] = GetAccessToken();
                CallFb();
            }
            if (!string.IsNullOrEmpty(ReturnError))
            {
                Stages = (int)AuthStage.GotError;
                Label1.Text = "Error when user is redirected back";
            }
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        Stages = (int) AuthStage.Initial;
        string url = "https://www.facebook.com/dialog/oauth?client_id=" + AppId + "&redirect_uri=" + RedirectUri + "&scope="+Scope;
        Response.Redirect(url);
    }
    private void CallFb()
    {
        if (Session["AccessToken"] != null)
        {
            var accessToken = Session["AccessToken"].ToString();
            var client = new FacebookClient(accessToken);
            try
            {
                dynamic result = client.Get("me", new { fields = "name,id,updated_time,picture" });
                string name = result.name;
                string id = result.id;
                string updatedTime = result.updated_time;
                Label1.Text = name + " , " + id + " , " + updatedTime + " , " + result.picture;
            }
            catch (FacebookOAuthException ex)
            {
                Session.Remove("AccessToken");
                HttpRuntime.Cache.Remove("access_token");
                Label1.Text = (ex.Message.IndexOf("OAuthException - #190") > -1) ? "User has not authorized" : "unknow error";
            }
        }
        else
        {
            var client = new FacebookClient();
            dynamic me = client.Get("zuck");
            string firstName = me.first_name;
            string lastName = me.last_name;
            Label1.Text = firstName + " , " + lastName;
        }
    }
    private Dictionary<string, string> GetOauthTokens(string code)
    {
        Dictionary<string, string> tokens = new Dictionary<string, string>();
        string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&client_secret={2}&code={3}",AppId, RedirectUri, AppSecret, code);
        HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            StreamReader reader = new StreamReader(response.GetResponseStream());
            string retVal = reader.ReadToEnd();

            Label1.Text += retVal;

            foreach (string token in retVal.Split('&'))
            {
                tokens.Add(token.Substring(0, token.IndexOf("=")),
                    token.Substring(token.IndexOf("=") + 1, token.Length - token.IndexOf("=") - 1));
            }
        }
        return tokens;
    }
    private string GetAccessToken()
    {
        if (HttpRuntime.Cache["access_token"] == null)
        {
            Dictionary<string, string> args = GetOauthTokens(Request.Params["code"]);
            HttpRuntime.Cache.Insert("access_token", args["access_token"], null, DateTime.Now.AddMinutes(Convert.ToDouble(args["expires"])), TimeSpan.Zero);
        }
        return HttpRuntime.Cache["access_token"].ToString();
    }
    public enum AuthStage
    {
        Initial,
        GotCode,
        GotError,
        GotAccesstoken
    } 
}

貼出前我有稍微修改一下,應該還是會動啦。

1 comment:

  1. After working alongside some of the replica christian louboutin uk best French shoe designers,replica men louboutin loafer Christian Louboutin launched his own label in 1991. Designed in Paris and crafted in Italy by skilled artisans, the label’s sophisticated heels, sneakers and flats fuse style with comfort. An A-list favourite, its iconic red lacquered soles are internationally recognised.

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...