Proper way to verify user's mobile number using Firebase - firebase

I know that I can use Firebase's phone verification on Android and iOS, but the problem is that the information about client's verification can easily be forged on the client side, because I using only server side SSL certificate, so, only client knows that the server is trusted.
So, I decided to send mobile number on the server-side and check it there: send verification code and ask this verification code from the user. But I can't see any C++ server Firebase SDK, only client-side C++ SDK is available.
So, I have two options:
Understand how is client-side verification can be trusted on the server-side(note that I can have untrusted clients)? So, it's means I could use main Firebase phone number auth method.
Use server-side phone verification.
Please, help me with this misunderstanding in the Firebase.

Client side absolutely works here. The flow is like this:
You request a sign in using a phone number
The Firebase Phone Auth server sends a code to that number
The user enters your code into your app, which sends it to the Firebase Auth server
The Firebase Auth server returns you a Firebase Auth token
This works because a malicious user could only know the code if they had your phone. It doesn't guarantee the device is the one with that phone number (the user could have two phones, or sign in with a phone on a laptop), but it does verify the user has access to that number.
For verifying that to your own backend, you retrieve a Firebase ID token. This is just a little bundle of base64 encoded JSON, but importantly its cryptographically signed by Firebase. This means on your server you can verify that it was really created by Firebase, for the user and phone number that is contained within it. A user couldn't generate one of those tokens without access to the underlying account.
See the docs on verifying ID tokens for more!
So your next steps would be:
Retrieve the Firebase ID token
You can do this any time you're signed in.
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
// Send token to your backend via HTTPS
// ...
} else {
// Handle error -> task.getException();
}
}
});
Verify on server the contents of the ID token.
The admin SDKs are set up out of the box to check for the right certificate, audience, expiry, and other important properties of an ID token.
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
var uid = decodedToken.uid;
// ...
}).catch(function(error) {
// Handle error
});
decodedToken will contain properties for the phone number too!

Related

Firebase auth: update lost phone number

When a user has lost their phone number, and they want to update their account with a new phone number, what would be the best path to follow? We already have their email.
The problem is Firebase needs to send an SMS to the old phone number before updating it, which the user has lost.
My best idea so far is to authenticate them with their email, then send a request to our backend with the new phone number they entered, and then use the Firebase Admin SDK to update the user's phone number. There's just one problem here: How would I verify this new phone number, to make sure the user didn't just add some random one?
I'm answering my own question with the path that I followed:
First, we get their email and send a verification email, which contains a button that redirect to a deeplink that contains a token that we use on the backend to verify that the user indeed is who they claim to be.
After the user clicks the link, we show a screen on the app that asks for the new phone number.
After the new phone number is entered, we send it to our backend, along with the token on the deeplink. We verify the token, then use Twilio to send a verification SMS to this new number.
In the meantime, the user has already been sent to the verification code screen. They get the Twilio SMS and put in the code. We send this code to our backend and make sure the code they entered is correct.
If the code is correct, we use the Firebase Admin API to update this user's phone number to the new one.
Firebase has a method for phone authentication
For Android: https://firebase.google.com/docs/auth/android/phone-auth?authuser=0#send-a-verification-code-to-the-users-phone
For Web: https://firebase.google.com/docs/auth/web/phone-auth#send-a-verification-code-to-the-users-phone
For IOS: https://firebase.google.com/docs/auth/ios/phone-auth?authuser=0#send-a-verification-code-to-the-users-phone
Edit
The way to change a user's phone number is through the method below
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.updatePhoneNumber(credentials)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
}
}
});
The problem is you need to send a verification code to the user's old phone number (Links above) to create a PhoneAuthCredential (reference).
I do not know a way around that using firebase

Firebase Auth verify this user

I am currently verifying my user using the Auth JS SDK and Admin Auth SDK combined. I am doing in the following approach:
In the front-end:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
var current_user = firebase.auth().currentUser;
current_user.getIdToken(true).then(function (idToken) {
$.getJSON('/firebase_token', { token: idToken }, function (user) {
In the back-end:
router.get("/firebase_token", (req, res, next) => {
admin.auth().verifyIdToken(req.query.token).then(function(decodedToken) {
res.send(decodedToken);
})
})
I am wondering if this is a secured approach, because the user can just send whatever token they want from the front-end. For example, an invalid user can send a valid token they copied from a valid account to pass the token verification.
I am wondering if in the admin SDK. There is a way to detect the currently signed in user. In other words, detect this user who is using this instance of the app with the admin SDK?
I am wondering if this is a secured approach, because the user can just send whatever token they want from the front-end. For example, an invalid user can send a valid token they copied from a valid account to pass the token verification.
Yes, that's possible. But then again, if the user got access to a token, that means they probably are the user represented by that token, or they know the credentials of that account. That's not a problem at all - this is the way authentication systems work.
I am wondering if in the admin SDK. There is a way to detect the currently signed in user. In other words, detect this user who is using this instance of the app with the admin SDK?
No, the Admin SDK can't possibly know what all is going on for all of the users using your application. The ID token is exactly the piece of information it needs to verify users. A valid token proves that the user is who they say they are.

Firebase admin - get Google OAuth token

I have a web application where users can sign in with Google.
To the sign-in process, I add a scope to be able to access Google Calendar.
Now that the user is signed in, I would like to - in server-side - get their current Google access token in order to make a request and get a list of their events.
Is there a way to get the current OAuth token (no need for refresh token) in order for me to make this completely on the server-side?
I'd say that you can check this article and put special attention to the recommendation for websites.
I understand you have configured already the consent screen, which is the first step of the basic steps on using OAuth 2.0. So I understand that you only have to perform the following steps:
Obtain an access token from the Google Authorization Server
Examine scopes of access granted by the user.
Send the access token to an API
I think you can also give a look to this other doc for more GCP insights over your goal to authorize the request using user tokens
Edited:
Regarding the Firebase Authentication, I understand this happens at the user's device, and you could use some code to retrieve the token and then send it to your back end servers as mentioned in here.
As a sample here there's the sample code for retrieving the token in Android:
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
// Send token to your backend via HTTPS
// ...
} else {
// Handle error -> task.getException();
}
}
});
A little about OAuth 2.0
Whenever a user signs up to your app/website via Google or 3rd Party, an Authorization Code, this Authorization Code is exchanged for an AccessToken & RefreshToken.
The AccessToken sent via Google are valid generally for 60 minutes.
Offline Access (Server Side)
Let's break it down to two parts:
If your need to update within 60 minutes of user's last activity
You can use firebase along with gapi to achieve that. You'll be provided with the AccessToken that can be sent back to server to add to calendar.
More info on implementation
If you need to update after 60 minutes of user's last activity
Firebase & gapi's most method handle the AuthorizationCode flow internally. They even further refresh the AccessToken after 60 minutes. This is beneficial for most developers as they won't have a headache of managing all the tokens.
This method but, hides RefreshToken & AuthorizationCode from the developer. That is even if your server has the access token, it won't be able to refresh it and it would be deemed useless.
To achieve complete offline access, in the initial request to get AuthorizationCode you will need to send a HTTP GET parameter access_type to offline
GAPI provides you with grantOfflineAccess() method which returns the AuthorizationCode that can be later used on your server to fetch access token & refresh token.
Note: If you are storing AuthorizationCode in your database, make sure it is secure. The limitation in Firebase are set due to security reason. It is more secure to not talk with AuthorizationCode generally.
More links
https://developers.google.com/identity/protocols/oauth2/web-server
https://developers.google.com/identity/sign-in/web/reference
https://developers.google.com/identity/sign-in/web/server-side-flow
https://developers.google.com/identity/sign-in/web/backend-auth
Retrieve Google Access Token after authenticated using Firebase Authentication

How to get all of device tokens from Firebase?

How to get device tokens (FCM registration tokens) from Firebase?
I'm making mobile app using firebase and its server using node.js.
I want to send push notification message from server, but I don't know how to get devices token.
How to get device tokens from external server? I'm using Firebase admin SDK now.
Is the device token only generated when the app is connected fcm server?
Should I save token to another database when user first run the app and register FCM server?
1. How to get device tokens from external server? I'm using Firebase admin SDK now.
There is currently no API to retrieve all the Registration tokens for your app from the Server side. It's the developer's (you) responsibility to send and store the Registration token to your App Server, which is generated from the Client App side.
2. Is the device token only generated when the app is connected FCM server?
The documentation pretty much covered this (see also my answer here):
On initial startup of your app, the FCM SDK generates a registration token for the client app instance.
It doesn't technically get connected to the FCM Servers. It connects to the FirebaseInstanceID service (via the SDK), which in turn generates the Registration Token.
3. Should I save token to another database when user first run the app and register FCM server?
As I mentioned in #1, you should save it where you can easily access it to send a message.
The device token is generated when the app first connects, this token must then be stored in your database, and replaced if a new token is assigned
public class MyAndroidFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String TAG = "MyAndroidFCMIIDService";
#Override
public void onTokenRefresh() {
//Get hold of the registration token
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
//Log the token
Log.d(TAG, "Refreshed token: " + refreshedToken);
}
private void sendRegistrationToServer(String token) {
//Implement this method if you want to store the token on your server
}
}
See the following Tutorial
I suggest not storing the tokens at all. Storing the token seems like a good idea until you realize that your users have multiple devices (iOS device, android device, desktop etc.) Now you have to manage storing multiple device tokens per user and that can get messy fast.
Instead, ignore the tokens that are generated and simply subscribe each user to a topic such as when user logs in or creates account subscribe them to a topic made of their userid
NSString* useridchannel = [NSString stringWithFormat:#"user_%ld", (long)userid];
[[FIRMessaging messaging] subscribeToTopic:useridchannel completion:^(NSError * _Nullable error) {
NSLog(#"Subscribed user to topic %#", useridchannel);
}];
When user logs out make sure to unsubscribe them from that topic as they probably don't want to receive notifications if they are no longer logged into your app
NSString* useridchannel = [NSString stringWithFormat:#"user_%ld", (long)userid];
[[FIRMessaging messaging] unsubscribeFromTopic:useridchannel completion:^(NSError * _Nullable error) {
NSLog(#"Unsubscribed user from %#", useridchanel);
}];
I realize that Device Groups are supposed to be used for this purpose, but again I don't want to be bothered with storing and managing tokens and also Firebase Admin SDK for PHP does not support sending messages to Device Groups. By using topics there is no need to deal with device tokens and you can send messages to all the users devices from server, the app, or firebase console easily.
You can make a get request to
https://<<your_project_name>>.firebaseio.com/.json?auth=<<your_database_secret_key>>
The database secret key you get in Firebase console under the tab settings/serviceaccounts/databasesecrets
This returns a JSON file containing all Storage data of your project.

Is there a way to determine if a Firebase user's UID is valid?

I am building a server route that I wish to restrict for use only by authenticated users. I plan to send a user.uid with a POST to this route, and I want to validate the UID is one that exists in Firebase. I know I can add UIDs manually in Firebase and check against this data, but is it possible to see what UIDs Firebase authentication is tracking? I think this approach would be better then checking against my own list.
The purpose for this is to ensure that these routes are not accessed with a phony UID (e.g. for malicious purposes).
Validating a UID is not enough to block malicious users: 1) the attackers could pretend to be other users by sending other user's UID, and 2) UID never changes or expires, which means there is no way to enforce the users (or attackers) to re-authenticate.
What you need is to pass the Firebase token from client app to your server, and validate the token before accepting it.
The token is securely signed by Firebase private key. No other party can issue a valid Firebase token.
The token is valid for only one hour. Firebase server will check the account status (e.g. password change event) before issuing a new token.
The token payload contains UID and audience. You should verify audience is your own application.
You can use Firebase Admin SDK or third party libraries to verify a Firebase token. See Firebase doc for details.
You can check whether a specific UID corresponds to a Firebase Authentication user in your project by using the Firebase Admin SDK on your server. From the Firebase documentation on retrieving user data:
admin.auth().getUser(uid)
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully fetched user data:", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
A UID is part of the payload when a Firebase user is authenticated to Firebase and is null when a user is not authenticated. You can get the UID upon user authentication. The syntax is different depending on what framework you are working in.
Check out the Firebase API Reference for specific syntax and examples: https://firebase.google.com/docs/reference/
create a token in your client app
private String getAuthTokenAndPost(){
mAuth.getCurrentUser().getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if(task.isSuccessful()){
String idToken = task.getResult().getToken();
sendReqToServer(idToken);
}else{
Toast.makeText(CartActivity.this, "couldn't generate Token", Toast.LENGTH_SHORT).show();
}
}
});
return "";
}
then use firebase admin SDK on your server side, here is an example of a Node server
getAuth()
.verifyIdToken(idToken)
.then((decodedToken) => {
const uid = decodedToken.uid;
// ...
})
.catch((error) => {
// Handle error
});
ID token verification requires a project ID. The Firebase Admin SDK attempts to obtain a project ID via one of the following methods:
If the SDK was initialized with an explicit projectId app option, the SDK uses the value of that option.
If the SDK was initialized with service account credentials, the SDK uses the project_id field of the service account JSON object.
If the GOOGLE_CLOUD_PROJECT environment variable is set, the SDK uses its value as the project ID. This environment variable is available for code running on Google infrastructure such as App Engine and Compute Engine.
check the document

Resources