Search code examples
c#magentooauthmagento-rest-api

Magento OAuth customer login


I'm attempting to authenticate against Magento's OAuth system for the purpose of customer mobile login - there are lots of examples for php but I can only find bits and pieces for C#. If possible, I'd like to circumvent use of a browser and perform the login programmatically.

Authentication and use of the REST API is contained within a PCL library - compatible with .NET 4.5, Xamarin.IOS, Xamarin.Android and Windows Phone Silverlight. As a result, I'm having to use portable libraries such as RestSharp.Portable.

Progress to date:

So far, I've been able to retrieve the unauthorised tokens; oauth_token and oauth_token_secret through the use of /oauth/initiate but I can't work out the next two steps /oauth/authorize and /oauth/token. The official Magento guide (http://www.magentocommerce.com/api/rest/authentication/oauth_authentication.html) suggests the next action is to visit /oauth/authorize, allow the user to sign in and then recover two url parameters from the callback url. However, when I attempt this in a browser, it simply redirects me to /index.php/customer/account/login/ - I'm expecting it to redirect to my callback url (localhost) so that I can detect the redirection through the use of the location cookie and then retrieve the tokens that way.

Can anybody advise on how to complete the authorization?

Auth process:

var magento = new MagentoHelper();

await magento.OAuthInitiate();
await magento.OAuthAuthorize();
await magento.OAuthToken();

Magento Operations:

public class MagentoHelper
{
    public readonly string ServiceUrl = ""; // e.g. http://127.0.0.1
    public readonly string MagentoService = ""; // e.g. /magento
    public readonly string ConsumerKey = "";
    public readonly string ConsumerSecret = "";

    public readonly string CustomerUsername = "";
    public readonly string CustomerPassword = "";

    private RestClient _client;
    private MagentoTools _magentoTools;
    private string _oauthToken;
    private string _oauthTokenSecret;

    public MagentoHelper()
    {
        _client = new RestClient(ServiceUrl);
        _magentoTools = new MagentoTools();
    }

    /// <summary>
    /// Retrieve unauthorized tokens from OAuth service
    /// </summary>
    public async Task OAuthInitiate()
    {
        var request = new RestRequest(MagentoService + "/oauth/initiate", HttpMethod.Post);

        string nonce = _magentoTools.GetNonce();
        string timestamp = _magentoTools.GetTimestamp();

        var parameters = new Dictionary<string, string>();
        parameters.Add("oauth_callback", "http://localhost:8888");
        parameters.Add("oauth_consumer_key", ConsumerKey);
        parameters.Add("oauth_nonce", nonce);
        parameters.Add("oauth_signature_method", "HMAC-SHA1");
        parameters.Add("oauth_timestamp", timestamp);
        parameters.Add("oauth_version", "1.0");

        var postUrl = new Uri(ServiceUrl + MagentoService + "/oauth/initiate");
        var signature = OAuth1.GetSignature("POST", postUrl, parameters, ConsumerSecret, "").ToString();
        parameters.Add("oauth_signature", OAuth1.EncodeString(signature));

        string authHeader = _magentoTools.GetAuthorizationHeader(parameters.ToList());
        request.Parameters.Add(new Parameter() { Type = ParameterType.HttpHeader, Name = "Authorization", Value = authHeader });

        try
        {
            var response = await _client.Execute(request);
            string data = Encoding.UTF8.GetString(response.RawBytes, 0, response.RawBytes.Length);
            var queryParameters = _magentoTools.FillFromString(data, true);

            _oauthToken = queryParameters["oauth_token"];
            _oauthTokenSecret = queryParameters["oauth_token_secret"];
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Navigate to OAuth login page, extract form post URL and submit user credentials.
    /// Upon redirection, store the redirection url in preparation for extraction of the new tokens.
    /// </summary>
    public async Task OAuthAuthorize()
    {
        var webClient = new HttpClient();
        var loginUrl = new Uri(_client.BaseUrl.ToString() + MagentoService.Trim('/') + "/oauth/authorize" + "?oauth_token=" + _oauthToken).ToString();

        // Get the login page and find the form post action url and the formkey
        var loginPage = new HtmlDocument();
        using (var responseStream = await webClient.GetStreamAsync(loginUrl))
        {
            loginPage.Load(responseStream);
        }

        var loginForm = loginPage.GetElementbyId("login-form");
        var postUrl = loginForm.GetAttributeValue("action", string.Empty);

        // Post the user credentials to the post action url
        var postRequest = (HttpWebRequest)WebRequest.Create(postUrl);
        postRequest.Method = "POST";
        postRequest.ContentType = "application/x-www-form-urlencoded";
        postRequest.CookieContainer = new CookieContainer();

        var postData = String.Format("login%5busername%5d={0}&login%5bpassword%5d={1}&oauth_token={2}", CustomerUsername, CustomerPassword, _oauthToken);
        byte[] postDataBytes = Encoding.UTF8.GetBytes(postData);

        using (var requestStream = await postRequest.GetRequestStreamAsync())
        {
            requestStream.Write(postDataBytes, 0, postDataBytes.Length);
        }
        using (var response = await postRequest.GetResponseAsync())
        {
            if (response.Headers["location"] == null) throw new Exception("location is null");
        }
    }

    public async Task OAuthToken()
    {
        await Task.Yield();
    }
}

Solution

  • I have created a C# client for the Magento REST API. It is not a PCL, but should work. It uses RestSharp. Code is at https://github.com/nickvane/Magento-RestApi

    The client is made for backend integration with Magento without user interaction. The login procedure actually does some screen scraping. The authentication process is explained in detail at https://github.com/nickvane/Magento-RestApi/wiki/Authentication-steps

    If the library cannot be compiled directly to a PCL, then you can surely copy lots of code. And any fixes are welcome as pull-request.