DotNetOpenAuth CTP - Facebook bad request

前端 未结 5 596
灰色年华
灰色年华 2020-12-23 18:41

I am trying to use the CTP to connect with Facebook over OAuth 2.0.

I can get the initial request to Facebook working OK, but when it comes back and we call:

<
5条回答
  •  轮回少年
    2020-12-23 18:51

    After messing around with an upgrade of DotNetOpenAuth for a long while and not experiencing any luck connecting to Facebook, I too put together some code to support Facebook login from within my ASP.NET MVC app.

    Firstly, code such as this should go in a controller somewhere.

    // You call this action to initiate the process with Facebook
    public ActionResult FacebookLogIn()
    {
        return CreateFacebookClient().RequestAuthorisation();
    }
    
    // Facebook will call you back here
    public ActionResult FacebookAuthorisationResponse()
    {
        var facebookClient = CreateFacebookClient();
        var authorisationResponse = facebookClient.HandleAuthorisationResponse();
    
        if (authorisationResponse.IsSuccess)
        {
            var accessToken = authorisationResponse.AccessToken;
    
            // TODO do whatever you want to do with your access token here
    
            return Redirect("SomeUrl");
        }
    
        // TODO handle the error somehow
        return Content(authorisationResponse.ErrorMessage);
    }
    
    private FacebookClient CreateFacebookClient()
    {
        const string clientId = "xxxxxxxxxxxxxxx";
        const string appSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    
        var redirectUrl = Url.Action("FacebookAuthorisationResponse", null, null, "http");
    
        return new FacebookClient(clientId, appSecret, redirectUrl);
    }
    

    That's pretty much all you need to do with your code. Once you have that access token, you can do things like this:

    // Get basic information for this user
    var basicInfoUrl = string.Format("https://graph.facebook.com/me?access_token={0}", Uri.EscapeDataString(accessToken.TokenString));
    var json = new WebClient().DownloadString(basicInfoUrl);
    

    The code that supports the relatively simple stuff above is here. You can just dump all of this in a file in your project:

    // Drew Noakes, http://drewnoakes.com
    // Created 08/08/2012 22:41
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    
    namespace DrewNoakes.Facebook.Mvc
    {
        public sealed class FacebookClient
        {
            private readonly string _clientId;
            private readonly string _appSecret;
            private readonly string _authorisationResponseUrl;
    
            public IFacebookClientStateManager StateManager { get; set; }
    
            public FacebookClient(string clientId, string appSecret, string authorisationResponseUrl)
            {
                _clientId = clientId;
                _appSecret = appSecret;
                _authorisationResponseUrl = authorisationResponseUrl;
    
                StateManager = MemoryStateManager.Instance;
            }
    
            public ActionResult RequestAuthorisation(string[] permissions = null)
            {
                // First step is to redirect the visitor's browser to Facebook
    
                var state = StateManager.GetState();
    
                var url = string.Format("https://www.facebook.com/dialog/oauth?client_id={0}&redirect_uri={1}&scope={2}&state={3}",
                    _clientId, Uri.EscapeDataString(_authorisationResponseUrl), permissions == null ? string.Empty : string.Join(",", permissions), state);
    
                return new RedirectResult(url, permanent: false);
            }
    
            public AuthorisationResponse HandleAuthorisationResponse()
            {
                var queryString = HttpContext.Current.Request.QueryString;
    
                // Ensure returned state is expected
                if (!StateManager.IsValidState(queryString["state"]))
                    return AuthorisationResponse.Error("Invalid state");
    
                // TODO handle case where user declined: YOUR_REDIRECT_URI?error_reason=user_denied&error=access_denied&error_description=The+user+denied+your+request.&state=YOUR_STATE_VALUE
    
                var code = queryString["code"];
                var url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&code={3}&client_secret={2}",
                    _clientId, Uri.EscapeDataString(_authorisationResponseUrl), _appSecret, Uri.EscapeDataString(code));
    
                var client = new WebClient { Proxy = null };
                var responseBody = client.DownloadString(url);
    
                // HTTP 200: access_token=USER_ACCESS_TOKEN&expires=NUMBER_OF_SECONDS_UNTIL_TOKEN_EXPIRES
                // HTTP 400: TODO handle JSON error reponse: { "error": { "type": "OAuthException", "message": "Error validating verification code." } }
    
                var response = HttpUtility.ParseQueryString(responseBody);
                var accessToken = response["access_token"];
                var expiresSecondsString = response["expires"];
    
                int expiresSeconds;
                if (!int.TryParse(expiresSecondsString, out expiresSeconds))
                    return AuthorisationResponse.Error("Unable to parse expiration time");
                var expiresAtUtc = DateTime.UtcNow.AddSeconds(expiresSeconds);
    
                return AuthorisationResponse.Success(accessToken, expiresAtUtc);
            }
        }
    
        public class AuthorisationResponse
        {
            public bool IsSuccess { get; private set; }
            public AccessToken AccessToken { get; private set; }
            public string ErrorMessage { get; private set; }
    
            private AuthorisationResponse() { }
    
            public static AuthorisationResponse Error(string errorMessage)
            {
                return new AuthorisationResponse { IsSuccess = false, ErrorMessage = errorMessage };
            }
    
            public static AuthorisationResponse Success(string accessToken, DateTime expiresAtUtc)
            {
                return new AuthorisationResponse { IsSuccess = true, AccessToken = new AccessToken(accessToken, expiresAtUtc) };
            }
        }
    
        public struct AccessToken
        {
            public string TokenString { get; private set; }
            public DateTime ExpiresAtUtc { get; private set; }
    
            public AccessToken(string tokenString, DateTime expiresAtUtc)
                : this()
            {
                if (tokenString == null)
                    throw new ArgumentNullException("tokenString");
                TokenString = tokenString;
                ExpiresAtUtc = expiresAtUtc;
            }
        }
    
        public interface IFacebookClientStateManager
        {
            string GetState();
    
            bool IsValidState(string state);
        }
    
        /// 
        /// The default implementation of .
        /// 
        public sealed class MemoryStateManager : IFacebookClientStateManager
        {
            private static readonly IFacebookClientStateManager _instance = new MemoryStateManager();
    
            public static IFacebookClientStateManager Instance
            {
                get { return _instance; }
            }
    
            private readonly Dictionary _stateTimes = new Dictionary();
    
            public string GetState()
            {
                var state = Guid.NewGuid().ToString("N");
                _stateTimes[state] = DateTime.UtcNow;
                return state;
            }
    
            public bool IsValidState(string state)
            {
                var isValid = _stateTimes.Remove(state);
    
                // Remove any keys that have not been accessed within a given period
                var staleKeys = _stateTimes.Where(pair => pair.Value < DateTime.UtcNow.AddMinutes(-30)).Select(pair => pair.Key).ToList();
    
                foreach (var staleKey in staleKeys)
                    _stateTimes.Remove(staleKey);
    
                return isValid;
            }
        }
    }
    

    I threw this together quickly tonight, but will come back later and patch it if I find issues. It's working really well on my site right now though.

    There are a couple of TODOs related to robust error response handling.

提交回复
热议问题