Is authorization always needed for sending SMS messages?

  • 1
  • Question
  • Updated 2 years ago

I am trying to better understand how the SMS API works. Newbie so please bear with me. Maybe I am making this too complicated and there is an easier way.

Using ManageEngine's ADSelfService to allow remote users to change their expired and forgotten passwords. It has the ability of sending verification codes via SMS using HTTP GET/POST through a web API. I can define HTTP Parameters and HTTP Request Headers with my POST request. It allows for two variables %mobNo% for the "to" phone number and %message% for the message "text".

My problem is Authorization.

Looking at the example here: https://developer.ringcentral.com/api-docs/latest/index.html#!#TextMessages.html it appears that authorization is not required. But it doesn't work without giving a Bearer token in there. As this example shows: https://developer.ringcentral.com/library/tutorials/message.html. Perhaps in the first example it is assumed that the session is already authorized??

And I am assuming that the token expires and needs to be refreshed. So if I enter a token in the config of ManageEngine, it may work for a short period of time after which it starts getting 401 TokenInvalid and Access Token Corrupted.

All in all using the SMS API seems to be an overly complicated way of sending a simple SMS message. Or maybe I am complicating it too much. But other than the two variable I have mentioned above, my HTTP POST would have to be static every time.

The goal is to be able to send that verification code from the main phone number of the company. So I am looking for sending these SMS messages through RingCentral.

Do I have to write a wrapper app that accepts the HTTP request from ManageEngine and then relays that to the SMS API after authorizing/authenticating? Or is there a way to use static passwords or other forms of authentication and have ManageEngine talk to the SMS API directly?

Thanks,

Shahid


Photo of Shahid Sheikh

Shahid Sheikh

  • 184 Points 100 badge 2x thumb

Posted 2 years ago

  • 1
Photo of John Wang

John Wang, Official Rep

  • 5,496 Points 5k badge 2x thumb
I'll answer the ManageEngine ADSelfService integration first. From looking online, I found the following which indicates a specification that does not match our API.

https://www.manageengine.com/products/self-service-password/help/admin-guide/Admin/server-settings/s...

To connect the ManageEngine interface below to the RingCentral API, there are a few approaches that can work:

(1) self-service: one way would be to set up a middleware service that would listen for API calls from ManageEngine using their format and then reformat into RingCentral's API format.
(2) assisted self-service: another solution would be for ManageEngine to publish a Zapier app (https://zapier.com/) to make the connection easier. RingCentral already has a Zapier integration with a SMS action that can fulfill sending the SMS text. Here ManageEngine would need to integrate with Zapier, a popular API integration platform, which they may be considering already.
(3) direct integration: ManageEngine or RingCentral can perform direct integration but this may take longer to prioritize.

If you have a way to stand up a simple service, say on AWS or Heroku or your own servers, option 1 may get you to success the fastest. Our SDKs will automatically handle token refresh for you so if you can use one of the SDKs, that will be easier for you to handle:
 
https://developer.ringcentral.com/library/sdks.html

Regarding the other questions:

Sending SMS via the API requires authorization to prevent abuse. Otherwise, anyone could send SMS over HTTP. This is specified in the access token part of our documentation but the SMS section should be updated:

https://developer.ringcentral.com/api-docs/latest/index.html#!#UsingAccessToken.html

Finally, the use of access and refresh tokens are good security practices and part of the OAuth 2.0 specification:

https://tools.ietf.org/html/rfc6749

As mentioned above, it seems like the easiest way to connect the 2 services today would be to set up a middleware server app that listens for ManageEngine calls and translates them to RingCentral API calls using a RingCentral SDK that automates token refresh. Is this something you can do?
Photo of Shahid Sheikh

Shahid Sheikh

  • 184 Points 100 badge 2x thumb

John,

Thank you for the detailed reply and for looking into ManageEngine documentation.

I have looked at the API. While very intriguing, I am in an environment without any development tools except for basic notepad, ASP.Net, and IIS. So I could possibly create a middleware page in ASP.Net that would relay the SMS message from ManageEngine to the API, handling Authorization and JSON without the ability of installing development tools (NuGet, Visual Studio, DotNet assemblies, etc) on the customer's machine where ManageEngine is setup is just a royal pain.

I agree that access and refresh tokens are good practice, but for a static integration requirement like this where the identity of the server/service that is wanting to send an SMS never changes, its probably overkill and a hindrance that has me looking for other simpler solutions than RingCentral SMS.

Are there any Windows PowerShell cmdlets available for RingCentral?

Thanks,

Shahid

Photo of John Wang

John Wang, Official Rep

  • 4,798 Points 4k badge 2x thumb
What version of ASP.Net / .NET are you running and what language are you using? That's useful to know to understand what's built into the core library.

To make authorization easier for for a backend service private app, you can use the password grant without refresh token in which case you can use the extension password and get a new access token each time. To do this, you should set the refresh_token_ttl to `-1` so that a refresh token is not generated for you.

Also, can you let me know what format ManageEngine uses to populate the %mobNo% user mobile number macro? For example, would a number like "+1 (650) 111-2222" be populated as "16501112222" in the query string?

If ManageEngine's %mobNo% macro returns a international phone number with only digits and an optional leading `+`, that phone number will be accepted by RingCentral's API directly and Zapier can be used. To use Zapier, set up an inbound webhook using "Webhooks by Zapier" for the action and RingCentral as the action. If you can confirm the %mobNo% format, I'll look into writing up a quick tutorial to help you set this up. It's worth setting this up to test as it can work seamlessly.
Photo of Shahid Sheikh

Shahid Sheikh

  • 184 Points 100 badge 2x thumb

ASP.NET is 4.5 installed part of .Net framework as a feature on Windows 2012 R2 Server.

ManageEngine passes on the mobile number exactly as it is specified in Active Directory.

I have it currently working with this very (very) crudely and poorly written asp page. ManageEngine just does an HTTP GET to http://localhost/rcsendsms?MobileNumber=%mobNo%&Message=%message%. Basically put it together by copy pasting different snippets of code I found from the Internet.

using System;
using System.Data;
using System.Collections.Generic;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
  var AppKey = "<Insert Application Key here>";
  var AppSecret = "<Insert Application Secret here>";
  var UserName = "<Username/phone number>";
  var Extension = "<extension>";
  var Password = "<password>";
  var URI = "https://platform.devtest.ringcentral.com/restapi/oauth/token";
  Result.Text = "OK";
  var PhoneNumber = Regex.Replace(Request.QueryString["MobileNumber"], "[^0-9]", "");
  if (PhoneNumber.Length > 11 ||  PhoneNumber.Length < 10)
  {
   Result.Text = "FAILED: " + Request.QueryString["MobileNumber"];
  }
  if (PhoneNumber.Length == 11 && PhoneNumber[0] != '1')
  {
   Result.Text = "FAILED: " + Request.QueryString["MobileNumber"];
  }
  PhoneNumber = "+" + PhoneNumber;
  if (Result.Text == "OK")
  {
   
   using (var client = new HttpClient())
   {
    var values = new Dictionary<string, string>
    {
       { "grant_type", "password" },
       { "username", UserName },
       { "extension", Extension },
       { "password", Password },
    };
    HttpContent content = new FormUrlEncodedContent(values);
    var byteArray = Encoding.ASCII.GetBytes(AppKey + ":" + AppSecret);
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
    var response = client.PostAsync(URI, content).Result;
    response.EnsureSuccessStatusCode();
    string responsestring = response.Content.ReadAsStringAsync().Result;
    responsestring = Regex.Replace(responsestring, "[{}]", "");
    string[] array = responsestring.Split(',');
    string token = "";
    foreach (string s in array)
    {
     string[] tuple = s.Split(':');
     if (tuple[0].Contains("access_token"))
     {
      token = Regex.Replace(tuple[1], "[\"\\s]", "");
     }
    }
    Result.Text = token;
   }
   URI = "https://platform.devtest.ringcentral.com/restapi/v1.0/account/~/extension/~/sms";
   using (var client2 = new HttpClient())
   {
    client2.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Result.Text);
    string SMSstring = "{ \"to\": [{\"phoneNumber\": \"" + PhoneNumber + "\"}], \"from\": {\"phoneNumber\": \"" + UserName + "\"}, \"text\": \"" + Request.QueryString["Message"] + "\" }";
    StringContent SMSContent = new StringContent(SMSstring, Encoding.UTF8, "application/json");
    HttpClient client = new HttpClient();
    var response = client2.PostAsync(URI, SMSContent).Result;
    response.EnsureSuccessStatusCode();
    Result.Text = response.Content.ReadAsStringAsync().Result;
   }
  }
 }
}

Photo of John Wang

John Wang, Official Rep

  • 4,798 Points 4k badge 2x thumb
Glad it's working and thanks for posting the code. It has been useful for me to see it and I'm sure it will be useful for others as well.

As mentioned above, you can remove the need to handle the refresh token if you never request it, by setting refresh_token_ttl to -1. Otherwise, your app could end up generating too many refresh tokens and be flagged. To do this, you can do something like the following and request a new access token each time.

    var values = new Dictionary<string, string>
    {
       { "grant_type", "password" },
       { "username", UserName },
       { "extension", Extension },
       { "password", Password }, { "refresh_token_ttl", "-1" }
    };
Photo of Shahid Sheikh

Shahid Sheikh

  • 184 Points 100 badge 2x thumb

Thanks.

Is access_token_ttl also supported? Since I get an access token every time and use it immediately after getting it, are there any downsides to setting the Access Token TTL to lets say 5 or 10 seconds?

Photo of John Wang

John Wang, Official Rep

  • 4,798 Points 4k badge 2x thumb
access_token_ttl is supported. The shorted time available is 10 minutes as specified in the Developer Guide and API Explorer.

If you set the access_token_ttl to 10 minutes and refresh_token_ttl to -1, your app should be behave okay if there is a reasonable number of password resets. If you make very large volume of API calls this may still be flagged.

One thing you can do is store the access_token and convert expires_in (seconds) into a time. Then for each API call, you can check if your access token is expired or not to make a new auth call. If it's expired, request a new one. You can set your time to 9 minutes so you always have an active access_token.
(Edited)
Photo of John Wang

John Wang, Official Rep

  • 5,496 Points 5k badge 2x thumb
I've just posted some information on integrating ManageEngine ADSelfService here:

http://ringcentral-integrations-cookbook.readthedocs.io/en/latest/integrations/manageengine/self_ser...

It doesn't have any code but provides basic integration information for both custom middleware and Zapier.