Search code examples
c#oauth-2.0openiddotnetopenauthyahoo

How to use DotNetOpenAuth.AspNet to provide social login while i'm using MVC3


I'm trying to use YahooJapan(Yconnect) as socail login in my WebSite and i decide to use the DotNetOpenAuth.AspNet.But i cannot get it work(actrully,the first step that to access the Authorization endpoint) , i always get a null code in the VerifyAuthentication method.I guess there maybe something wrong in my AuthorizationForAspNet method (maybe the order of the steps is wrong). Please help me, thanks.

the following is the Controller:

    public virtual ActionResult ExternalLogin(int providerNo, string returnAction = "Login")
    {
        if (provider == Provider.Yahoo)
        {
            state = OAuth2Provider.AuthorizationForAspNet(provider);
        }
    }

the following is the AuthorizationForAspNet method in Class OAuth2Provider: (the url"https://****/User/ExternalLogin?providerNo=4"is my returnURI that i decided in the YahooDeveloperConsole)**

    public static OAuth2State AuthorizationForAspNet(Provider provider)
    {
        var YahooClient = new YahooClient(
        clientId: clients[provider].AppID, clientSecret: clients[provider].AppSecret);

        OAuthWebSecurity.RegisterClient(YahooClient);

        OAuthWebSecurity.RequestAuthentication("yahoo");

        AuthenticationResult result = YahooClient.VerifyAuthentication(new HttpContextWrapper(HttpContext.Current), new System.Uri("https://******/User/ExternalLogin?providerNo=4"));
    }

the following is my customized YahooClient:

public class YahooClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client
{
    private static string Escape(string target)
    {
        return Uri.EscapeDataString(target);
    }

    private const string AuthorizationEndpoint = "https://auth.login.yahoo.co.jp/yconnect/v1/authorization";
    private const string TokenEndpoint = "https://auth.login.yahoo.co.jp/yconnect/v1/token";
    private const string UserDetailsEndPoint = "https://userinfo.yahooapis.jp/yconnect/v1/attribute";

    private readonly string _clientId;

    private readonly string _clientSecret;

    public YahooClient(string clientId, string clientSecret): base("yahoo") 
    {
        if (string.IsNullOrWhiteSpace(clientId))
            throw new ArgumentNullException("clientId");

        if (string.IsNullOrWhiteSpace(clientSecret))
            throw new ArgumentNullException("clientSecret");

        _clientId = clientId;
        _clientSecret = clientSecret;

    }

    protected override Uri GetServiceLoginUrl(Uri returnUrl)
    {
        string sid = String.Empty;
        if (returnUrl.ToString().Contains("__sid__"))
        {
            int index = returnUrl.ToString().IndexOf("__sid__") + 8;
            int len = returnUrl.ToString().Length;
            sid = returnUrl.ToString().Substring(index, len - index - 1);
        }

        string redirectUri = (returnUrl.ToString().Contains("&"))?(returnUrl.ToString().Substring(0, returnUrl.ToString().IndexOf("&")) ):(returnUrl.ToString());

        var builder = new UriBuilder(AuthorizationEndpoint);
        builder.Query = string.Join("&",
            "response_type=code",
            "client_id=" + Escape(_clientId),
            "redirect_uri=" + Escape(redirectUri),
            "scope=" + Escape("openid profile"),
            "state=" + sid);
        return builder.Uri;
    }

    protected override IDictionary<string, string> GetUserData(string accessToken)
    {
        // UserInfo API
        var builder = new UriBuilder(UserDetailsEndPoint);
        builder.Query = string.Join("&", "schema=openid");

        var request = WebRequest.Create(builder.Uri);

        request.Headers.Add("Authorization", "Bearer " + Escape(accessToken));

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            if (response.StatusCode != HttpStatusCode.OK) return null;

            var json = response.GetResponseBody();
            var userData = JObject.Parse(json);
            if (userData == null) return null;

            return new Dictionary<string, string>()
                .AddIfNotEmpty("id", (string)userData["user_id"])
                .AddIfNotEmpty("username", (string)userData["name"]);
        }
    }

    protected override string QueryAccessToken(Uri returnUrl, string authorizationCode)
    {
        var request = WebRequest.Create(TokenEndpoint);
        request.ContentType = "application/x-www-form-urlencoded";
        request.Method = "POST";

        // Basic
        var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes(_clientId + ":" + _clientSecret));
        request.Headers.Add("Authorization", "Basic " + credentials);

        var postData = string.Join("&",
            "grant_type=authorization_code",
            "code=" + Escape(authorizationCode),
            "redirect_uri=" + Escape(returnUrl.AbsoluteUri));

        request.ContentLength = postData.Length;
        using (var stream = request.GetRequestStream())
        using (var writer = new StreamWriter(stream))
        {
            writer.Write(postData);
            writer.Flush();
        }

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            if (response.StatusCode != HttpStatusCode.OK) return null;

            var json = response.GetResponseBody();
            var tokenData = JObject.Parse(json);
            if (tokenData == null) return null;

            return (string)tokenData["access_token"];
        }

    }

    public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl)
    {
        string code = context.Request.QueryString["code"];
        string u = context.Request.Url.ToString();

        if (string.IsNullOrEmpty(code))
            return AuthenticationResult.Failed;

        string accessToken = this.QueryAccessToken(returnPageUrl, code);
        if (accessToken == null)
            return AuthenticationResult.Failed;

        IDictionary<string, string> userData = this.GetUserData(accessToken);
        if (userData == null)
            return AuthenticationResult.Failed;

        string id = userData["user_id"];
        string name = string.Empty;

        return new AuthenticationResult(
            isSuccessful: true, provider: "yahoo", providerUserId: id, userName: name, extraData: userData);
    }

    public override void RequestAuthentication(HttpContextBase context, Uri returnUrl)
    {
        base.RequestAuthentication(context, returnUrl);
    }
}

Solution

  • this works.

            if (provider == Provider.Yahoo)
            {
                DotNetOpenAuth.AspNet.AuthenticationResult result;
                var YahooClient = new YahooClient(clientId: clients[provider].AppID, clientSecret: clients[provider].AppSecret);
                var context = HttpContext.Current;
    
                //the second time
                if (!String.IsNullOrEmpty(context.Request.QueryString.ToString()) && (context.Request.QueryString.ToString().Contains("code")))
                {
                    result = YahooClient.VerifyAuthentication(new HttpContextWrapper(context), new Uri(returnUrl));
                    if (result.IsSuccessful)
                    {
                        //Success;
                    }
                }
                //the first time
                else
                {
                    OAuthWebSecurity.RegisterClient(YahooClient, "yahoo");
                    OAuthWebSecurity.RequestAuthentication("yahoo");
                }
            }