I have just started working with callable functions on firebase.
I deployed a function that gives the response hello world.
It worked fine when I tested it locally.
Now I am trying to call it from android app.
According to this document, these callable functions automatically pass the authentication data and deserialise the data to get the user id in the function. When I run as suggested in the document above , I get an Unauthenticated error.
I have used google sign in using firebase auth in the app. I dont want to make it public and keep it private so only the users can send requests. Is there any way I could manually include authentication in the header of the request.
Here is the code from the call
sendmessage(string)
.addOnCompleteListener {
if (!it.isSuccessful){
val e= it.exception
if(e is FirebaseFunctionsException){
val code =e.code
val details = e.details
Log.d("Firebase error",code.toString()+details.toString())
}
}
else{
Log.d("result obtained",it.result.toString())
}
}
private fun sendmessage(question:String): Task<String> {
val data = hashMapOf(
"question" to question,
"push" to true
)
return functions
.getHttpsCallable("detectIntent")
.call(data)
.continueWith {
val result=it.result?.data as String
result
}
}
Callable functions don't provide any amount of control over what happens in the HTTP headers. Authentication is handled automatically, based on the state of the user signed in through Firebase Auth. If you're not getting an authenticated user in your function in context.auth, that means you made the request before sign-in was complete.
Related
When a user in a registration fragment enters a username, email, and password he is forwarded to a verification fragment where I sent him email verification and he stays on that screen until he clicks the link from the email, after that he's forwarded to profile fragment.
While the user is on the verification fragment, he can go back to the registration fragment before clicking the link from the email I sent. In that case, I call a function from onStart() to delete his data from the Firebase:
private fun deleteUnverifiedAccount() {
val firebaseAuthentication = FirebaseAuth.getInstance()
if (firebaseAuthentication.currentUser == null) {
shortToast("empty")
}
if (firebaseAuthentication.currentUser != null) {
if (!firebaseAuthentication.currentUser.isEmailVerified) {
val user = firebaseAuthentication.currentUser
user.delete()
.addOnCompleteListener {
shortToast("deleted")
}
}
}
}
Here's the problem:
This function successfully deletes the user from Firebase Authentication
But when the user tries to register the second time, his data is again being saved to Firebase Authentication.
And if at the moment he goes back again to the registration fragment without verifying his email then his data is not deleted from Firebase Authentication.
And when the deleteUnverifiedAccount() function is called it says that currentUser == null even though he is clearly not null in Firebase Authentication.
Firstly i did not try to delete any user from firebase but i have just searched something and i find this link https://www.javatpoint.com/firebase-realtime-database-update-and-delete this link will help you because i think problem of your code you try to delete your user but not user's data.
your syntax will be like this
myFirebase.child(current user's id).remove value
can you try this before user.delete() i hope it works
I need to receive the "null" value from the Firebase.auth.currentUser function, but it returns some default user.
val auth = Firebase.auth
val currentUser = auth.currentUser
if (currentUser != null) {
val action = SignInFragmentDirections.actionSignInFragmentToTransactionsFragment(currentUser)
findNavController().navigate(action)
}
Since the auth.currentUser never returns null, my application always goes to another screen and does not allow registering a new user.
Why is this happening and how can I get null when calling auth.ŃurrentUser?
"Does not allow registering a new user"
It seems to me you are trying to create multiple users from using the Android SDK but it is not built for that. Once you sign in, then you would have to sign out to create another user because only one user can be logged in a single application (at least of a single Firebase project).
If you want to create multiple accounts then you can do so directly from the Firebase console. However if your app is meant to be used by admin or privileged users who can create new accounts and you don't want to give them access to the Firebase console then you would have to use something like Cloud functions or your own server.
There you can use the Firebase Admin SDK to create new users. A simple functions may look like:
exports.createNewUser = functions.https.onCall((data, context) => {
return admin
.auth()
.createUser({
email: data.email
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
console.log('Successfully created new user:', userRecord.uid);
return userRecord.uid
})
.catch((error) => {
console.log('Error creating new user:', error);
});
});
You can call the function from your app like this:
private fun createUser(name: String, email: String): Task<String> {
// Create the arguments to the callable function.
val data = hashMapOf(
"name" to name,
"email" to email
)
return functions
.getHttpsCallable("createNewUser")
.call(data)
.continueWith { task ->
val result = task.result?.data as String
}
}
You can read more about callable functions here.
Do note that I simply ran the create user method in the cloud functions but ideally you should check which user is calling the function (context.auth has the info) and make sure only the authorized user can use that function.
I figured it out - if I logged in app and didn't call the auth.signOut method - I deleted the account from the Firebase Console only, for this reason the auth token saved locally for a some time and for this reason auth.currentUser returned non-null. When I deleted the app (and added the signOut logic in my app later) and reinstall it, all works correct.
I have an application that is implemented with clean architecture with MVVM pattern. In this app we need a refresh token request that is needed in all the app features. When a refresh token is success then call the last request again. What is the best way to implement this?
I have two idea:
1 - Implement it in every feature and use it. So if I have three features in my app I will implement it three time.
2 - Implemented globally
I know the first idea but I can't figure out how to do the second one which I think is better.
I use retrofit for networking . The structure is : data , domain , presentation .
With Retrofit you can create a custom Authenticator which will trigger when a request was denied because of an authentication error (typically 401, the documentation for the Authenticator interface explains more). In your authenticator you can retrieve a new token and automatically create a new request with the new token.
An authenticator will be something like:
class Authenticator : okhttp3.Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// Make your network request to retrieve a new token
val newToken = ...
// Check if a new token was retrieved
val retrievedNewToken: Boolean = true
return if (retrievedNewToken) {
response.request().newBuilder()
.header("Authorization", "token-value-here")
.build()
} else {
// Couldn't get new token, abort the request
null
}
}
}
And use it in your OkHttpClient:
val client = OkHttpClient.Builder()
.authenticator(Authenticator())
.build()
This is a fairly general answer as I can't provide any specific way of doing it since I don't know the rest of your code, but that's the gist of it. Something to be aware of is that you might need to handle if a new token request is already happening, as it will potentially make multiple requests for a new token if you make several requests right after each other that all are denied.
I have firestore rules like this:
match /{document=**} {
allow read, write: if request.auth.token.name == "dummyUser";
}
Now when I createUserWithEmailAndPassword, after I created the user, I set the displayName to "dummyUser" like this, with method setUsersSecureName() that I made:
fun setUsersSecureName(myCallback: (Boolean?) -> Unit) {
val user = FirebaseAuth.getInstance().currentUser
val profileUpdates = UserProfileChangeRequest.Builder()
.setDisplayName("dummyUser")
.build()
user?.updateProfile(profileUpdates)
?.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG_HELPER_METHODS, "Secure user profile updated.")
myCallback(true)
}
}
}
I do the same for anonymous logins also. So I create anonymous log in like this:
auth.signInAnonymously()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG_MAIN, "signInAnonymously:success")
val user = auth.currentUser
setUsersSecureName(){
makeRequest()
}
As you see, after login is successful with anonymous user, I change the users name. Now it seems to be changed, when I check it, the users displayName is "dummyUser". However, the firestore request doesn't work, and I get the message in the log:
PERMISSION_DENIED: Missing or insufficient permissions.
This is also not working when users are signed in with email and password. Why would this be?
Thanks
Security rules work by receiving a user ID token from Firebase Authentication at the time of the request. The Firestore SDK does this automatically. You normally don't have to do anything special.
However, in this specific case, after you update the user's profile, Firebase Authentication is still holding on to a user ID token that doesn't know about the change of name. You will probably also have to tell Firebase Authentication to fetch a new one by calling user.getIdToken(true) after the profile is successfully updated, in order to force a refresh of the ID token. After the refresh succeeds, then you can try the Firestore query to see if it works. user.reload() might work as well. Both of those methods are asynchronous and return a Task that you should use to track the completion of the request.
I have two APIs say API 1 and API 2. API 1 get authentication token and API 2 get user profile info. I need to hit API 2 from ViewModel but if API 2 gives an authentication error, then I need to get new token first and then hit API 2(user info)
Currently, I use RxJava Single and Retrofit for all APIs but for APIs one by one. There are a lot of APIs that use authentication token and if it expires in any of the APIs then I need to call authentication API and get the token again and then call the APIs with a new authentication token.
What should be the best way to design a wrapper so that it can be used with all APIs with code reusability as well.
Thanks in advance.
I have some idea. If you have basemodel for getting response for API, you can make custom getter for response code and check if response code in token_expired.
This is sample response that I got From API.
{
response_code: "200",
response_msg: "",
result: ...
}
And this is my BaseModel.
class BaseModel<T> {
#SerializedName("response_code")
var response_code: Int? = null
get() {
if(field == ErrorStatusCode.TOKEN_EXPIRE) {
ErrorCodeHandler.requestNewToken()
}
return field
},
#SerializedName("response_msg")
var response_msg: String? = null
#SerializedName("result")
var data: T? = null
}
So whenever token expire, I will request Net token by call ErrorCodeHandler.requestNewToken()
But If you want to recall API that found token timeout I have no idea what to do too.
PS. Hope this help.