How to create Firebase Authentication claims? - firebase

I am developing an Android app that needs to authenticate with a server application. The server application is a Spring Boot app that uses Spring Security. The server app uses a custom authentication provider to set the Granted Authorities for the user.
This is the code that I am using to retrieve the idToken:
FirebaseToken firebaseToken = null;
try {
FirebaseApp app = FirebaseUtil.getFirebaseApp();
firebaseToken = FirebaseAuth.getInstance(app).verifyIdTokenAsync(authenticationToken.getIdToken()).get();
} catch ( InterruptedException | ExecutionException | CancellationException e ) {
throw new AuthenticationServiceException(e.getMessage());
}
return firebaseToken;
One I have the Firebase Token I can retrieve the claims like this:
Map<String, Object> claims = token.getClaims();
I can then iterate over the claims and create the authorities like so:
List<GrantedAuthority> authorities = Lists.newArrayList();
authorities.add(new SimpleGrantedAuthority("some_role"));
What I can't figure out is how to create the claims using the Firebase Console. Is this possible? I suspect that I need to use the Firebase Database but can't find exactly what I'm looking for in the firebase docs.

Custom claims for Firebase Authentication can currently only be created through the Firebase Admin SDKs. From the documentation on creating custom claims:
// Set admin privilege on the user corresponding to uid.
admin.auth().setCustomUserClaims(uid, {admin: true}).then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
It's not a bad idea to allow creating claims from the console too, so I'd recommend you file a feature request.

If you've been working with the Firebase Emulator Suite on localhost, you've probably noticed that you can directly edit the user in that console to add a custom claim like {"role":"admin"}. Just go to the Authentication tab, click the overflow menu for a specific user, select "Edit User", and set the custom claim in a text box.
You're probably here because you've discovered that the "Edit User" option doesn't appear in the production Firebase console, so you need to figure out how to do the same thing with code. Here are the steps I took to set a custom claim from Windows using Python, a service account, and the Firebase admin tools. Steps should be similar on Linux or OSX. This assumes you already have python and pip installed.
Download your credentials for a service user by starting here. You'll use the file you download in step 3.
https://console.firebase.google.com/project/<your_project>/settings/serviceaccounts/adminsdk
Install the firebase-admin tools for Python from a terminal (For Linux or OSX, you probably need to precede this command with sudo):
pip install firebase-admin
Open a python terminal by running python from the command-line and execute these lines of code:
import firebase_admin
from firebase_admin import credentials, auth
cred = credentials.Certificate("c:\\Users\\<path_to_my_credentials_file>.json")
default_app = firebase_admin.initialize_app(cred)
user = auth.get_user_by_email('<user_email_address>')
auth.set_custom_user_claims(user.uid, {'role': 'admin'})
# Verify the change worked:
user = auth.get_user_by_email('<user_email_address>')
print(user.custom_claims)

You can also easily do it with Python with:
auth.set_custom_user_claims(user_id, {"admin":True}, app=None)

Related

Azure AD - Insufficient privileges to perform requested operation by the application '00000003-0000-0000-c000-000000000000'

I work on a task that should invite users and add them in my azure active directory list. Before being able to access my app, the invited user should verify through email. This is the code I use:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(_config["AzureAd:ClientId"])
.WithTenantId(_config["AzureAd:TenantId"])
.WithClientSecret(_config["AzureAd:ClientSecret"])
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var invitation = new Invitation
{
InvitedUserEmailAddress = "mail#hotmail.com",
InviteRedirectUrl = "https://url.com/",
SendInvitationMessage = true
await graphClient.Invitations
.Request()
.AddAsync(invitation);
I found this snippet somewhere on the internet and judging by the comments, it seems to work. However, when I run my app and call this functionality, I get an error that says
Code: Unauthorized Message: Insufficient privileges to perform requested operation by the application '00000003-0000-0000-c000-000000000000'. ControllerName=MSGraphInviteAPI, ActionName=CreateInvite, URL absolute path=/...
In API permissions, I have a User.Invite.All permission under Microsoft Graph. Besides this I have User.Read as well but I don't think it's relevant for this at the moment. Has some of you stumbled upon an error like this and managed to successfully solve it? If so, would you be kind to share the solution?
You are using client_credentials flow. Which means it is not a User who is performing this task, but rather service credentials. You would need to provide Application permissions, rather than what you have set - Delegated Permissions.
If you don't see Application Permissions, its because you created an Azure AD B2C Application Registration. Rather, create the App Reg with the first option Accounts in this organizational directory only (Contoso only - Single tenant) .
These are the docs you need:
AAD Client Cred flow
MS Graph API daemon app
This is correct method for AAD and AAD B2C tenant today.

How can I allow my Unity app access to a Firebase realtime Database without user accounts and without public access?

I am using the Firebase Unity SDK (5.4.3). I need the app to access a Realtime Database. Everything works fine when I have access configured to public, but I need to secure the database so it can only be read/modified through the app.
I followed the instructions here: https://firebase.google.com/docs/database/unity/start
for allowing the Editor to "configure the SDK to use a service account to run in the Unity Editor." This allows the Unity editor to access the database, but this does not work on device. There are instructions for authenticating users, but I do not want any sort of log in in the app.
In short, how can I allow access through the app but disallow access outside of the app. Can I use a service account on device and how do I configure that?
Thank you.
As said by Doug Stevenson it is not possible; you either have a public login or a restricted one with authentication.
However I would Simply have one dedicated user like myUnityAppUser with a password like e.g. 123456 somewhere defined in a script or maybe an additional encryption file somewhere.
Then do the login automatically without user interaction -> send userName+password. The password could still be encrypted etc but this can all be handled by the App itself without you actively doing the login Everytime
Than you make a usual login like e.g. (Source)
public void Login(string email, string password)
{
auth.SignInWithEmailAndPasswordAsync(email, password).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithEmailAndPasswordAsync canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithEmailAndPasswordAsync error: " + task.Exception);
if (task.Exception.InnerExceptions.Count > 0)
UpdateErrorMessage(task.Exception.InnerExceptions[0].Message);
return;
}
FirebaseUser user = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
user.DisplayName, user.UserId);
SceneManager.LoadScene("LoginResults");
});
}
Now somewhere in your app you simply call
Login ("myUnityAppUset#some.email", "123456");
with the somehow somewhere "hardcoded" (maybe encrypted?) credentials.
Note that it is still possible that someone decompiles the app and can cheat again.
This is not possible. If you want to restrict who or what can access your Realtime Database (and Cloud Storage, and Firestore), you will need to use Firebase Authentication, and write security rules that lock down access to only users who have logged into your app/game.
Without Firebase Authentication in use, the only access to your database will essentially be public - by anyone who knows the name of your project. Anyone can find out the name of your project simply by reverse engineering your app/game and pulling the configuration information out of it. That configuration information is not private - it is also essentially public information.

SigningError with Firebase getSignedUrl()

I'm trying to use file.getSignedUrl() to get the download URL from Firebase Storage via Google Cloud Functions (Nodejs). I'm getting this error in the Cloud Functions console:
{ SigningError: A Forbidden error was returned while attempting to retrieve an access token for the Compute Engine built-in service account. This may be because the Compute Engine instance does not have the correct permission scopes specified. Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/myapp-cd94d/serviceAccounts/myapp-cd94d#appspot.gserviceaccount.com.
at SigningError (/user_code/node_modules/#google-cloud/storage/build/src/file.js:58:9)
at authClient.sign.then.catch.err (/user_code/node_modules/#google-cloud/storage/build/src/file.js:1019:22)
at process._tickDomainCallback (internal/process/next_tick.js:135:7) name: 'SigningError' }
I copied the code from the Add the Firebase Admin SDK to Your Server documentation. I have my serviceAccountKey.json in my functions folder. firebase deploy isn't given me the error
Error parsing triggers: Cannot find module 'serviceAccountKey.json'
so I must have the right path to my serviceAccountKey.json. I even generated a new private key, that didn't fix the problem. I have firebase-admin 6.1.0 and firebase-tools 6.1.0. Here's the relevant parts of my code:
const admin = require('firebase-admin');
var serviceAccount = require("./myapp-cd94d-firebase-adminsdk-1234x-sEcReT.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://myapp-cd94d.firebaseio.com"
});
...
const config = {
action: 'read',
expires: '03-17-2025'
};
file.getSignedUrl(config).then(function(data) {
const url = data[0];
console.log(url);
})
.catch(function(error) {
console.error(error);
})
I saw that Doug Stevenson's answer has different code but it appears to be equivalent to the code in the documentation.
The answer has to do with Cloud Identity and Access Management. First, go to your Google Cloud Platform IAM & admin page. You'll see various service accounts. Look for the service account that looks like myapp-cd99d#appspot.gserviceaccount.com. It should say App Engine default service account in the Name column. (If an error message referenced a different service account, find that service account.)
In the Role column, you may or not see some roles. If you're getting a SigningError message, the Role column is missing the role Service Account Token Creator. Check the checkbox to the left of myapp-cd99d#appspot.gserviceaccount.com to select the service account, and then click the pencil to the right to edit it. In the next screen, click +ADD ANOTHER ROLE. Scroll down to Service Accounts, select Service Account Token Creator, and save. Now you should see Service Account Token Creator in the Roles column for App Engine default service account. Now you have permission to create signed tokens.
Next, repeat these steps and add a role for Storage Object Creator. This will allow you to run getSignedURL().
You could save alternatively assign Service Account Admin and Storage Admin, which include the Service Account Token Creator and Storage Object Creator roles respectively, plus other roles.
Now, if you instead got a SingingError message, it might be because you're warbling Bruce Springsteen's "Glory Days" out of tune. :-)
In my case I had enable Identity and Access Management (IAM), the url is the following one:
https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project="YOUR
PROJECT NAME"

Firebase Dashboard set email verified

I created a user via the console and inside my client (iOS App) I require the user to have their email verified before they can use it.
However the test user does not have a valid email (it does not exist).
The email is testing#project-name.com.
Is there any way I can set the account as "verified email"? I don't find anything in the console, is it possible to set this status on a user (maybe in a onCreate auth trigger?)?
Thank you
There is no option in the Firebase Console to set the emailVerified state of a user. So you'll have to do it in code.
The easiest way to do this is through the Firebase Admin SDK, which is available for Node.js, Java, Python and Go. In Node.js it's as simple as:
admin.auth().updateUser(uid, { emailVerified: true })
You can find the user's UID in the Firebase console.
adding for laravel-firebase SDK:
app('firebase.auth')->updateUser($uid, [ 'emailVerified' => true ]);

How to share Authentication token between app in Firebase 3.0?

In older version of Firebase we can obtain authenticated token for sharing between our app, extensions.
But when upgrade to Firebase 3.0, that function doesn't work anymore.
I've use getTokenWithCompletion: on FIRUser, then in my App Extension I call signInWithCustomToken:completion: with token i just obtained. But Firebase return an error with messgage:
Error Domain=FIRAuthErrorDomain Code=17000 "The custom token format is
incorrect. Please check the documentation." UserInfo=0x799b6010
{error_name=ERROR_INVALID_CUSTOM_TOKEN, NSLocalizedDescription=The
custom token format is incorrect. Please check the documentation.}
How to get authenticate token from FIRUser to re-authenticate it in my App Extension?
signInWithCustomToken is meant to be used with your own tokens minted on your own server (read more here).
The best way for you to bootstrap a session between different components of your application will be to, at sign-in time, share the user's credential and perform the bootstraping across all your components.
For example, if you are using Facebook login, when your retrieve the facebook access token, you would have to share it from your app to your extension, and then call signInWithCredential with the same token in both your main app and extension.
There is currently no way to sign in a user into a Firebase app with the v3.X SDKs from another Firebase app instance.
I had the same problem this morning when upgrading to the latest version of Firebase on Android.
To fix the problem I had to update the Firebase Server SDK to version 3.0+
This is a Java backend implementation, but the same applies for NodeJS as well.
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-server-sdk</artifactId>
<version>[3.0.0,)</version>
</dependency>
In the new Firebase Server SDK you have to initialize your Firebase app first using a generated JSON file.(Found under permissions in your new Firebase console) Then you can generate the JWT token.
FirebaseOptions options = new FirebaseOptions.Builder()
.setServiceAccount(new FileInputStream("path/to/json/file.json"))
.setDatabaseUrl("https://myapp.firebaseio.com/")
.build();
FirebaseApp.initializeApp(options);
String token = FirebaseAuth.getInstance().createCustomToken("userID");
A token generated this way worked allowed me to use the new signInWithCustomToken() method.
You can read more here: https://firebase.google.com/docs/auth/server
Hope this helps
I had the same error as the OP. I am assuming that you are using a service account email and private key during token generation on your server. The token my server generated was successful on http://jsfiddle.net/firebase/XDXu5/. However, my app would give the same error.
In the end, it turned out that I was signing it using "HS256" (which is what I was using in my existing token generation - pre firebase 3.0). When I changed it to "RS256" instead, the token generated works in the app.
Hope that helps.

Resources