I am trying to follow this tutorial.
And I created this firebaseAuthWithGoogle function:
public void firebaseAuthWithGoogle(Activity context,FirebaseAuth mAuth, GoogleSignInAccount acct) {
Log.i(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(context, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.i(TAG, "firebase signInWithCredential:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Log.i(TAG, "firebase signInWithCredential" + task.getException());
}
// ...
}
});
}
Which I call from here:
final GoogleSignInAccount acct = result.getSignInAccount();
AsyncTask asyncTask = new AsyncTask() {
#Override
protected Object doInBackground(Object[] params) {
try{
String scope = "oauth2:" + Scopes.PROFILE;
Account account = new Account(acct.getEmail(), "com.google");
final String token = GoogleAuthUtil.getToken(PSSignInFlowActivity.this, account, scope); PSSocialService.getInstance().firebaseAuthWithGoogle(PSSignInFlowActivity.this, PSApplicationClass.getInstance().mAuth, acct);
}catch (Exception e){
Log.e("","firebase error trying to get client secret : " + e.getMessage());
}
return null;
}
};
asyncTask.execute();
My idToken is: firebaseAuthWithGoogle:1024xxxxxxxxxxxxxx956 (so it is not null, added it maybe I'm not sending what I'm suppopsed too?)
But when I debug, i get this error:
Must specify an idToken or an accessToken.
What am I doing wrong? They use the same logic on the tutorial
PS: I am on compile 'com.google.firebase:firebase-auth:10.2.0' if it matters
EDIT: I noticed that acct.getIdToken() returns null, why is that?
Shouldn't the GoogleSignInAccount have a idToken?
I was logging out the acct.getId() which is the 1024xxxxxxxxxxxxx956 I wrote upper.
EDIT2: I added to my GSO this option:
.requestIdToken(serverID)
And now I get the idToken, but I get this response back:
04-05 10:34:16.388: I/PSSocialService(6285): firebase signInWithCredentialcom.google.firebase.FirebaseException: An internal error has occurred. [ Invalid Idp Response ]
serverID is: 264xxxxxxxxxx27.apps.googleusercontent.com
Is that the correct value I should use?
In the tutorial they use:
.requestIdToken(getString(R.string.default_web_client_id))
But I tried that, and it crashes, cause It says I need to use the same clientID.
I use serverID for .requestServerAuthCode(serverID) and I guess that is why
In the firebase console, for Authentification/Providers/Google it is needed to add the all external cliend ids, in order to whitelist them. This is why it would not work for me
Related
setting up the Android Firebase on Android.
I need to get the unique identifier of the user, so that I can have consistency accross all platforms no matter if the email is hided or not.
Based upon https://firebase.google.com/docs/auth/android/apple , using .startActivityForSignInWithProvider(this, provider.build()) we can see:
public void onSuccess(AuthResult authResult) {
Log.d(TAG, "checkPending:onSuccess:" + authResult);
// Get the user profile with authResult.getUser() and
// authResult.getAdditionalUserInfo(), and the ID
// token from Apple with authResult.getCredential().
And from Apple https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773 we can also see
The identity token is a JSON Web Token (JWT) and contains
and
sub
The subject registered claim identifies the principal that’s the subject of the identity token. Because this token is for your app, the value is the unique identifier for the user.
Question:
What I got from firebase is AuthCredential , but I am expecting JWT with "sub" in it.
How can I get it?
thanks to Ricardo from Firebase support I got this working.
in Android the sub value from Apple is in
"firebase":{"identities":{"apple.com":["001814.24f212edfsdfdfsfd69b7b5fee972e.1722"],"email":["a#b.com"]},"sign_in_provider":"apple.com"}
You get it using
auth.startActivityForSignInWithProvider(this, provider.build()).addOnSuccessListener( new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(AuthResult authResult) {
// Sign-in successful!
FirebaseUser user = authResult.getUser();
AuthCredential identityToken = authResult.getCredential();
authResult.getUser().getIdToken(false).addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult result) {
String idToken = result.getToken();
//Do whatever
Log.d(TAG, "GetTokenResult result = " + idToken);
try {
decodeJWT(idToken);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
})
Where the JWT is decoded with
private static void decodeJWT(String JWTEncoded) throws Exception {
try {
String[] split = JWTEncoded.split("\\.");
Log.d("JWT_DECODED", "Header: " + getJson(split[0]));
Log.d("JWT_DECODED", "Body: " + getJson(split[1]));
} catch (UnsupportedEncodingException e) {
//Error
}
}
private static String getJson(String strEncoded) throws UnsupportedEncodingException{
byte[] decodedBytes = Base64.decode(strEncoded, Base64.URL_SAFE);
return new String(decodedBytes, "UTF-8");
}
I am implementing apple sign in with Firebase in Android. I have followed the instructions mentioned here : https://help.apple.com/developer-account/#/dev1c0e25352
also created the Key, as mentioned here : https://help.apple.com/developer-account/#/dev77c875b7e
also enabled Apple Sign in from firebase console and added the Service ID, and newly generated Key data to the firebase console.
my Code :
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
List<String> scopes =
new ArrayList<String>() {
{
add("email");
add("name");
}
};
provider.setScopes(scopes);
mAuth.startActivityForSignInWithProvider(this, provider.build())
.addOnSuccessListener(
new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(AuthResult authResult) {
// Sign-in successful!
Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
FirebaseUser user = authResult.getUser();
// ...
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "activitySignIn:onFailure", e);
}
});
but i am getting an error, in the OnFailureListener:
The supplied auth credential is malformed or has expired. [ Error getting access token from https://appleid.apple.com, OAuth2 redirect uri is: https://******.firebaseapp.com/__/auth/handler, response: OAuth2TokenResponse{params: error=invalid_client, httpMetadata: HttpMetadata{status=400, cachePolicy=NO_CACHE, cacheDurationJava=null, cacheImmutable=false, staleWhileRevalidate=null, filename=null, lastModified=null, retryAfter=null, headers=HTTP/1.1 200 OK
can anyone help out here.
we are trying to add Google Sign option to our project. We have successfully added this option to the site and to the ios application. But encountered difficulties adding to the android application. Using "Configure project" option in this page https://developers.google.com/identity/sign-in/android/start-integrating I specify SHA1 of Andrid debug key (Gradle -> android -> signingReport) and package name (com.my.app). This form gives us WEB type client ID. I downloaded credentials.json and put it in the app folder of the project. I also found an android type client ID with specified SHA1 hash in the Google API Console, but I don't understand where to use it (if we use android type oauth client, we get an error "signInResult:failed code=10", so we should use web type client, and without such android client in the API console we get 12500 error and no token).
Next we added a sample code to get token and pass it to our backend server according to this document https://developers.google.com/identity/sign-in/android/backend-auth.
private void enterGoogle() {
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken("12345-OurWebTypeClient.apps.googleusercontent.com").build();
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, RC_SIGN_IN);
}
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
String accessToken = account.getIdToken();
authWithGoogleToken(accessToken);
} catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
private void authWithGoogleToken(String accessToken) {
if (accessToken == null)
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
else
RetrofitFactory.getInstance().authGoogle(accessToken)
.enqueue(new retrofit2.Callback<JsonObject>() {
#Override
public void onResponse(retrofit2.Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
if (response.isSuccessful() && response.body() != null) {
if (!response.body().has("error")) {
Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, response.body().get("error").getAsString(), Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(retrofit2.Call<JsonObject> call, Throwable t) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
}
});
}
using this code, we get long token which starts from "eyJ". If we change a code little bit to use alternative methods:
.requestServerAuthCode("12345-OurWebTypeClient.apps.googleusercontent.com").build();
and
String accessToken = completedTask.getResult().getServerAuthCode();
we get short token which starts from "4/0". We do not know what a difference between these 2 types of tokens from 2 methods of getting tokens, but on backend server we get one error for both types of tokens:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
We do not have such error for token from iOS SDK for example. Please help to find out what is wrong with implementation of Google Sign In in the Android app?
we solved it using this workaround
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
AsyncTask.execute(() -> {
try {
String accessToken = GoogleAuthUtil.getToken(context, account.getAccount(), "oauth2:" + Scopes.PLUS_LOGIN);
runOnUiThread(() -> {
authWithGoogleToken(accessToken);
});
} catch (Exception e) {
e.printStackTrace();
}
});
String accessToken = account.getIdToken();
authWithGoogleToken(accessToken);
Email is null when signing in with Google provider
I have tried with 3 Gmail accounts - 1 of them is OK, but with 2 I get null.
All three are listed in the console properly with the account's Email as "Identifier"
currentUser = FirebaseAuth.getInstance().getCurrentUser();
String Email = currentUser.getEmail();
code creating auth:
providers = Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
new AuthUI.IdpConfig.FacebookBuilder().build()
);
startActivityForResult(
AuthUI.getInstance().createSignInIntentBuilder()
.setAvailableProviders(providers)
.setIsSmartLockEnabled(false)
.build(),MY_REQUEST_CODE);
I had the same problem, no Google email address was returned.
After adding the scope email:
var provider = new this.$firebase.auth.GoogleAuthProvider();
provider.addScope('email');
it was returning the email address in the providerdata.
The same problem I had also with Facebook login. I needed to add the scope like this
var provider = new this.$firebase.auth.FacebookAuthProvider();
provider.addScope("email");
Try this one, I had the same issue, but on applying GoogleSignInAccount acct = GoogleSignIn.getLastSignedInAccount(LoginActivity.this); after authentication with firebase, the issue got resolved.
private void firebaseAuthWithGoogle(String idToken) {
AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = mAuth.getCurrentUser();
String a="Email: "+user.getEmail()
+"\nName: "+user.getDisplayName()+"\nL= "+user.getProviderData();
//Here I got email field as null: 1 of them is OK, but with remaining I get null
Log.d(TAG, "onComplete: USER DATA:\n"+a);
// Add this code to get current sign-in user info
GoogleSignInAccount acct = GoogleSignIn.getLastSignedInAccount(LoginActivity.this);
if (acct != null) {
String personName = acct.getDisplayName();
String personGivenName = acct.getGivenName();
String personFamilyName = acct.getFamilyName();
String personEmail = acct.getEmail();
String personId = acct.getId();
Uri personPhoto = acct.getPhotoUrl();
Log.d(TAG, "onComplete: RESULT--> "+"Name: "+personName+"\n"+
"Email: "+personEmail+"\n"+
"ID: "+personId+"\n"+
"Photo: "+personPhoto+"\n"+
"PersonGiven Name: "+personGivenName+"\n");
}
// updateUI(user);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Snackbar.make(r, "Authentication Failed.", Snackbar.LENGTH_SHORT).show();
// updateUI(null);
}
// ...
}
});
}
In my code, I can see that <currentUser.providerData> contains two lists.
After comparison, the 0th list had no email value, but contained displayName and photoUrl. The first list had all three values. So I'm using the first value. But I don't know the exact reason.
I copied the code from the testing project to my main project and the problem is resolved. I don't understand why.
i have set up firebase storage for my app, and added the code for anonymous auth on the app and on the firebase console.
it worked at first, but i dont know why it stopped working, saying that the user does not have permission to access the object
Anonymous auth is correctly set up and i did see it working, code is almost like Google Firebase docs
logcat:
D/FirebaseAuth: signInAnonymously:onComplete:true
D/FirebaseAuth:
onAuthStateChanged:signed_in: (Random auth user id)
... When i request the item from firebase
E/StorageUtil: error getting token java.util.concurrent.ExecutionException: com.google.firebase.FirebaseException: An internal error has occured. [Internal error encountered.]
I/DpmTcmClient: RegisterTcmMonitor
from: com.android.okhttp.TcmIdleTimerMonitor W/NetworkRequest: no auth
token for request E/StorageException: StorageException has occurred.
User does not have permission to access this object.
Code: -13021 HttpResult: 403
Can Someone help?
Declaring Variables
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
on the OnCreate method
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d("FirebaseAuth", "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d("FirebaseAuth", "onAuthStateChanged:signed_out");
}
// ...
}
};
mAuth.signInAnonymously()
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d("FirebaseAuth", "signInAnonymously:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Log.w("FirebaseAuth", "signInAnonymously", task.getException());
Toast.makeText(SingleMemeEditor.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
// ...
}
});
and the method that gets from storage:
Bitmap bmp;
final Context lContext = context; //getting the MainActivity Context
final String lFileName = fileName; //filename to download
final String lCatPath = catPath; //internal categorization folder
FirebaseStorage storage = FirebaseStorage.getInstance();
// Create a storage reference from our app
StorageReference storageRef = storage.getReferenceFromUrl(context.getResources().getString(R.string.firebase_bucket));
// Create a reference with an initial file path and name
StorageReference filesRef = storageRef.child("files/" + fileName);
try
{
final File localFile = File.createTempFile("images", "jpg");
filesRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>()
{
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot)
{
// Local temp file has been created
File file = new File(getDirectory(lContext)
+ File.separator + lCatPath + File.separator + lFileName);
try
{
Boolean b = file.createNewFile();
if(b)
{
FileInputStream in = new FileInputStream(localFile);
FileOutputStream out = new FileOutputStream(file);
// Transfer bytes from in to out
byte[] buf = new byte[(int)localFile.length()];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
Drawable.createFromPath(file.getPath())).getBitmap());
}
catch (IOException ex)
{
// Handle any errors
Log.e("CopyingFromTemp", ex.getMessage());
}
}
}).addOnFailureListener(new OnFailureListener()
{
#Override
public void onFailure(#NonNull Exception ex)
{
// Handle any errors
Log.e("FirebaseDownloadError", ex.getMessage());
}
});
}
catch(Exception ex)
{
Log.e("FirebaseDownloadError", ex.getMessage());
}
also i'm using standard security rules:
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
as Benjamin Wulfe hinted, i deleted the App's data on the phone and it worked, which means that some kind of Token data was stored on the phone and Anonymous Auth was getting an old session data.
so i added a sign out code before signInAnonymously
mAuth.signOut();
and done!
Thanks to you all for the help!
EDIT: I found another method which is better than signing out and in again (which lead to hundreds of unused anonymous users on the firebase console, and that because the app is not in production yet, would have been millions).
this is what i did:
if (mAuth.getCurrentUser() != null)
{
mAuth.getCurrentUser().reload();
}
else
{
mAuth.signInAnonymously()
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>()
{
#Override
public void onComplete(#NonNull Task<AuthResult> task)
{
Log.d("FirebaseAuth", "signInAnonymously:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful())
{
Log.w("FirebaseAuth", "signInAnonymously", task.getException());
Toast.makeText(MainActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
// ...
}
});
}
this just reloads current authenticated (anonymous) user.
The message " W/NetworkRequest: no auth token for request " is key for debugging this issue.
This log message means that Firebase Storage did not see any login in the current context. This includes anonymous logins. It means that no authorization was passed to the backend and the only way this will be allowed is if you set your rules to be completely open (public access) which is not recommended (see below).
//this sets completely open access to your data
allow read, write;
I would review the code you have for logging in and ensure it successfully completes before any storage operation is done.
If you are sure your auth code is correct, try resetting data on the device so that no saved state might be there to mess up the application's authorization information.
You might have to check your RULES for storage in firebase console. By default it is set to only permit to authenticated user only
like this
allow read, write: if request.auth != null;
Sometime its disconnect from firebase database,So Connect your app with firebase authentication in android studio through firebase assistance tool.