Stan
Stan

Reputation: 156

Getting error using bearer authorization to sync PouchDB to Cloudant

I am an experienced JavaScript programmer, but new to PouchDB, Cloudant, and oAuth. When I try to sync PouchDB and Cloudant using Bearer Authorization, I get the error:

"_reader access is required for this request", status: 401

I am trying to write a demo web app, using PouchDB and Cloudant. I set up an IBM account. I created one service called Cloudant-xx. I chose Authentication Method: IBM Cloud IAM (I did not choose "and legacy..."). I went to the dashboard for this service and 1) created a database (myDatabase), and 2) selected the head-profile on the left of the screen, then [CORS], and disabled CORS (for development).

I went back to the service, selected [Service Credentials], and created a new service credential. This gave me an object with {"apikey": "ELB4lEVA...IO", ..., "username": "451c...74-bluemix"}.

I used curl to get an access 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=ELB4lEVA...IO" \
  "https://iam.cloud.ibm.com/identity/token"

which returned {"access_token":"eyJra...6fJns","token_type":"Bearer","expires_in":3600,"expiration":1558965151,"scope":"ibm openid"}

Within the hour, I copied and pasted the access token into my javascript code, ran the code, and got the error "_reader access is required for this request" (see above). I cannot find where to set _reader access. Can anybody help me? Thanks!

    let localDb;
    const localName = "myDatabase";
    const remoteUrl = "https://451...d974-bluemix.cloudantnosqldb.appdomain.cloud/myDatabase";
    const token = "eyJra...6fJns";
    const syncOptions = {
        live: true,
        retry: true,
        continuous: true,
        ajax: {
            headers: {
                Authorization: "Bearer " + token,
            },
        },
    };
    localDb = new PouchDB(localName);
    localDb.sync(remoteUrl, syncOptions).on('change', function (info) {
        console.log(8888, "change", info);
    }).on('paused', function (info) {
        console.log(8888, "paused", info);
    }).on('active', function (info) {
        console.log(8888, "active", info);
    }).on('denied', function (err) {
        console.log(8888, "denied", err);
    }).on('complete', function (info) {
        console.log(8888, "denied", info);
    }).on('error', function (err) {
        console.log(8888, "error", err);
    });
    console.log(4444, localDb);

In the console I see:

4444 PouchDB {__opts: {…}, auto_compaction: undefined, prefix: "_pouch_", name: "myDatabase", _adapter: "idb", …}
index.js:194 

451...d974-bluemix.cloudantnosqldb.appdomain.cloud/:1 GET https://451...d974-bluemix.cloudantnosqldb.appdomain.cloud/myDatabase/ 401
index.js:192

8888 "error" CustomPouchError {error: "unauthorized", reason: "_reader access is required for this request", status: 401, name: "unauthorized", message: "_reader access is required for this request", …}

Upvotes: 2

Views: 577

Answers (1)

Stan
Stan

Reputation: 156

I answered my own question. Turns out I was following out-of-date examples. In PouchDB 7.0, {ajax: {headers: {}} has been replaced with fetch: function (url, opts) {}. Plus I had to use a PouchDB instance instead of a string for the remote database.

https://pouchdb.com/api.html#create_database

Options for remote databases:

fetch(url, opts): Intercept or override the HTTP request, you can add or modify any headers or options relating to the http request then return a new fetch Promise.

This now works.

    let localDb;
    const localName = "myDatabase";
    const remoteUrl = "https://451...74-bluemix.cloudantnosqldb.appdomain.cloud/myDatabase";
    const token = "eyJ...448";
    const remoteOptions = {
        fetch: function (url, opts) {
            opts.headers.set("Authorization", "Bearer " + token);
            return PouchDB.fetch(url, opts);
        }
    };
    const syncOptions = {
        live: true,
        retry: true,
        continuous: true,
    };
    localDb = new PouchDB(localName);
    const remoteDb = new PouchDB(remoteUrl, remoteOptions);
    localDb.sync(remoteDb, syncOptions).on("change", function (info) {
        console.log(8888, "change", info);
    }).on("paused", function (info) {...})...

Upvotes: 3

Related Questions