Open snej opened 8 years ago
I have been using cbl on android successfully without coax using a typescript wrapper over the REST API. https://github.com/happieio/cordova-plugin-couchbase-lite/blob/master/APIs/typescript/cbl.ts
Here is a reference cordova app using the typescript wrapper. https://github.com/happieio/typeapp
Well, that's weird. Your wrapper doesn't do anything with Authorization headers; that would imply that the XHR implementation generates one from the credentials in the URL. Then why isn't that happening to the people running into this problem? Maybe it has something to do with the version of Cordova?
It looks like the native side of the Cordova API is handling the generation of the user name and password. https://github.com/couchbaselabs/Couchbase-Lite-PhoneGap-Plugin/blob/master/src/android/CBLite.java#L77-L92
perhaps people that are running into the issue haven't updated this file? I do remember this being an issue a while ago.
No, the people having this problem definitely have the username and password in the URL. The problem is that those aren't being sent back in the XHR — they don't get translated into an Authorization header.
See also this forum thread, where a document attachment can't be used as the src
in an HTML IMG
tag because of 401 errors.
My app uses Sencha Touch, the Ext.Ajax.request can access cbl without additional authorization handling.
Ext.Ajax.request({ url: url, method: 'GET', useDefaultXhrHeader: false, success: function (response) { }, failure: function (response) { } });
However, when I use Ext.Ajax.request to request replication with sync gateway, the web authorization popup window will show.
Note:
TJWS might not accept credential with url (http://<username>:<password>@hostname/path
).
https://github.com/couchbase/couchbase-lite-java-listener/blob/master/src/main/java/com/couchbase/lite/listener/LiteServer.java http://tjws.sourceforge.net/
A web server never sees the credentials in this form -- it only receives the path and the host name. It's up to the client to detect the credentials and convert them into an Authorization header that the server will receive.
Implementation of couchbase-lite-java-listener only accepts credential from Authorization
HTTP request header. The client needs to set Authorization
header.
Credentials requestCredentials = credentialsWithBasicAuthentication(request);
if (allowedCredentials != null && !allowedCredentials.empty()) {
if (requestCredentials == null || !requestCredentials.equals(allowedCredentials)) {
Log.d(Log.TAG_LISTENER, "Unauthorized -- requestCredentials not given or do not match allowed credentials");
response.setHeader("WWW-Authenticate", "Basic realm=\"Couchbase Lite\"");
response.setStatus(401);
return;
}
Log.v(Log.TAG_LISTENER, "Authorized via basic auth");
} else {
Log.v(Log.TAG_LISTENER, "Not enforcing basic auth -- allowedCredentials null or empty");
}
public Credentials credentialsWithBasicAuthentication(HttpServletRequest req) {
try {
String authHeader = req.getHeader("Authorization");
if (authHeader != null) {
StringTokenizer st = new StringTokenizer(authHeader);
if (st.hasMoreTokens()) {
String basic = st.nextToken();
if (basic.equalsIgnoreCase("Basic")) {
try {
String credentials = new String(Base64.decode(st.nextToken()), "UTF-8");
Log.v(Log.TAG_LISTENER, "Credentials: ", credentials);
int p = credentials.indexOf(":");
if (p != -1) {
String login = credentials.substring(0, p).trim();
String password = credentials.substring(p + 1).trim();
return new Credentials(login, password);
} else {
Log.e(Log.TAG_LISTENER, "Invalid authentication token");
}
} catch (Exception e) {
Log.w(Log.TAG_LISTENER, "Couldn't retrieve authentication", e);
}
}
}
} else {
Log.d(Log.TAG_LISTENER, "authHeader is null");
}
} catch (Exception e) {
Log.e(Log.TAG_LISTENER, "Exception getting basic auth credentials", e);
}
return null;
}
I know; that's what I said yesterday. The problem here isn't in the CBL listener. The problem is that we're expecting something to convert the credentials in the URL into an Authorization header, and for some reason it isn't happening. That "something" seems like part of Cordova itself, or part of the web view the app is running in.
Developers who don't use the coax library to send their REST requests won't be able to connect to CBL at all on Android — every request will fail with a 401 Unauthorized status. See this forum thread, and this one too.
The reason is that Android's CBL listener is configured with a random username/password (for security reasons), which is encoded in the URL given to the app; but those credentials have to be extracted from the URL and turned into an
Authorization:
header in the actual XHR. Coax knows how to do this, but other libraries apparently don't — in the thread above, the developer was using the fetch library.This problem doesn't occur on iOS (or Mac) because on those platforms we don't need to set a password — we're able to use NSURLProtocol instead of opening a real TCP socket, so there's no possibility of untrusted apps being able to connect. But this can blindside developers because if they develop on those platforms first, their app will work great on iOS but mysteriously fail completely on Android.
We should probably bundle an API for REST requests, like coax, with the plugin itself, and encourage developers to use it. But we also need to document that, if they don't use it, they need to set the Authorization header themselves. (Ideally we can provide a JS function that takes a URL with credentials and returns the username and password.)