IBM Cloud Speech-to-Text SDK auth failures with bearer token

453 views Asked by At

I'm learning to use the Watson Speech JS SDK. In particular I like Transcribe from Microphone, with Alternatives. I'm generating my token with a Firebase Cloud Function. I'm using AngularJS, not JQuery. The first problem I'm running into is

var stream = WatsonSpeech.SpeechToText.recognizeMicrophone(Object.assign(token, {
          objectMode: true,
          format: false,
          wordConfidence: true
}));

I got this error message:

WatsonSpeechToText: missing required parameter: opts.token

(Using $scope.token or token makes no difference.)

Looking up this error in the documentation:

module.exports = function recognizeMicrophone(options) {
  if (!options || !options.token) {
    throw new Error('WatsonSpeechToText: missing required parameter: opts.token');
  }

OK, it's looking for an options object. I fixed the error with this code:

const options = {
      token: $scope.token,
      objectMode: true,
      format: false,
      wordConfidence: true
};
console.log(options);
var stream = WatsonSpeech.SpeechToText.recognizeMicrophone(options);

Now I get this error:

WebSocket connection to 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?model=en-US_BroadbandModel&watson-token=[object%20Object]' failed: HTTP Authentication failed; no valid credentials available

The options object logs this:

token:
  access_token: "eyJraWQiOiIyMDIwMDIyNTE4MjgiLCJhbGciOiJSUzI1NiJ9.eyJpYW1faWQiOiJp0tU2..."
  expiration: 1585332575
  expires_in: 3600
  refresh_token: "OKC8z8ebLMzZcrAt6YgInnJJn0UIx1P3NTeDvdEC3kJqIQ7Yn9J9iu6-DF..."
  scope: "ibm openid"
  token_type: "Bearer"
objectMode: true
format: false
wordConfidence: true
smart_formatting: false

The token is a JSON object, which includes the access_token. Is this what the SDK wants? The RecognizeStream documentation doesn't say whether it wants the JSON token or just the naked access_token.

Adding 000 to the expiration field shows that I have 53 minutes left on this token.

I'm using the API key that's specific to my Speech-to-Text service.

Any other suggestions?

1

There are 1 answers

0
Thomas David Kehoe On BEST ANSWER

Version 0.37.0 of the SDK introduced breaking changes:

All options parameters for all methods are coverted to be lowerCamelCase
For example: access_token is now accessToken and content-type is now contentType

This is related to IBM's move from Cloud Foundry services with username/password auth to Services with IAM auth and api keys. The SDK documentation says:

NOTE: The token parameter only works for CF instances of services. For RC services using IAM for authentication, the accessToken parameter must be used.

The options object looks like this if you use an api key:

const options = {
      accessToken: $scope.token,
      objectMode: true,
      format: false,
      wordConfidence: true
};

If you leave out the token property you get this error message:

WatsonSpeechToText: missing required parameter: opts.token (CF) or opts.accessToken (RC)

What that means is, if you're getting your token from Cloud Foundry (CF) the property must be opts.token (or options.token); but if you're getting your token from IAM auth and an api-key, which is called RC for no reason I know of, then the property must be opts.accessToken (or options.accessToken).

Confusing the matter, the demo source code implies that access_token is the property name:

var stream = WatsonSpeech.SpeechToText.recognizeMicrophone(Object.assign(token, {
          objectMode: true,
          format: false,
          wordConfidence: true
}));

Object.assign is taking the target token object as it comes from IBM and then adding or replacing the properties and values in the source object. Because the property is access_token in the IAM token you'd think that, because the demo runs, access_token is the property. But apparently the demo is running on a Cloud Foundry token, which can use either token or access_token as the property name.

If you get this error message:

WatsonSpeechToText: missing required parameter: opts.token (CF) or opts.accessToken (RC)

then you're using neither token nor accessToken as the property name.

If you get this error message:

WebSocket connection to 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?model=en-US_BroadbandModel&watson-token=[object%20Object]' failed: HTTP Authentication failed; no valid credentials available

then you're using token with a token generated with an IAM api-key. Or your token is expired. You can check that easily by putting this code in your app to tell you how many minutes are left on your token:

// shows time to token expiration
    var expiry = new Date($scope.token.expiration * 1000);
    var now = Date.now();
    var duration = -(now - expiry);

    function msToTime(duration) {
      var milliseconds = parseInt((duration % 1000) / 100),
      seconds = Math.floor((duration / 1000) % 60),
      minutes = Math.floor((duration / (1000 * 60)) % 60),
      hours = Math.floor((duration / (1000 * 60 * 60)) % 24);

      hours = (hours < 10) ? "0" + hours : hours;
      minutes = (minutes < 10) ? "0" + minutes : minutes;
      seconds = (seconds < 10) ? "0" + seconds : seconds;

      return "Token expires in " + minutes + ":" + seconds + " minutes:seconds";
    }
    console.log(msToTime(duration))

You can test if your token is valid from the CLI. First get a new token:

curl -k -X POST \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --header "Accept: application/json" \
  --data-urlencode "grant_type=urn:ibm:params:oauth:grant-type:apikey" \
  --data-urlencode "apikey=s00pers3cret" \
  "https://iam.cloud.ibm.com/identity/token"

then request the language models:

curl -X GET "https://stream.watsonplatform.net/speech-to-text/api/v1/models?access_token=eyJraWQiO...."

I had another problem in my code. I was updating the IBM Speech-to-Text SDK but my code was linking to an old SDK I'd installed with bower three years ago. I didn't realize that I was using 0.33.1 when 0.38.0 is the latest version. Add this to your code to catch this problem:

console.log (WatsonSpeech.version);

With the old SDK I was getting an old error message that didn't even mention the new property name:

WatsonSpeechToText: missing required parameter: opts.token

My blog post has more about IBM Cloud Speech-to-Text.