wordpress.com oauth2 and ASP.NET dotnetopenauth provider


Here is a good start on making extending the providers in an ASP.NET dotnetopenauth-enabled project to talk to the WordPress.com OAUTH and API endpoints, as part of websso.

namespace WebRole1
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Net;
    using System.Security.Authentication;
    using System.Text;
    using System.Web;
    using System.Web.Script.Serialization;
    using DotNetOpenAuth.AspNet;
    using DotNetOpenAuth.AspNet.Clients;
    using DotNetOpenAuth.Messaging;
    using DotNetOpenAuth.OAuth;
    using DotNetOpenAuth.OAuth.ChannelElements;
    using DotNetOpenAuth.OAuth.Messages;
    using Newtonsoft.Json.Linq;
    using System.Runtime.Serialization.Json;

    using System.Collections;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using Newtonsoft.Json;
    
       
    public class WordPressComCustomClient : OAuth2Client
    {
        private OAuth2AccessTokenData data;

        private InMemoryOAuthTokenManager imoatm = null;
        private InMemoryOAuthTokenManager rsoatm = null; // resource server
        private string ep;
        private string blogsite;
        
        public WordPressComCustomClient(string site, string consumerKey, string consumerSecret, string pn, string RSKey, string RSSecret ) :
            this(
            site,
            "https://localhost:9031/", 
            pn,
            consumerKey, 
            consumerSecret,
            RSKey, 
            RSSecret
            ) { }

        public WordPressComCustomClient(string site, string accountlinkingURL, string pn, string consumerKey, string consumerSecret, string RSKey, string RSSecret) :
            base("WP_" + pn)
        {
            imoatm = new InMemoryOAuthTokenManager(consumerKey, consumerSecret);
            rsoatm = new InMemoryOAuthTokenManager(RSKey, RSSecret);
            ep = accountlinkingURL;
            blogsite = site;
        }

        protected override Uri GetServiceLoginUrl(Uri returnUrl)
        {
            var coll = HttpUtility.ParseQueryString(returnUrl.Query);
            string __sid__ = coll["__sid__"];

            const string formatState = "{0}";
            var state = String.Format(formatState, __sid__);

            UriBuilder u = new UriBuilder(returnUrl);
            u.Query = "";

            UriBuilder authzUrl = new UriBuilder(ep.ToString());
            authzUrl.Path = "/oauth2/authorize";
            authzUrl.Query = string.Format("response_type=code&redirect_uri={0}&client_id={1}&client_secret={2}&state={3}&blog={4}",
                                        HttpUtility.UrlEncode(u.Uri.ToString()), 
                                        imoatm.ConsumerKey,
                                        imoatm.ConsumerSecret,
                                        HttpUtility.UrlEncode(state),
                                        blogsite
                                        );
            return authzUrl.Uri;
        }

        protected override IDictionary GetUserData(string accessToken) 
        {
            Dictionary extraData = new Dictionary();

            ServicePointManager.ServerCertificateValidationCallback
                += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
                {
                    return true;
                };

            WebRequest tokenRequest = WebRequest.Create("https://public-api.wordpress.com/rest/v1/me/?pretty=1");
            tokenRequest.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + accessToken);
            tokenRequest.Method = "GET";

            HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
            if (tokenResponse.StatusCode == HttpStatusCode.OK)
            {
                using (Stream responseStream = tokenResponse.GetResponseStream())
                {
                    StreamReader s = new StreamReader(responseStream);
                    JObject j = JObject.Load(new JsonTextReader(s));

                    foreach (var el in j)
                    {                        
                        extraData.Add(el.Key, el.Value.ToString());
                        if (el.Key.StartsWith("username"))
                        {
                            extraData.Add("id", el.Value.ToString());
                        }
                    }
                }
            }

            extraData.Add("accesstoken", accessToken);

            return extraData;
        }

        protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
        {
            const string oauthsts = "/oauth2/token";
            UriBuilder tokenissuingpoint = new UriBuilder(ep);
            tokenissuingpoint.Path = oauthsts;


            ServicePointManager.ServerCertificateValidationCallback 
                += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
                {
                   return true;
                };


            UriBuilder retpath = new UriBuilder(returnUrl);
            retpath.Query = "";

            UriBuilder entity = new UriBuilder();
                entity.AppendQueryArgument( "client_id", imoatm.ConsumerKey);
				entity.AppendQueryArgument( "redirect_uri", retpath.ToString());
				entity.AppendQueryArgument( "client_secret", imoatm.ConsumerSecret );
				entity.AppendQueryArgument( "code", authorizationCode);
				entity.AppendQueryArgument( "grant_type", "authorization_code");
            WebRequest tokenRequest = WebRequest.Create(tokenissuingpoint.Uri);

            //tokenRequest.Headers.Add(HttpRequestHeader.Authorization, "basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(imoatm.ConsumerKey + ":" + imoatm.ConsumerSecret)));
            tokenRequest.ContentType = "application/x-www-form-urlencoded";
            tokenRequest.ContentLength = entity.Query.Length - 1;
            tokenRequest.Method = "POST";
            using (Stream requestStream = tokenRequest.GetRequestStream())
            {
                var writer = new StreamWriter(requestStream);
                writer.Write(entity.Query.Substring(1));
                writer.Flush();
            }

            HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
            if (tokenResponse.StatusCode == HttpStatusCode.OK)
            {
                using (Stream responseStream = tokenResponse.GetResponseStream())
                {
                    var serializer = new DataContractJsonSerializer(typeof(OAuth2AccessTokenData));
                    var tokenData = (OAuth2AccessTokenData)serializer.ReadObject(responseStream);

                    if (tokenData != null)
                    {
                        data = tokenData;
                        return tokenData.AccessToken;
                    }
                }
            }

            return null;
        }

        public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
        {
            var coll = HttpUtility.ParseQueryString(returnUrl.Query);
            string __sid__ = coll["__sid__"];

            var contextCookie = new HttpCookie(__sid__, returnUrl.AbsoluteUri.ToString());
            context.Response.Cookies.Add(contextCookie);

            base.RequestAuthentication(context, returnUrl);
        }

        public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
        {
            AuthenticationResult res = base.VerifyAuthentication(context, returnPageUrl);
            if (res.IsSuccessful && res.ExtraData != null)
            {
                var contextId = context.Request.QueryString["__sid__"];
                var ctxCookie = context.Request.Cookies[contextId];
                if (ctxCookie == null)
                {
                    throw new InvalidOperationException("Context cookie not found");
                }

                var originalRequestUri = new Uri(ctxCookie.Value);
                var contextCookie = new HttpCookie(contextId)
                {
                    Expires = DateTime.UtcNow.AddDays(-1)
                };

                var extraData = res.ExtraData.IsReadOnly
                                    ? new Dictionary(res.ExtraData)
                                    : res.ExtraData;

                var nvc = HttpUtility.ParseQueryString(originalRequestUri.Query);

                extraData.Add("ReturnUrl", nvc["ReturnUrl"]);

                res = new AuthenticationResult(
                    res.IsSuccessful,
                    res.Provider,
                    res.Provider + extraData["id"],
                    extraData["id"],
                    extraData);
            }

            return res;
        }
          
    }
}
 

to make use of it, note that the wordpress AS returns control to the Asp.NET pattern’s “RegisterExternalLogin” handler, as is normal. However, because of the way wordpress.com token issuing and authorization endpoints handle the registered value of the redirect URI, we were not able to let the authorization endpoint redirect back to the handler with the (required) additional query string parameters – used within RegisterExternalLogin to locate the outstanding request. However, OAUTH itself saves the day – by handling state – within which we can transfer cookie names – allowing RegisterExternalLogin to recapture the full URI required to resume processing of the inbound OAUTH code message.


      private void ProcessProviderResult()
        {
            // Process the result from an auth provider in the request
            ProviderName = OpenAuth.GetProviderNameFromCurrentRequest();

            if (String.IsNullOrEmpty(ProviderName))
            {
                var contextId = Request.QueryString["state"];
                var ctxCookie = Request.Cookies[contextId];
                if (ctxCookie == null)
                {
                    throw new InvalidOperationException("WordPress Context cookie not found");
                }
                UriBuilder entity = new UriBuilder(ctxCookie.Value);
                foreach (var s in Request.QueryString.AllKeys)
                {
                    entity.AppendQueryArgument(s, Request.QueryString[s]);
                }

                Response.Redirect(entity.Uri.AbsoluteUri.ToString());
            }
....

 
Advertisements

About home_pw@msn.com

Computer Programmer who often does network administration with focus on security servers. Very strong in Microsoft Azure cloud!
This entry was posted in oauth. Bookmark the permalink.

One Response to wordpress.com oauth2 and ASP.NET dotnetopenauth provider

  1. Pingback: my attempt at a wordpress.com OAUTH2 plugin | BlogoSfera

Comments are closed.