I am trying to implement SafetyNet in my app. I also, don't have a server, and I am using Firebase Firestore and Firebase Functions.
My knowledge about Firebase Functions is very limited. And I was wondering if I could somehow use the functions to help me with the SafetyNet attestation. As I see, I should be producing a nonce on the cloud, send this nonce to the app, use it to attest, and send it back to the cloud to verify the integrity correct?
But I can't seem to find anywhere on how to do this. Can anyone point me in the right direction?
YES
Sorry for the excitement there, but this is possible since a few weeks ago through a new feature called Firebase App Check.
With App Check, you always end up with a two-step process:
Use an attestation provider (such as SafetyNet) in your application, so that information about the app is attached to each request it makes to Firebase.
Then at some point in time, when enough of your app requests have this information attached, check for the app information in Cloud Functions, or enable the check in one of the other supported services.
If you check the documentation on enabling App Check enforcement for Cloud Functions, you'll see that it mostly boils down to this check in the code:
exports.yourCallableFunction = functions.https.onCall((data, context) => {
// context.app will be undefined if the request doesn't include a valid
// App Check token.
if (context.app == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called from an App Check verified app.')
}
// Your function logic follows.
});
Related
I have a Spring Boot back end API using Auth0 JWT authentication and currently have two clients for it, a Vue SPA and an Android app. The Vue SPA works fine. It uses an SPA Application type in Auth0, and the authentication mechanism uses an Audience, like so:
{
"domain": "mycompany.auth0.com",
"clientId": "mySPAclientID",
"audience": "https://myaudience.mycompany.com"
}
I figured I'd be able to do something similar in Android, so I created a Native Application type in my Auth0 Dashboard, downloaded the corresponding quickstart, and attempted to authenticate. I am, of course, able to authenticate against Auth0 and get a JWT back, but the JWT does not work against my Spring Boot API, which is designated by my https://myaudience.mycompany.com audience and which my Spring Boot security config expects to be present in the JWT. My first thought was that I could simply add the audience to the login action in the Android app:
WebAuthProvider.login(auth0)
.withScheme("demo")
.withAudience(String.format("https://%s/userinfo", getString(R.string.com_auth0_domain)))
.withAudience("https://myaudience.mycompany.com") // added this
.start(this, new AuthCallback() { ... }
But this doesn't work. When I parse the returned JWT, the audience portion does not contain the expected block. It should look like this:
"aud": [
"https://myaudience.mycompany.com",
"https://mycompany.auth0.com/userinfo"
]
But instead it looks like this:
"aud": "myNativeClientID"
It's also missing the scope element entirely. What's the correct way to make this work? Do I need a new Auth0 API, which would require a new Spring Boot security mechanism? Or is there something simple I'm missing in the login request? Or some config I'm missing in my Auth0 Native Application? I've been through all the relevant documentation and nothing works. I'm not sure how to proceed, any help would be hugely appreciated.
With a helpful nudge by an Auth0 employee, I realized that I'd confused the Auth0 ID token with the access token. In Auth0, ID tokens are always issued as JWTs, but they can't be used to access Auth0 APIs, only access tokens can do that. The trouble is that unless you specify an "audience" in your login request, the access token is of a simple token format like this:
1v-QyDrPaJ5rOUBOk3g_0HtEwtN4C-4U
But when you add an audience (ie an Auth0 API, which is in turn referenced in your back end, whatever it may be, in my case Spring Boot), the access token becomes a JWT with all the requisite audiences and scopes (by default the scope is limited to openapi, I believe).
So because the access token was not a JWT (since I hadn't yet added the audience when I first examined the login response), I dismissed it, and was using the ID token, which as I mentioned can't be used to gain access to an Auth0 API. Once I reverted back to using the access token JWT, everything worked as expected.
Accepting this as an answer in case it helps anyone in the future.
I am trying to access users YouTube private data like subscriptions which require OAuth authentication. I have tried 2 approaches so far.
1.Using GoogleAuthUtil.getToken() method. But it's deprecated and using this function only access token can be acquired which expires after 1 hour. I have no idea how to retrieve refresh token using this method.
2.According to this article and this answer we should avoid use of getToken method because of security and UX problems. So I tried using Auth.GOOGLE_SIGN_IN_API. According to the documentation, we need to use GoogleSignInOptions. So I used it and using requestScopes() i added the Youtube scope. Also I used requestserverauthcode() to get the exchange code, using which access and refresh token can be acquired. Now here's my problem, we need to use GoogleApiClient with addApi(). The available set of API's doesn't have a Youtube API listed to put it in addApi(). So there is no way I can get access to access tokens / refresh token.
Can anyone help me out?
EDIT:
I solved it. I used getToken approach to get tokens. Whenever a 401 authorization error occurs, we need to call getToken method returns a new token.
Using the Mobile Backend Starter (MBS) Android classes (those distributed as a sample project when creating a new project in Google Dev Console and demoed at Google I/O 2013) I'm able to insert new entities to the cloud datastore via calls to CloudBackendMessaging.insertAll or .updateAll. The latter will create entities if none exist so seems functionally identical to insert for new records.
The insertion/creation works fine. However when I attempt to update existing entries in the datastore, I received permissions errors e.g. (from the backend log)
Method: mobilebackend.endpointV1.updateAll
Error Code: 401
Reason: required
Message: Insuffient permission for updating a CloudEntity: XXXXXX by: USER: YYYYYYY
which results in a matching access error in the logcat client side.
In all cases I am using Secured access authenticating with a valid Google account (my own).
The entities being inserted are thus showing as "owned" by my user ID with "updated by" and "created by" showing my Google account's email address.
However when the update of the existing record is made, using exactly the same CloudBackendMessenger object and thus same credentials etc. the backend is telling me I can't update due to permissions issues. But surely if I just made the entity with the same credentials this can't be correct? Looking at the documentation it appears that I should be able to edit entities owned by the same user ID in all cases (regardless of the KindName and whether it is prepended [public], [private] or nothing).
Can anyone who has received permissions errors on UPDATES via Mobile Backend Starter for Datascore please shed any light? I have been banging my head over this for most of today.
I've faced the similar error "Insuffient permission for updating a CloudEntity" when using cloudBackendAsync.update(cloudEntity). I resolved it by making sure the cloudEntity has it's createdAt field set. createdAt is autogenerated and I think I am not supposed to touch it. But it worked for me. In my case I am first obtaining list of cloud entities. This is when I get createdAt field of cloud entities. Then when I am updating I setting the createdAt field from previously obtained entities.
Edit: Had to do similar thing for owner field also.
Similar to one of the comments above, I successfully got around this by getting the original CloudEntity before doing the insert/update/delete function.
CloudQuery cq = new CloudQuery("datastoretype");
cq.setLimit(1);
cq.setFilter(Filter.eq("_id",id));
cloudEntity.setId(id);
mProcessingFragment.getCloudBackend().get(cloudEntity, handler);
Thereafter it was trivial to do the following:
mProcessingFragment.getCloudBackend().update(cloudEntity, handler);
The docs definitely ought to be more clear on this, whether it is a strict requirement or bug.
The answers posted so far work around the problem if you don't mind all users being able to access the entity you are trying to update. However, a better solution that retains the access permissions is detailed by google here - https://cloud.google.com/cloud/samples/mbs/authentication
If you want to pass the user’s Google Account info to the backend on
each call, use the CloudBackend#setCredential() method (also available
on the subclasses, CloudBackendAsync and CloudBackendMessaging) to set
a GoogleAccountCredential object before calling any Mobile Backend
methods.
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(this, "<Web Client ID>");
credential.setSelectedAccountName("<Google Account Name>");
cloudBackend.setCredential(credential);
Setting credientials enables the client to operate when the backend is
in “Secured by Client ID” mode and also sets createdBy/updatedBy/owner
properties of CloudEntity automatically.
I have been doing a lot of research recently on securing my app engine. Currently, I've been reading through the question below and the links in that question:
How do I restrict Google App Engine Endpoints API access to only my Android applications?
However, it doesn't answer my problem. My question is similar to the question above, restricting access to my endpoint API to only my app. The guy seemed to have got it working when he inputs a correct email into the credentials.
My question is if I can achieve the same results without having to input any credentials. I want it so that only my app can use my endpoint API so to prevent other apps from abusing it and using up my quota. I already got a client id for my android application, and have placed it within my #API annotation. To test if it worked, I made a random value for the client id in the #API notation of another api class. However, my app was still able to use methods from both class. Any help?
-Edit-
From reading from the docs and researching further, the endpoint way of authorizing apps is by authenticating the user and for my API to check if user is null. My question is that in the process of authenticating the user, is Google somehow able to read my app's SHA1 fingerprint and authorize it to its list of client ids? If so, how can I replicate this process in my endpoint so that I check the SHA1 fingerprint of the app making the request and compare it to a set value? I don't understand the mechanics behind the endpoints very well, so correct me if I am understanding this wrong.
If the android app has access, then the user has access. A motivated party has many options for inspecting your protocol, including putting the device behind transparent proxy or simply running the app through a debugger. I do suggest running your app through ProGuard before publishing, as this will make the process [a bit] more difficult.
Ultimately, you'll need to make your appengine API robust against untrusted parties. This is simply the state of the web.
How you can protect your endpoint API is described here: http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html
The secret is that you request a token from Google Play using the following scope: audience:server:client_id:9414861317621.apps.googleusercontent.com where 9414861317621.apps.googleusercontent.com is your ClientId.
Google Play will look up the id at your endpoints app and return a Google-signed JSON Web Token if it finds the id. Then you pass that id in with your request. Above article says you should pass it in with the body. I would possibly rather add another parameter for that because otherwise you can't pass your own entities anymore. Anyway, your server backend receives the token, and you ask Google as described if it is authentic, before you process the API request.
If you pass in the token using an extra parameter, you can catch it on the server side by adding HttpServletRequest to your endpoint signature and then using request.getHeader("Yourname") to read it out. Make sure you never add the parameter as a URL parameter as it may be logged somewhere.
public void endpointmethod(
// ... your own parameters here
final HttpServletRequest request
) throws ServiceException, OAuthRequestException {
request.getHeader("YourHeaderName") // read your header here, authenticate it with Google and raise OAuthRequestException if it can't be validated
On the Android side you can pass in your token when you build the endpoint api, like this, so you don't have to do it with each and every request:
Yourapiname.Builder builder = new Yourapiname.Builder(AndroidHttp.newCompatibleTransport(), getJsonFactory(), new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
httpRequest.setHeader(...);
}})
Hope this helps you make your endpoints API secure. It should.
I'm trying to authenticate a mobile application for the Android platform to a custom node.js server api. I would like to use Google OAuth2 tokens for this rather than roll my own authentication, since Android devices with Google Play installed make this available to app developers. I'm using the GoogleAuthUtil.getToken call from the Google Play Services library, documented here. I'm trying to follow the advice outlinedin this android developers blogpost
The getToken method is returning in my case a long 857 byte string. If I try to pass this token to Google's TokenInfo endpoint, it returns:
{'error': 'invalid_token', 'error_description': 'Invalid Value'}
What am I doing wrong here? In the 'scope' of the getToken call, I am sending:
audience:server:client_id:**i_put_my_clientid_here**. I have a clientid generated for "installed applications". Using this client id, the call to getToken doesn't work at all. When I generated a client id for a "service account", the call succeeds, but I get an 857 byte token that fails when passed to the TokenInfo endpoint as described above.
EDIT:
I also created a client id for "web applications", as it appears that is the right client id to use when calling getToken. But the behavior is the same, I get back an 857 byte token that doesn't validate when calling Google's endpoint.
How can I properly get a valid auth token using Google Play services on Android? Once I have the right token, what is the right node.js library to validate it server side? Can I use passport-google-oauth ?
Hm, this is really a comment rather than an answer, but I can’t put newlines in those:
it has to be the web-side Clent ID that goes in the put_my_clientid_here spot
if GoogleAuthUtil.getToken() gives you a String withou throwing an Exception, it really ought to be valid. When you hit tokeninfo, did you use ...tokeninfo?id_token=<857-byte-value-here>
if you’re a rubyist, grab the google-id-token gem and see if it can validate your 857-byte token.
If you just want to read the contents of the data returned by GoogleAuthUtil.getToken then the process is very simple. The returned data is simply a JWT. So all you'd have to do is split the data by the . character, and then base64 (url) decode each piece.
It gets slightly more complicated if you want you want to verify the message's authenticity. Simply use your favorite crypto library to do the verification. The 3rd component of the JWT is the signature of the data and the Google certs are publicly available; that's all you need to verify the message.
For a week I have been looking into how to validate GoogleAuthUtil tokens received in Android Client application at Node.js server using passport.js
Finally I came across passport-google-token passport strategy which perfectly performs the task.
https://www.npmjs.com/package/passport-google-token
More details are present in the above link.
The official node SDK lets you do that now.
Here's the link: https://github.com/google/google-auth-library-nodejs/blob/master/lib/auth/oauth2client.js#L384
I'm not too familiar with the details of how Android works with respect to handing a token from the device to the server. My general impression, however, is that you don't go through the typical web-based OAuth dance. Instead, you directly call the "user info" endpoint, which will return the info corresponding to the user who holds the token, or reject the request if the token is invalid. There's some discussion on this related question:
validating Android's authToken on third party server
In effect, the token becomes a secret that is shared between both the device and your server, so its important to protect it.
There are a couple strategies for Facebook and Twitter that were developed to do similar things using tokens from iOS devices:
https://github.com/drudge/passport-twitter-token
https://github.com/drudge/passport-facebook-token
You can take some inspiration from them and tweak it to talk to Google's endpoints. Let me know how this turns out. I'd love to see a similar "passport-google-token" strategy, so if you implement one, let me know and I'll link to it!