I'm currently trying to write a Data Extraction Application for RingCentral to dump CSVs to AWS as part of a Data Warehousing project. I've reviewed several pieces of their documentation outlining how to authenticate using the Password Flow, but there seems to be some missing pieces based on their examples in the documentation here.
In the example listed under "Password Flow" There is an authentication header that is never mentioned and the value that is included is pulled out out of thin air by the author, and then never mentioned in the authentication steps below it. I can validate that my application meets the Private, Server-Only Requirements with scope 'Read Call Logs'. Below is a screenshot of my settings: [Link to Image because I don't have enough reputation...ugh][2]
Using the Sandbox credentials and endpoint for development, here's what I've tried so far:
url = 'https://platform.devtest.ringcentral.com/restapi/oauth/token'
headers = {'Content-Type'=>'application/x-www-form-urlencoded'}
body = {:grant_type=>"password", :username=>"13133982093", :password=><OUR_SANDBOX_PASSWORD>}
resp = HTTParty.post(url, headers: headers, body: URI.encode_www_form(body))
The printed response:
#<HTTParty::Response:0x7ff29588a420 parsed_response={"error"=>"invalid_client", "error_description"=>"Invalid client: ", "errors"=>[{"errorCode"=>"OAU-153", "message"=>"Invalid client: ", "parameters"=>[{"parameterName"=>"client_id", "parameterValue"=>""}]}]}, @response=#<Net::HTTPBadRequest 400 Bad Request readbody=true>, @headers={"server"=>["nginx/1.10.2"], "date"=>["Tue, 20 Jun 2017 22:05:36 GMT"], "content-type"=>["application/json;charset=utf-8"], "transfer-encoding"=>["chunked"], "connection"=>["close"], "x-application-context"=>["application:8080"], "content-language"=>["en"], "www-authenticate"=>["Bearer realm=\"RingCentral REST API\", error=\"invalid_client\", error_description=\"Invalid client: \""], "rcrequestid"=>["96aaacb0-5604-11e7-97a9-005056bb594d"], "aceroutingkey"=>["sjc11-c01-ace01.c83d65c2-46d3-11e7-ab8e-005056a73f60"], "x-server-name"=>["sjc06-c01-hlb02"], "x-request-time"=>["0.010"], "x-upstream-server"=>["10.24.22.193:8080"], "x-upstream-status"=>["400"], "x-upstream-htime"=>["0.010"], "x-upstream-rtime"=>["1497996336.573"], "x-upstream-ctime"=>["0.000"], "x-tcpinfo"=>["1000, 500, 10, 28960"], "routingkey"=>["SJC11P01"]}>
I've tried Google Searching for the Error Code OAU-153 and it's completely undocumented. I've even included things like an Authorization Header produced from the client_id and client_secret (a.k.a - app_key and app_secret) like such: auth= {'Authorization': "Basic #{ Base64.encode64("#{client_id}:#{client_secret}") }"}
Which was included in a call like: resp = HTTParty.post(token_url, headers: headers, body: URI.encode_www_form(token_body), auth: auth)
But this still produced the same error as above with error_description "Invalid Client: "
, and error_code OAU-153
.
Additionally, due to project requirements the Ring Central Ruby SDK causes dependency conflicts with very important gems in my current application, thus we're having to write new Auth code for this data source.
At this point I'm lost. I've heavily validated that my keys, username and password are all correct. I've included the username
phone number value as a sanity check for formatting. I'm essentially left with 2 questions I cannot answer:
1) Am I approching this correctly? Have I selected the correct roles and permissions for this application? Do I need to create a new one with a different Auth Flow and Privacy settings?
2) Am I using the right tools for this? Is HTTParty possibly a culprit in how it's sending my requests? Digging in the Repository hasn't brought much to light in the time I've spent.
EDIT:
We made changes to the project which allowed us to use the SDK for authentication like this:
client = RingCentralSdk::REST::Client.new do | config |
config.app_key = ENV['RC_CLIENT_ID']
config.app_secret = ENV['RC_CLIENT_SECRET']
config.server_url = @server_url
config.username = @account_num
config.password = @password
config.extension = @extension_id
Then we store token info as a hash by doing: token_hash = client.token.to_hash
An example of the hash can be seen below:
{"token_type"=>"bearer", "refresh_token_expires_in"=>604800,
"scope"=>"ReadCallLog", "owner_id"=>[COMPANY's OWNER_ID],
"endpoint_id"=>"N0UV835BQ2C_Bz2ofJA1Eg",
:access_token=> [REMOVED FOR SECURITY],
:refresh_token=> [REMOVED FOR SECURITY],
:expires_at=>1498081681}
However, now I cannot perform 2 operations: a simple authenticated request or a token Refresh.
Attempting simple call-log
request:
uri = "https://platform.devtest.ringcentral.com/restapi/v1.0/account/<phone_num_with_pre_+>/extension/101/call-log"
headers = {"Accept"=>"application/json", "Authorization"=> "#{ token_hash[:access_token] }"}
resp = HTTParty.get(uri, headers: headers)
This sort of request earns me an error:
#<HTTParty::Response:0x7fdf2b0ae800 parsed_response={"errorCode"=>"InvalidParameter", "message"=>"Resource for parameter [accountId] is not found", "errors"=>[{"errorCode"=>"CMN-102", "message"=>"Resource for parameter [accountId] is not found", "parameterName"=>"accountId"}], "parameterName"=>"accountId"}, @response=#<Net::HTTPNotFound 404 Not Found readbody=true>, @headers={"server"=>["nginx/1.10.2"], "date"=>["Wed, 21 Jun 2017 21:39:58 GMT"], "content-type"=>["application/json;charset=UTF-8"], "content-length"=>["290"], "connection"=>["close"], "rcrequestid"=>["2b66c9d6-56ca-11e7-b418-005056bb26b9"], "routingkey"=>["SJC11P01PAS02"], "x-error-id"=>["2b66c9d6-56ca-11e7-b418-005056bb26b9"], "content-language"=>["en-US"], "x-rate-limit-group"=>["heavy"], "x-rate-limit-limit"=>["10"], "x-rate-limit-remaining"=>["9"], "x-rate-limit-window"=>["60"]}>
This error suggests 2 things, 1) My account_id parameter is input wrong -- however, I copied the Sandbox number directly from the RC Developer Portal for my application, both with the prepended +
and without, and the same error occurred, 2) There are no call-log
records, and thus the API server thinks that the Resource doesn't exists and throws the error.
So, I guess my question is? How can I even develop against this Sandbox when there is no data for me to query? Is there an easy way to make data for this account?
Attempting Refresh Token Flow:
NOTE: I just sign-in again every time I need a token at the moment. Clunky but not terrible.
Perform the request:
uri = "https://platform.devtest.ringcentral.com/restapi/oauth/token"
refresh_headers = {"Accept"=>"application/json", "Content-Type"=>"application/x-www-form-urlencoded",
"Authentication"=>"Basic [BASE64_ENCODED_APP_KEY:APP_SECRET_DELIMITED_COMBO]}
refresh_body = {:grant_type=>"refresh_token", :refresh_token=>"#{token_hash[:refresh_token]}"}
resp = HTTParty.post(uri, headers: refresh_headers, body: URI.www_encode_form(refresh_body))
Earn an error:
#<HTTParty::Response:0x7fdf3055d8d8 parsed_response={"error"=>"invalid_client", "error_description"=>"Invalid client: ", "errors"=>[{"errorCode"=>"OAU-153", "message"=>"Invalid client: ", "parameters"=>[{"parameterName"=>"client_id", "parameterValue"=>""}]}]}, @response=#<Net::HTTPBadRequest 400 Bad Request readbody=true>, @headers={"server"=>["nginx/1.10.2"], "date"=>["Wed, 21 Jun 2017 22:16:07 GMT"], "content-type"=>["application/json;charset=utf-8"], "transfer-encoding"=>["chunked"], "connection"=>["close"], "x-application-context"=>["application:8080"], "content-language"=>["en"], "www-authenticate"=>["Bearer realm=\"RingCentral REST API\", error=\"invalid_client\", error_description=\"Invalid client: \""], "rcrequestid"=>["3958edb2-56cf-11e7-8758-005056bb26b9"], "aceroutingkey"=>["sjc11-c01-ace01.c83d65c2-46d3-11e7-ab8e-005056a73f60"], "x-server-name"=>["sjc06-c01-hlb01"], "x-request-time"=>["0.000"], "x-upstream-server"=>["10.24.22.193:8080"], "x-upstream-status"=>["400"], "x-upstream-htime"=>["0.000"], "x-upstream-rtime"=>["1498083367.846"], "x-upstream-ctime"=>["0.000"], "x-tcpinfo"=>["1000, 500, 10, 28960"], "routingkey"=>["SJC11P01"]}>
Weep silently to yourself...
:'-(
Frustratingly the documentation for the RingCentral Ruby SDK have no documented method for refreshing these tokens, and nebulously mentions something about "Manual Refreshes". Any ideas or thoughts on my requests or the SDK would be appreciated. I would prefer to perform refreshes than re-auth over and over again.