Firebase Appcheck and RecaptchaV3 Provider - firebase

I'm wondering if someone can explain the overlap between Firebase AppCheck and Recaptchav3. I have a public web application with AppCheck enabled and configured to use the Recaptchav3 provider. An integral part of using Recaptcha however is verifying the client's score to determine whether or not to deny the request. AppCheck does it's job in my Firebase function by providing attestation based on referrer, however, I haven't found a way to retrieve the Recaptcha score.
I've tried retrieving the AppCheck token from the X-Firebase-AppCheck header and submitting it to the Recaptcha service for verification, but that returns an invalid user response error.
So my question is: is it possible to retrieve the Recaptcha score from the callable function context? If not, how is this case normally addressed?
I've manually included the Recaptcha script and retrieved the token on the client side which works, but that amounts to Recaptcha being added twice (first with AppCheck and second from a manual inclusion), which doesn't feel right.

I've spoken to Firebase support and it seems that it is not possible to retrieve the Recaptchav3 score from AppCheck. So the only solution is to retrieve it on the client-side as you would in a typical Recaptcha config, and submit it along with the request to your backend. The process to verify the Recaptcha token and retrieve the score once received at the backend remains the same.
So at the moment, the only value AppCheck on the web provides is automatic referrer verification.

Related

Trouble using Firebase Auth to access google API via gapi SDK

In my firebase (Angular) app, I'm using firebase authentication to log a user in via their Google Profile. As part of this process, the user gives me permission to access their gmail account (scope 'https://www.googleapis.com/auth/gmail.compose').
After the user has logged in this way, I want to configure the "gapi" google javascript SDK so that the "signed in user" is the user signed in via firebase auth. Here's where I'm having trouble.
It appears that I need to set the client token for the gapi sdk like so gapi.client.setToken(userAccessToken) and the token needs to be set before the gapi client is initialized. Attempting to do this doesn't seem to work however (a call to gapi.auth2.getAuthInstance().isSignedIn.get() returns false when it should return true).
I also can't figure out a way of changing the "signed in user" if the firebase user logs out and a new one logs in. This is because, again, the gapi client seems to require the gapi.client.setToken() be called before the client is initialized, and I can't see any way of re-initializing and already initialized gapi client.
I can get the gapi client working if I use the gapi client's own gapi.auth2.getAuthInstance().signIn() method, but then the user is asked to sign in to my app twice using (from the user's perspective) identical google login popup boxes (one prompt originating from firebase auth and the other from the gapi client).
Does anyone have any suggestions / tips? After someone logs in via firebase auth I can get access to their userAccessToken, I just can't figure out how to programmatically pass that to the gapi client in a clean way.
Ideally:
on application load the gapi client would also load and initialize.
When a user chose to sign in, I would be able to use Firebase Auth to log someone in via their google profile, then get their access token and pass it to the gapi client to make google api calls.
If the firebase user ever logged out, I would clear the gapi client's api token.
If a new firebase user logged in, I would re-set the gapi client's api token.
I have come upon a placeholder (i.e. non-ideal) solution to this problem by following this S.O. answer.
In short, the GAPI client does not seem to let you manually pass it an access token, but the firebase auth client does let you manually pass it an access token. So, instead of handling authentication with the firebase sdk and passing the token to the GAPI client, you need to do the reverse and handle authentication with the GAPI client and then pass the token to the firebase SDK.

Replacing Google Sign-In for Websites with Cloud Identity-Aware Proxy

There's an open feature request for Metabase to support IAP. I took a stab at it, and have a Clojure implementation of the steps detailed in Securing your app with signed headers (i.e. verify token header, verify token payload, retrieve user identity).
But this question isn't necessarily specific to Metabase. The general idea is to replace Google Sign-In and only use only IAP signed headers for authentication and user creation in an application on Google App Engine (specifically, GAE flex environment).
The "problem" is that the user identity information from the IAP token looks like: {"email":"alice#example.com","sub":"accounts.google.com:118133858486581853996"}. I also came across Using special URLs, but this returns something like: {"email":"accounts.google.com:USER_EMAIL","sub":"accounts.google.com:118133858486581853996"}.
With a Google Sign-In token, I can obtain values for given_name and family_name along with email, which means I can fetch-or-create a valid Metabase user. Is there a way to get the first and last name via the JWT sub, (i.e. accounts.google.com:118133858486581853996)?
Hm, if they have a public profile you can pass the number after "accounts.google.com:" to https://developers.google.com/+/web/api/rest/latest/people/get . Unfortunately, you won't be able to authenticate to that API as the user, since IAP doesn't currently provide a way to call let users delegate access to call Google APIs. (You'll have to use a service account to call that API.)
The other solution would be, if IAP provided a way to a) specify additional scopes in its OAuth request to Google, and if it then b) passed additional claims from the OIDC token into the IAP JWT, you'd be able to configure IAP to request the "profile" scope. However, IAP currently only requests the "email" and "openid" scopes, and doesn't have a mechanism for specifying additional scopes.
-- Matthew, Google Cloud IAP engineering

Firebase Auth on Web - How to add spam protection for email/password login

For my website, I want to build my own login form for email/password based authentication using Firebase authentication instead of using FirebaseUI Web. I'll be using createUserWithEmailAndPassword JS function to create new user accounts. But how can I prevent spam registrations? Usually for web based forms, I would use Google Recaptcha and validate the recaptcha on my server. But here, I'm not using my server for creating the user accounts. I'm making a call on the client side to create the user accounts.
Of course, I'll be using email verification in the flow, but how would I prevent bots from creating the accounts in the first place?
I also understand that Firebase has some sort of limit for the number of requests per min from a single IP, but I would like to go further and try to prevent those registrations.
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
Thanks.
After 2 years, this question is still valid and as far as I see, it is not possible. You probably do not need an answer to this question anymore but it may help others. Even if you succeed in doing something manually, those js functions will stay there and can be called manually by any user who knows how to do it.
If there are no hidden, top secret options which are not available in the documentation, this is not possible. There is a recaptcha option but it is only for Phone Authentication. So, it seems like you have 2 options.
Ignore and delete users who do not verify their email address.
Disable email option from Firebase console and implement your own
email authentication. Generate your own token and log user into
Firebase with that custom token. https://firebase.google.com/docs/auth/web/custom-auth
I'm following up on frankish's answer. He is totally correct, and I agree I think it's strange that Firebase automatically integrates ReCaptcha when doing phone authentication (and now when doing Phone MFA), but does not provide support in createUserWithEmailAndPassword for passing a recaptcha verifier. Thus, the only way to really get around this is to do something like the following:
Set up ReCaptcha (either V2 or V3) manually on your signup page. Do NOT use firebase. auth. RecaptchaVerifier, that is only for integration with phone authentication.
Immediately after calling createUserWithEmailAndPassword, you need to make a call to your own server that passes up the recaptcha token. There is a Firebase blog post here about how to do that with a Firebase Function: https://firebase.googleblog.com/2017/08/guard-your-web-content-from-abuse-with.html. Note I think it's a bit strange that Firebase documented how to do this with server-side functions but didn't directly integrate this with account creation.
The final point is that in your server-side code, after you make the call to validate the recaptcha token, you need to set a custom claim on the Firebase user with the Firebase Admin API. That claim can be something like recaptchaPassed: true (or false). For details on custom claims see https://firebase.google.com/docs/auth/admin/custom-claims.
After that, you can then do things based on the value of that custom claim. For example you could read that custom claim in other server-side calls, or you can use it in Firestore security rules (good blog post on this, https://medium.com/google-developers/controlling-data-access-using-firebase-auth-custom-claims-88b3c2c9352a). You could also choose to immediately delete the user server-side (using the admin API), if recaptcha verification fails.
Note it's important to understand that there is nothing that guarantees that some malicious script will call your server-side token verification function after the code on the client calls createUserWithEmailAndPassword. Thus, the only way the rest of your code can guarantee that a particular Firebase user passed recaptcha verification is by looking for your custom claim that you set on the user server-side.

Directly validate Firebase token from react native app

I implemented Auth with Firebase + Facebook. To be able to use the Auth with my custom backend I implemented the admin SDK on my node Server. Now I am trying to validate if my Firebase token is still valid to know when I need to show the login screen. I could probably send the token to my custom backend. But since I don't need anything else from the backend, it would be easier to directly call firebase from the client.
Does someone know if a function like that exists?
Firebase client SDKs periodically refresh the ID tokens and keep them alive. Therefore the token will remain valid, until the client explicitly invokes a sign out operation. See this discussion for more details: https://groups.google.com/forum/#!msg/firebase-talk/rjR0zYiiEhM/Clt9aFtgAwAJ (It is a little old, but still relevant

Using firebase jwt to authenticate users to external server\service?

Okay so in my iOS app I log the user into firebase, then get the jwt token. So now I have my server with an api which accepts an idtoken in the header of the GET.
What do I do here? Certainly I wouldn't be validating the JWT againt firebase on every single API call right? I mean its fast, but that adds latency with a second external check, no? How does one simply just decode that guy in C#? I have an Auth0 layer already and that decodes the JWT with my server-stored secret, but that same code doesn't work for the Firebase token.
Could it just be decoded then extract the user details from that, maybe just check expiry and if expiry > X months it's still okay?
In order to verify Firebase ID tokens and JWTs in general, you only make a network call on your server to get the public certs which are usually not updated for several hours. You could cache that and try to verify with an ID token and if it fails, only then, load the new public certs.
And yes, you must verify the ID token on each call especially since Firebase ID tokens expire after typically an hour and need to be refreshed continuously.

Resources