Cannot pass a read permission (email) to a request for publish authorization - firebase

I am trying to use Facebook login using Firebase authentication. I have followed whole documentation. Lastly whenever I click the login button it gives an error saying:
Cannot pass a read permission (email) to a request for publish authorization
The line on which it is showing error is:
LoginManager.getInstance().logInWithPublishPermissions(LoginActivity.this,Arrays.asList("email", "public_profile"));
Can someone explain me what I am doing wrong? I have looked other answers also but no help from there.

Use logInWithReadPermissions instead of logInWithPublishPermissions. The error message is very clear about that, you are trying to request read permissions with a function that is being used for publish permissions.

.getInstance().logInWithPublishPermissions is not Firebase Authentication. Signin via federated providers such as Facebook is available via the Firebase JavaScript (web) SDK. See Authenticate Using Facebook Login with JavaScript for details.
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});

Related

How can I solve create account problem with different providers?

I have a sign in with Google:example#gmail.com
then create an account with the same email:example#gmail.com
There is a problem with two different providers
Sign in with Google (same Gmail)
Sign in with Email (same Gmail)
How Can I handle these two (When I delete the google sign-in account from Firebase Console. I can create an account with that email) Otherwise I can't create an account with that email and also can't sign in.
I learning Firebase Auth with https://github.com/gladly-team/next-firebase-auth
If you first sign in with Google using "example#gmail.com", it means a user will be created using this particular email address. If you try to sign in with any other provider or with an email and password using the same email address, you'll get an error message that says that the user already exists. And it makes sense since you have already used that email for a user before.
There are two ways in which you can solve this problem. When you get such an error, you can check the provider used to create the account, and notify the user to use it. For example, if the user signs in with Google and tries to authenticate with email and password right after that, display a message to the user in which you should say that the user already exists, and should use the authentication provider which was selected to create the account in the first place, in this case, Google.
The second option would be to allow the user to have multiple accounts using the same email address with different authentication providers. This option can be enabled directly in the Firebase Console, in the Authentication section.
So it's up to you to decide which option works better for your project.
The simple Solution is to enable multiple account an email.
Or ----------------
You Link the account.
This is an example when there is a facebook account with a certain email
and you want to use that same email to sign in with Email and password or gmail, if those two emails are not linked different provider error will be thrown. check here for more
export function linkFaceBookAccount(authContext?: AuthContextType, notificationContext?: NotificationContextType, history?: History.History) {
const provider = new FacebookAuthProvider(); // create a provider
linkWithPopup(auth.currentUser as User, provider).then((result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
// const credential = FacebookAuthProvider.credentialFromResult(result);
// const token = credential?.accessToken;
// The signed-in user info.
const user = result.user;
saveUserToLocalStorage(user);
authContext?.loadUserToState(user);
notificationContext?.addNotification({
message: `This email's (${auth.currentUser?.email}) account has been successful linked with your facebook account`,
title: "Link successful",
notificationType: "SUCCESS",
positiveActionText: "continue",
positiveAction: () => {
history?.push("/")
}
})
}).catch((error) => {
const email = error.customData?.email;
const errorCode = error.code;
const duplicateAccount = errorCode === "auth/account-exists-with-different-credential";
notificationContext?.addNotification({
message: errorFirebase(error, email),
title: "Linking Error",
notificationType: "WARNING",
positiveActionText: duplicateAccount ? "Link" : "ok",
negativeActionText: duplicateAccount ? "cancel" : undefined,
code: errorCode,
positiveAction: () => {
if (duplicateAccount) {
duplicateAccountLinking(email, "FACEBOOK", history);
}
}
})
});}

Firebase Web Admin

First of all, I am using nodejs for the backend. I use firebase hosting and firebase functions to deploy an express() app.
What I am trying to achieve is to make an admin website, which is connected to Firebase. so I have a route /admin/ like this:
adminApp.get("/", (request, response) => {
return response.redirect("/admin/login");
});
Here I basically want to check if a current user is logged in - or not.
I know firebase supports client side authentication using:
firebase.auth().onAuthStateChanged(user => {
if (user) {
} else {
}
});
And using
function login() {
var userEmail = document.getElementById("email").value;
var userPass = document.getElementById("password").value;
firebase.auth().signInWithEmailAndPassword(userEmail, userPass).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
if (error) {
document.getElementById('loginError').innerHTML = `Error signing in to firebase`;
}
});
}
However image this case:
Someone (not an admin) is visiting /admin/some_secret_website/ which he obviously does not have access to.
If I rely on client side authentication, it first loads the entire website and the scripts and then notices - hey I am not authenticated, let me redirect to /login. By then however anyone knows the source code of an admin page.
I'd rather have something like:
adminApp.get("/admin/some_secret_website", (request, response) => {
if (request.user) {
// user is authenticated we can check if the user is an admin and give access to the admin page
}
});
I know that you can get the user's token and validate that token using the AdminSDK, but the token must be send by the client code, meaning the website was already loaded.
I came across Authorized HTTPS Endpoint by firebase, but it only allows a middleware when using a bearer token.
Does anybody know how I can maintain a server side user object to not even return admin html to the browser but only allow access to admins?
Like Doug indicated, the way your admin website/webapp would function with Firebase Cloud Functions (which is effectively a Nodejs server) is that you get the request, then use the headers token to authenticate them against Firebase Auth. See this answer for a code snippet on this.
In your case, I'm thinking you would create a custom claim for an "administrator" group and use that to determine whether to send a pug templated page as a response upon authentication. As far as Authorization, your db rules will determine what said user can CRUD.

Is email authentication using id token possible?

I'm creating a POC for an iOS application that uses extension. The extension has to know if a user is authenticated. Unfortunately a containing app and an extension are separate application which means I have to keep auth state somewhere.
I don't want to store user email and password but instead a token and use it to authenticate. However trying to authenticate with the issued token and providerId (Firebase) does seems to work.
Both apps under the same Firebase project.
Main Application (Firebase ios App1):
let userDefatuls = UserDefaults(suiteName: "group.test")
userDefatuls?.set(providerId!, forKey: "providerId")
userDefatuls?.set(value!, forKey: "token")
print("Saved value to user defaults \(value!)")
userDefatuls?.synchronize()
Extension Application (Firebase ios App2):
let userDefaults = UserDefaults(suiteName: "group.test")
let token = userDefaults?.string(forKey: "token")
let providerId = userDefaults?.string(forKey: "providerId")
print("What is the provider id \(providerId)")
let credential = OAuthProvider.credential(withProviderID: providerId!, accessToken: token!)
Auth.auth().signIn(with: credential) { (user, error) in
print("********* What is the user \(user) what is the error \(error)")
}
The above renders:
What is the user nil what is the error:
Optional(Error Domain=FIRAuthErrorDomain Code=17999 "An internal error has occurred, print and inspect the error details for more information." UserInfo={error_name=ERROR_INTERNAL_ERROR, NSLocalizedDescription=An internal error has occurred, print and inspect the error details for more information., NSUnderlyingError=0x600000447290 {Error Domain=FIRAuthInternalErrorDomain Code=3 "(null)" UserInfo={FIRAuthErrorUserInfoDeserializedResponseKey={
code = 400;
errors = (
{
domain = global;
message = "INVALID_PROVIDER_ID : Provider Id is not supported.";
reason = invalid;
}
);
message = "INVALID_PROVIDER_ID : Provider Id is not supported.";
}}}})
Do you know if the support and works? Is there something what I'm doing wrong?
You can't sign in with a Firebase ID token. What you can do is:
Create an endpoint that takes an ID token, verifies it:
https://firebase.google.com/docs/auth/admin/verify-id-tokens
and returns a custom token:
https://firebase.google.com/docs/auth/admin/create-custom-tokens
You then signInWithCustomToken in the extension.
However, this could open a vulnerability as if a short lived ID token is leaked, the attacker can exchange it for a permanent session via that endpoint.
You can only return the custom token if the auth_time on the ID token is recent. This ensure that a recently signed in user can sync their app to with the extension.

Handle denied email permission in Facebook

I am trying to build Login with Facebook API manually. I am using https://www.nuget.org/packages/Facebook/ & using following code in my ASP.NET MVC.
Basic idea is to ask users permission, access the users email, auto-register to my system.
Problem is when user un-check access to email & click on Ok on the facebook authentication popup. Next time when user clicks on "Login with Facebook" button, facebook authentication pop-up won't appear, as user has already allowed the access, and I don't get users email. The only way, facebook authentication box re-appear, is user revoke access to my app from his personal facebook account.
Is there another way, I can get the facebook authentication popup again? Or better way to do this?
var fb = new FacebookClient();
dynamic result = fb.Post("oauth/access_token", new
{
client_id = System.Configuration.ConfigurationManager.AppSettings["FacebookAppId"],
client_secret = System.Configuration.ConfigurationManager.AppSettings["FacebookAppSecret"],
redirect_uri = System.Configuration.ConfigurationManager.AppSettings["FacebookRedirectURL"],
code = code
});
var accessToken = result.access_token;
// Store the access token in the session
Session["AccessToken"] = accessToken;
// update the facebook client with the access token so
// we can make requests on behalf of the user
fb.AccessToken = accessToken;
// Get the user's information
dynamic me = fb.Get("me?fields=first_name,last_name,id,email");
if (!String.IsNullOrWhiteSpace(me.email))
{
string email = me.email;
// Register Or login user
}
else
{
// Handle declined email permissions
}

Using firebase facebook auth with facebook graph api

I am using firebase facebook simple login.
Is there any way I can use it conjunction with facebook js graph api?
Let say, calling FB.api('xxx', function(){}) ?
Facebook via Firebase Simple Login returns the Facebook access token as part of the payload. You could then use this directly with the Facebook Graph API and their JavaScript SDK:
var ref = new Firebase(URL);
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if (user) {
var facebookToken = user.accessToken; // <<-- here it is
}
});
// Note: Attach this to a click event to permit the pop-up to be shown
auth.login('facebook');
As you noted, Singly is another great approach that abstracts some of the effort of talking to Facebook, but shouldn't be necessary if your use case is fairly straightforward.
UPDATE
Firebase Simple Login now supports authenticating a user with an existing Facebook access token, meaning that you can easily use the Facebook JS SDK in combination with Firebase Simple Login without asking your users to authenticate twice.
For example, once you have a valid Facebook access token using the Facebook JS SDK:
var ref = new Firebase(...);
var auth = new FirebaseSimpleLogin(ref, function(error, user) { ... });
auth.login('facebook', { access_token: '<ACCESS_TOKEN>' });
See the access_token option at https://www.firebase.com/docs/security/simple-login-facebook.html for more information.

Resources