9
votes

I'm building an enterprise app using Microsoft Graph to sign in. After a successful signing i want to use the token to be sent to authenticate to Firebase Auth (so i can secure the access to the database).

The token recieved after a successful sign in cannot be used directly to Firebase.

On the Firebase custom Auth instructions page it says:

Get your project's server keys:

  1. Go to the Service Accounts page in your project's settings.
  2. Click Generate New Private Key at the bottom of the Firebase Admin SDK section of the Service Accounts page.
  3. The new service account's public/private key pair is automatically saved on your computer. Copy this file to your authentication server.

The third point says that you need to enter the key to the authentication server. Is this possible using Microsoft Graph or Azure AD?

The key that Firebase gives you is a JSON file. I've checked Microsoft App registration portal which allows you to edit the apps Manifest, but with no luck.

The JSON file looks like this:

{
    "type": "service_account",
    "project_id": "APP_ID",
    "private_key_id": "KEY_ID_VALUE",
    "private_key": "-----BEGIN PRIVATE KEY----<KEY VALUE>-----END PRIVATE KEY-----\n",
    "client_email": "firebase-adminsdk-0ubvc@********.iam.gserviceaccount.com",
    "client_id": "XXXXXXXXXXXX",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-0ubvc%XXXXXXXX.iam.gserviceaccount.com"
}

I can't seem to find any github projects or stackoverflow threads that covers this issue.

How can you recieve custom tokens with MS Graph or Azure AD?

4
It seems like you are mixing concerns. I'm making some assumptions here since you have not described your architecture in detail. It sounds like you have user authentication with AAD and service authentication with Firebase. If that is the case then your application as a whole is being authorized to access Firebase, not individual users. The work of authorizing particular users for full/partial database access is then up to your application to handle.Nathan
Thanks Nathan! Your assumptions are correct. I'm using the AAD to authorize the users and Firebase for my services. My main concern is that i want a successful AAD-authentication to generate a new user in Firebase Auth. Although i don't want to allow anyone who's not authenticated with AAD to be able to do this. After they are authenticated, user rights are handled directly in Firebase. This is because the database is in Firebase and i want to be able to make requests to various microsoft services and then store information on Firebase.Giovanni Palusa
Couple more questions. When you say "Enterprise App", what kind of app do you mean? mobile app, web app? What language are you implementing the app in? Other than the mobile/web app you are building are you also building any supporting web services (APIs) to be used by this app?Nathan
This mobile app is both in Android/JAVA and in Swift. But sending a correctly generated token to firebase is clearly stated how to do in the Firebase documentation. But I need to find a way to authenticate through AAD and send the user credentials to create an account in Firebase, and mark this person as correctly authenticated. So it feels like this is pure backend between Firebase and AAD.Giovanni Palusa

4 Answers

6
votes

I have now fully worked this out. Through extensive research and a lot of help here on Stackoverflow i managed to solve this.

UPDATE

Database secrets are currently deprecated and use a legacy Firebase token generator. Now Firebase Admin import is good enough.

so, these are my findings:

1. You DO need to send your private keys to your firebase functions when minting a Firebase Token. In the Firebase console, you can extract the key and rename the file to service-account.json. This should be put in your Functions folder before performing your Firebase deploy

  1. In your index.js file, you can get your service file by entering this code:

    const admin = require('firebase-admin');
    
  2. Write the function for accepting information from the other Authentication service:

    // Create a Firebase token from any UID
    exports.createFirebaseToken = functions.https.onRequest((req, res) => {
    
      // The UID and other things we'll assign to the user.
      const uid = req.body.uid;
      const additionalClaims = {
        name: req.body.name,
        email: req.body.email
      };
    
      // Create or update the user account.
      const userCreationTask = admin.auth().updateUser(uid, additionalClaims).catch(error => {
    
        if (req.method === 'PUT') {
          res.status(403).send('Forbidden!');
        }
    
        if (req.method === 'GET') {
         res.status(403).send('Please use POST for this function');
        }
    
        // If user does not exists we create it.
        if (error.code === 'auth/user-not-found') {
          console.log(`Created user with UID:${uid}, Name: ${additionalClaims.name} and e-mail: ${additionalClaims.email}`);
          return admin.auth().createUser({
          uid: uid,
          displayName: additionalClaims.name,
          email: additionalClaims.email,
        });
            }
            throw error;
            console.log('Error!');
        });
    
    
        return Promise.all([userCreationTask]).then(() => {
          console.log('Function create token triggered');
          // Create a Firebase custom auth token.
          admin.auth().createCustomToken(uid, additionalClaims).then((token) => {
          console.log('Created Custom token for UID "', uid, '" Token:', token);
            res.status(200).send(token);
            return token
        });
      });
    });
    

It's very important to respond with a res.status since this will finish the task. A single return statement will not do this. A fully working sample from Firebase themself can be found on github

  1. You can now make a HTTP-request that could look something like this using Alamofire and swift

    Alamofire.request("https://us-central1-<YOUR DATABASE REFERENCE>.cloudfunctions.net/createFirebaseToken", 
    method: .post, parameters: parameters, encoding: JSONEncoding.default).
    responseString(completionHandler: { (token) in
        // Handle the result here
    })
    

    In this case, the Parameters is a regular JSON file that contains whatever you want to add to the users Firebase account.

  2. IMPORTANT Anyone with your cloudfunctions URL can trigger this token minting. So make sure you add security measures to handle this. This is briefly mentioned in a Firecast video on youtube made by Firebase and has also been discussed in this thread in Stackoverflow

  3. When your client received the token you a custom auth sign in iOS or in Android as described in the documentation.

  4. You are now authenticated both towards Firebase and Microsoft

  5. I have also added an extra layer of security, by checking that the ID i got from Microsoft, is the same ID stored in the authenticated account from Firebase.

2
votes

I've created an example here for Firebase web and browser-based federated authentication. The authentication response from Azure AD is validated by a function, a custom firebase token is minted and sent to the front-end; which is sent to the Firebase Auth service.

Guide with more elaboration on the flow: https://medium.com/@alex.wauters/how-to-integrate-a-firebase-web-app-with-azure-active-directory-b5c0f01a0c24

1
votes

It's a lot simpler than you think. After you authenticate with Microsoft, and you get a Microsoft credential, you just need to get the associated Microsoft user ID for that credential. You can then mint a Firebase custom token and set the Firebase uid to that same Microsoft user ID. For example you can do it as follows with the Firebase Admin Node.js SDK:

admin.createCustomToken(msftUid, additionalUserClaims)
  .then(customToken => {
    // Return this to the client.
  })

After you send it back to the client, you then call signInWithCustomToken(customToken) which would sign in the same user with Firebase Auth. That user will have currentUser.uid equal to the Microsoft user ID.

0
votes

The issue is you need to have some piece of code of your own that reacts to the AAD IDP auth success and then generates the JWT to be used by Firebase. That JWT is generated using the private key supplied by firebase. You indicated that at least some of your code is in Java, which is good news since that is one the languages the Firebase Admin SDK supports. In theory I suppose you could do that token generation in your mobile app code, but I strongly suggest not doing so since that would require deploying your private key with your app, which would compromise it. You will need to stand up a web service that can handle these authentication requests from your app, and respond with the JWT generated by the Firebase Admin SDK. Once your mobile app has that JWT generated by your service, then it can use that to connect to Firebase as that user.