Currently, when I set authentication signinflow to redirect, the login process doesn't complete, although it does go to the Google or Facebook login screen. However, when I set the signinflow to popup, the authentication process works as intended. What I'm curious about is how do I go about debugging the authentication in Firebase? The console only shows front-end javascript related code obviously, and the DebugView in the firebase console doesn't show anything when I login or logout whether it's via popup or redirect. What are the steps involved in finding out where the authentication error is in relation to the redirect signinflow mode?
My app.js code is as follows:
firebaseUI.start('#firebaseui-auth-container', {
signInFlow: 'redirect',
signInSuccessUrl: 'http://www.bing.com',
signInOptions: [
firebaseDB.firebase_.auth.EmailAuthProvider.PROVIDER_ID,
firebaseDB.firebase_.auth.GoogleAuthProvider.PROVIDER_ID,
firebaseDB.firebase_.auth.FacebookAuthProvider.PROVIDER_ID
],
});
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log(user.displayName);
} else {
console.log('no user signed in');
}
});
Several tips come to mind:
(1) Look carefully at the JavaScript console logs on your browser. JavaScript is rather notorious for "spit out an error-message (in a place where you'd ordinarily never see it ...) and keep going."
(2) Look carefully at the server-side logs, both for Firebase and for the web server. The sequence of interest probably involves one or more "round trips" between several different pieces of software, and you need to methodically, systematically reconstruct exactly what took place and in what sequence. "The whole time-line."
(3) "Don't assume!" "Trust, but verify," as they say. Don't act on any "assumption" as to what the actual problem is – e.g. here you probably "assume" that it's a problem in "Firebase authentication." Assumptions can send you "chasing after white rabbits" for a distressingly long time, only to come up empty-handed. (Trust me on this one ...)
You probably won't get too much useful information from a "TCP/IP dumper" (such as tcpdump ...) because the communications is probably encrypted.
Related
I am trying to link an anonymous mongodb stitch session to a Google account using the following code snippet from the docs:
function linkWithGoogleAccount() {
user
.linkUserWithRedirect(new GoogleRedirectCredential())
.then(_=> alert("getting here"))
.catch(console.error)
}
Further, I added http://localhost:8080 (where I develop the app) to the list of Allowed Request Origins.
With the above code, I expect to handle the OAuth redirect similar to the regular Google authentication (which works fine).
However, when the alert "getting here" is triggered I see a GET 400 error with URI https://eu-west-1.aws.stitch.mongodb.com/api/client/v2.0/app/myapp-abcde/auth/providers/oauth2-google/login?redirect=http://localhost:8080/&state=M2I...In0%3D&link=true&providerRedirectHeader=true. After accepting the alert I get redirected to localhost:8080/null.
I assume I misunderstand some aspects of the whole procedure (it's hard to debug) and would appreciate any help. Thanks!
So I read through the example which Apple gave us (CloudKit catalog) and I noticed that everytime you want to write or read you need to put your API token into the script.
Now Javascript is clientbased which means every user can read the API token and can read and write into my containers?!
This code would be in one of the Javascript files
CloudKit.configure({
locale: 'en-us',
containers: [{
// Change this to a container identifier you own.
containerIdentifier: 'com.example.apple-samplecode.cloudkit-catalog',
apiTokenAuth: {
// And generate a web token through CloudKit Dashboard.
apiToken: '<insert your token here>',
persist: true, // Sets a cookie.
signInButton: {
id: 'apple-sign-in-button',
theme: 'black' // Other options: 'white', 'white-with-outline'.
},
signOutButton: {
id: 'apple-sign-out-button',
theme: 'black'
}
},
environment: 'development'
}]
});
Now the question is: am I missing something or is the solution to user a server-to-server communication over Node?
Here's my understanding:
API Tokens are not really meant to be private, and can't be since they're designed to be used in client-side JavaScript. Even if you tried to obfuscate the token in your code, it would be easily discovered by examining the URLs that are called during the sign in process.
The important thing to understand is that they can't do much by themselves. They allow a user to sign in to your container, and then the signed in user can read and write their own data—the same things they'd have access to if they signed in to iCloud on their iPhone or Mac and used your app there.
There's not much of a security concern because even if they take your token and write their own JavaScript, they're only messing with their own data. That said, you can use the "Allowed Origins" option to make this harder to do. (I put it that way because they could conceivably use a browser extension or something to alter the JS on your site. In general it seems wise to treat a user's CloudKit data as untrusted, even when it's coming from the API.)
Server to Server Keys are very different, and have a private key that is of course meant to be private. In that scenario anyone with the private key has read and write access to your public database. As the name implies, this is not something you'd use directly from JavaScript—you'd write your own server-side code that contacts the CloudKit API directly.
Unfortunately, while Apple has a nice red warning when you create a private Server to Server key, they don't seem to offer any security guidance on API Tokens. I'm 99% confident that this is because it's not a concern, and working on getting confirmation for that last 1%.
The init.js runs at client side, in the browser, you can easily notice that from the code:
<script>
window.addEventListener('cloudkitloaded',CKCatalog.init);
</script>
This will disclose the API token to the user ...
But you can always mitigate the risk of dangerous usage on the API token by:
Set "Allowed Origins" of the API token to only the domain of your site;
Set "Sign In Callback" to your URL only;
etc
In short, running in client side will disclose your API token, while it is still OK if you take actions to prevent dangerous usage of your token.
What I would like to be able to do is access the gmail/calendar api using javascript from within a gmail contextual gadget (rather than bouncing the requests out to somewhere else).
I also want to do this using a service account for authentication so access is controlled via the gadget setup/domain and the user is never prompted for any details themselves.
The documentation around this area is a mass of out dated and confusing information, so it's hard to even tell if this is meant to be possible or not.
I have run through a number of different approaches with little success.
According to here https://developers.google.com/gadgets/docs/basic?csw=1 writing OAuth gadgets is no longer supported. Spent a lot of time getting nowhere with this approach before noticing that comment, and abandoning it.
Next, tried using gadgets.io.makeRequest from https://developers.google.com/gadgets/docs/reference/#gadgets.io but this didn't seem the right fit for what I wanted, too much of a roll your own solution. I had no success with that, so abandoned it to try something else.
Next tried gapi.auth.authorize https://developers.google.com/api-client-library/javascript/features/authentication This one seemed promising at first, but I cannot find any information on using it with a service account, and I just get errors when trying to use it (but at least errors feel like moving forwards!).
My current code for this is along the lines of
function connectToGmail() {
gapi.client.setApiKey(API_KEY);
window.setTimeout(checkAuth,1);
}
function checkAuth() {
var parameters = {
client_id: OAUTH2_CLIENT_ID,
scope: OAUTH2_SCOPES,
immediate: true,
output: 'embedded'
};
gapi.auth.authorize(parameters, function(result) {
console.log('login complete');
console.log(gapi.auth.getToken());
});
}
but the authorize callback is never called.
Have tried adding hd: to the parameters too, and a few other undocumented values I've seen used in my hunt for details.
In various experiments I generally this error in the browser console
Refused to display 'https://accounts.google.com/o/oauth2/auth?client_id=**********.googleusercontent.com&response_type=token&state=1847286098%7C0.1045275236' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.
Fair enough, I understand the same origin part of that problem, but wish it gave me the errors thru the callback - I have no intention of opening anything here - that's why I want to use a service account!
If I manually navigate to that URL I see the broken robot page,
That's an error
Error: invalid_client
no registered origin
Request Details
immediate=true
response_type=token
scope=https://www.googleapis.com/auth/gmail.readonly
redirect_uri=postmessage
proxy=oauth2relay869439690
state=1847286098|0.1045275236
origin=https://uvh02lohhb14dcmd9rb9buet1dr4b61j-a-gm-opensocial.googleusercontent.com
include_granted_scopes=true
hd=***********.co.uk
client_id=.apps.googleusercontent.com
hl=en
I don't think there's anything useful in there, just including it for completeness in showing what I've tried.
Any ideas on if this - is/should be/will be - possible, and if not any suggestions for other approaches to take?
I wrote an Android app that should 'connect' to a (private) forum using HTTP GET (and sometimes POST) requests. The basic idea is as such:
Login page where users submit their credentials. Login is performed by doing a HTTP POST (tried GET too, same result) to the Login page of the forum, with their username and password as the parameters. The request should return some cookies that I store in a BasicCookieStore.
Every page of the forum they want to visit is retrieved using HTTP GET. I parse the HTML source that I obtain and show them only the relevant info. In order to authenticate the users, the same BasicCookieStore that I used for login (step 1) is set as the cookiestore for the HttpClient.
This method has been working all the time during my testing, and has worked for my beta testers too. Now that I released the app, it became apparent that many users were having issues, especially on mobile connections (Wifi seems to be no problem).
By logging the HTML source that was returned in all the HTTP GET requests, I have a strong suspicion that the actual login works fine, but somehow the cookies don't get returned or stored or something in that direction. The problem is that the HTML source of the first page they will receive should be the list of forums. In the case of users with problems however, they get served a page that basically reads "You must enable Javascript to view this page".
The strange thing is, I don't receive that page when testing, nor do many of my users. Even worse: some users are now reporting it worked fine for them for days or weeks, and has now stopped working. Others have the exact opposite: not working for days, suddenly working now. One user has reported he was in Greece for 2 weeks, where it worked flawlessly, then he got back to Germany, and it stopped working again.
There seems to be a random component at play here.
I have tried various things, mostly with the way I do the HTTP GET requests. I started out using the normal DefaultHttpClient, with various settings, such as this:
HttpClient httpClient = new DefaultHttpClient();
// Define parameters
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT);
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
// Set cookiestore (getCookieStore returns the same cookiestore)
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, getCookieStore());
HttpGet http = new HttpGet(url);
http.addHeader("Accept", ACCEPT_STRING);
http.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
// Execute
HttpResponse response = httpClient.execute(http, localContext);
//... Process result (omitted)
Now I have switched to using AndroidHttpClient instead, with the rest of the code basically unchanged, and seem to get the same result.
I have also tried using the AsyncHttpClient library, which works quite differently, but once again the same result. I tried using its PersistentCookieStore as well, and you guessed it - same result.
I am clueless at this point. Am I looking in the wrong direction? The fact that a website would respond with "you need to enable Javascript" for some users but not for all seems to indicate an issue with cookies. I don't know how a website determines if javascript is enabled, but surely with a HTTP GET request there is no javascript at play. So why do I (and many other users) get to the page without any problems, while others get the 'no javascript' message? The only reason I can think of is cookies, but I have no clue what the problem exactly is.
Any help would be much appreciated!
I doubt the problem is cookies. More likely is a network configuration problem.
For example, your user might have connected to a wifi hotspot with a captive portal page (which uses javascript to make you sign in before you can use the hotspot). In this case they should first open the browser, try to browse to (e.g.) http://google.com, get redirected, sign in, and then launch your app.
Or, your user might be connecting through a proxy. Many mobile carriers around the world will proxy their users' HTTP connections, sometimes doing horrible things to the content. Switching to HTTPS might help with that.
I am starting to build/design a new single page web application and really wanted to primarily use client-side technology (HTML, CSS, JavaScript/CoffeScript) for the front-end while having a thin REST API back-end to serve data to the front-end. An issue that has come up is about the security of JavaScript. For example, there are going to be certain links and UI elements that will only be displayed depending on the roles and resources the user has attached to them. When the user logs in, it will make a REST call that will validate the credentials and then return back a json object that has all the permissions for that user which will be stored in a JavaScript object.
Lets take this piece of javascript:
// Generated by CoffeeScript 1.3.3
(function() {
var acl, permissions, root;
root = typeof exports !== "undefined" && exports !== null ? exports : this;
permissions = {
//data…
};
acl = {
hasPermission: function(resource, permission, instanceId) {
//code….
}
};
root.acl = acl;
}).call(this);
Now this code setup make sure even through the console, no one can modify the variable permissions. The issue here is that since this is a single page application, I might want to update the permissions without having to refresh the page (maybe they add a record that then needs to be added to thier permissions). The only way I can think of doing this is by adding something like
setPermission: function(resource, permission, instanceId){
//code…
}
to the acl object however if I do that, that mean someone in the browser console could also use that to add permissions to themself that they should not have. Is there any way to add code that can not be accessed from the browser console however can be accessed from code in the JavaScript files?
Now even if I could prevent the issue described above, I still have a bigger one. No matter what I am going to need to have the hasPermission functionality however when it is declared this way, I can in the browser console overwrite that method by just doing:
acl.hasPermission(resource, permission, instanceId){return true;}
and now I would be able to see everything. Is there anyway to define this method is such a way that a user can not override it (like marking it as final or something)?
Something to note is that every REST API call is also going to check the permissions too so even if they were to see something they should not, they would still not be able to do anything and the REST API would regret the request because of permissions issue. One suggestion has been made to generate the template on the server side however I really don't like that idea as it is creating a very strong coupling between the front-end and back-end technology stacks. If for example for whatever reason we need to move form PHP to Python or Ruby, if the templates are built on the client-side in JavaScript, I only have to re-build the REST API and all the front-end code can stay the same but that is not the case if I am generating templates on the server side.
Whatever you do: you have to check all the permissions on the server-side as well (in your REST backend, as you noted). No matter what hoops you jump through, someone will be able to make a REST call that they are not supposed to make.
This effectively makes your client-side security system an optimization: you try to display only allowed operations to the user and you try to avoid round-trips to the server to fetch what is allowed.
As such you don't really need to care if a user can "hack" it: if they break your application, they can keep both parts. Nothing wrong can happen, because the server won't let them execute an action that they are not authorized to.
However, I'd still write the client-side code in a way that it expect an "access denied" as a valid answer (and not necessary an exception). There are many reasons why that response might come: If the permissions of the logged-in user are changed while he has a browser open, then the security descriptions of the client no longer match the server and that situation should be handled gracefully (display "Sorry, this operation is not permitted" and reload the security descriptions, for example).
Don't ever trust Javascript code or the front-end in general. People can even modify the code before it reaches your browser (sniffers etc) and most variables are accessible and modifiable anyways... Trust me: you are never going to be safe on the front-end :)
Always check credentials on the server-side, never only on the front-end!
In modern browsers, you can use Object.freeze or Object.defineProperty to make sure the hasPermission method cannot be redefined.
I don't know yet how to overcome the problem with setPermission. Maybe it's best to just rely on the server-side security there, which as you said you have anyway.