WooCommerce OAuth 1.0 + JWT authentication with JSReact

My scenario:JWT auth for user queries, which requires to customize .htaccess to recognize the Bearer Authorization heade

My scenario:

  • JWT auth for user queries, which requires to customize .htaccess to recognize the Bearer Authorization header
  • OAuth 1.0a for system queries, and because of .htaccess customization the auth parameters need to go into the body/query string

I use React for the frontend, WP/WooCommerce as backend. I use axios (I'd like to avoid replacing it for lower-level alternatives) and for OAuth 1.0 I use oauth-1.0a.

Now GET requests work fine when the oauth parameters are in the querystring, but I just can't get POST to work in JS and it's driving me nuts.. However, it does work in Postman when the auth parameters go into the POST body, so I know it's not a server issue. I have zero idea what's not working here. Oauth is not the nicest to debug.

Below is a fully working example, if someone would be so nice to take a look at this. I even threw in a temporary consumer_key and consumer_secret with read/write privileges. It's purely dev right now with sample stuff in WooCommerce, can't destroy much at this point. I'll disable the tokens very soon though.

import axios from 'axios'
import OAuth from 'oauth-1.0a'
import crypto from 'crypto-browserify'

const woocommerce = WooCommerceAPI('ck_1da6169c0338088f3e02097a6c9017e800c58176', 'cs_0e090b0a578f48aa8d1096bcd5525415a856836e')
woocommerce
  .post(
    '/customers',
    {username: 'test', 'password': 'test', email: '[email protected]'}
  ).then((response) => {
    console.log(response)
  }).catch( (error) => {
    console.log(error)
  })

class WooCommerceAPI {
  constructor(consumerKey, consumerSecret, timeout = 60000) {
    this.consumerKey = consumerKey
    this.consumerSecret = consumerSecret
    this.timeout = timeout

    this._request = axios.create({
      baseURL: '' + '/wp-json/wc/v3',
      timeout: this.timeout
    })
  }

  _getOAuth() {
    const data = {
      consumer: {
        key: this.consumerKey,
        secret: this.consumerSecret
      },
      signature_method: 'HMAC-SHA1',
      hash_function: (base_string, key) => {
        return crypto
          .createHmac('sha1', key)
          .update(base_string)
          .digest('base64')
      }
    }

    return new OAuth(data)
  }

  // POST responds with 'Sorry, you are not allowed to create resources.'

  post(endpoint, data_params, params = null) {
    const method = 'POST'

    const oauth_params = this._getOAuth().authorize({
      url: '' + '/wp-json/wc/v3' + endpoint,
      method: method,
      data: data_params
    })

    return this._request.post(endpoint, oauth_params)
  }

  // GET requests work

  static _normalizeQueryString(params) {
    if (!params) {
      return ''
    }

    let queryString = ''
    const params_list = []

    for (const p in params) {
      params_list.push(p)
    }
    params_list.sort()

    for (const i in params_list) {
      if (queryString.length) {
        queryString += '&'
      }

      queryString += encodeURIComponent(params_list[i])
        .replace('%5B', '[')
        .replace('%5D', ']')
      queryString += '='
      queryString += encodeURIComponent(params[params_list[i]])
    }

    return '?' + queryString
  }

  get(endpoint, params = null) {
    const method = 'GET'
    const queryString = WooCommerceAPI._normalizeQueryString(params)

    this._request.interceptors.request.use(config => {
      console.log(queryString)
      return config
    })

    const oauth_params = this._getOAuth().authorize({
      url:
        '' +
        '/wp-json/wc/v3' +
        endpoint +
        queryString,
      method: method
    })

    return this._request.get(endpoint, {
      params: { ...params, ...oauth_params }
    })
  }
}

My scenario:

  • JWT auth for user queries, which requires to customize .htaccess to recognize the Bearer Authorization header
  • OAuth 1.0a for system queries, and because of .htaccess customization the auth parameters need to go into the body/query string

I use React for the frontend, WP/WooCommerce as backend. I use axios (I'd like to avoid replacing it for lower-level alternatives) and for OAuth 1.0 I use oauth-1.0a.

Now GET requests work fine when the oauth parameters are in the querystring, but I just can't get POST to work in JS and it's driving me nuts.. However, it does work in Postman when the auth parameters go into the POST body, so I know it's not a server issue. I have zero idea what's not working here. Oauth is not the nicest to debug.

Below is a fully working example, if someone would be so nice to take a look at this. I even threw in a temporary consumer_key and consumer_secret with read/write privileges. It's purely dev right now with sample stuff in WooCommerce, can't destroy much at this point. I'll disable the tokens very soon though.

import axios from 'axios'
import OAuth from 'oauth-1.0a'
import crypto from 'crypto-browserify'

const woocommerce = WooCommerceAPI('ck_1da6169c0338088f3e02097a6c9017e800c58176', 'cs_0e090b0a578f48aa8d1096bcd5525415a856836e')
woocommerce
  .post(
    '/customers',
    {username: 'test', 'password': 'test', email: '[email protected]'}
  ).then((response) => {
    console.log(response)
  }).catch( (error) => {
    console.log(error)
  })

class WooCommerceAPI {
  constructor(consumerKey, consumerSecret, timeout = 60000) {
    this.consumerKey = consumerKey
    this.consumerSecret = consumerSecret
    this.timeout = timeout

    this._request = axios.create({
      baseURL: 'https://boundlessmaps.gis-ops' + '/wp-json/wc/v3',
      timeout: this.timeout
    })
  }

  _getOAuth() {
    const data = {
      consumer: {
        key: this.consumerKey,
        secret: this.consumerSecret
      },
      signature_method: 'HMAC-SHA1',
      hash_function: (base_string, key) => {
        return crypto
          .createHmac('sha1', key)
          .update(base_string)
          .digest('base64')
      }
    }

    return new OAuth(data)
  }

  // POST responds with 'Sorry, you are not allowed to create resources.'

  post(endpoint, data_params, params = null) {
    const method = 'POST'

    const oauth_params = this._getOAuth().authorize({
      url: 'https://boundlessmaps.gis-ops' + '/wp-json/wc/v3' + endpoint,
      method: method,
      data: data_params
    })

    return this._request.post(endpoint, oauth_params)
  }

  // GET requests work

  static _normalizeQueryString(params) {
    if (!params) {
      return ''
    }

    let queryString = ''
    const params_list = []

    for (const p in params) {
      params_list.push(p)
    }
    params_list.sort()

    for (const i in params_list) {
      if (queryString.length) {
        queryString += '&'
      }

      queryString += encodeURIComponent(params_list[i])
        .replace('%5B', '[')
        .replace('%5D', ']')
      queryString += '='
      queryString += encodeURIComponent(params[params_list[i]])
    }

    return '?' + queryString
  }

  get(endpoint, params = null) {
    const method = 'GET'
    const queryString = WooCommerceAPI._normalizeQueryString(params)

    this._request.interceptors.request.use(config => {
      console.log(queryString)
      return config
    })

    const oauth_params = this._getOAuth().authorize({
      url:
        'https://boundlessmaps.gis-ops' +
        '/wp-json/wc/v3' +
        endpoint +
        queryString,
      method: method
    })

    return this._request.get(endpoint, {
      params: { ...params, ...oauth_params }
    })
  }
}
Share Improve this question edited Jul 18, 2019 at 9:40 nilsnolde asked Jul 18, 2019 at 9:31 nilsnoldenilsnolde 1336 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Puuh, after a full day of research in total, I realized the problem..

First of all, the code above is not supposed to work actually. The oauth parameters have to go into the query string apparently. That's how all WooCommerce client APIs do it:

post(endpoint, data_params, params = null) {
  const method = 'POST'

  const oauth_params = this._getOAuth().authorize({
    url: 'https://boundlessmaps.gis-ops' + '/wp-json/wc/v3' + endpoint + WooCommerceAPI._normalizeQueryString(params),
    method: method
  })

  return this._request.post(endpoint, data_params, {
    params: oauth_params
  })
}

So that should work in theory, but still doesn't. The OPTIONS preflight fails. Now that's apparently indeed a server-side problem. Basically WooCommerce has a limitation which I didn't fully understand tbh. The way around is to avoid a preflight, which apparently is possible if you avoid Content-Type: application/json header and a body. Using axios, you just put all parameters in the query string and leave the body and headers empty. This wouldn't work on all servers, but it does with WooCommerce luckily.

References:

  • https://github/woocommerce/woocommerce/issues/14987#issuecomment-300581467
  • https://github/woocommerce/woocommerce/issues/15395#issuecomment-305471553

The working solution is:

post(endpoint, data_params) {
  const method = 'POST'

  const oauth_params = this._getOAuth().authorize({
    url: 'https://boundlessmaps.gis-ops' + '/wp-json/wc/v3' + endpoint + WooCommerceAPI._normalizeQueryString(data_params),
    method: method
  })

  return this._request.post(endpoint, null, {
    params: oauth_params
  })
}

Happy hacking;)

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745313143a4622092.html

相关推荐

  • WooCommerce OAuth 1.0 + JWT authentication with JSReact

    My scenario:JWT auth for user queries, which requires to customize .htaccess to recognize the Bearer Authorization heade

    4小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信