Most apps that you build with Firebase’s backend services, such as Realtime Database, Cloud Firestore, and Cloud Storage, need some way to sign users in: among other things, this lets you provide a consistent experience across sessions and devices, and lets you set user-specific permissions. Firebase Authentication helps you meet this requirement by providing libraries and services that you can use to quickly build a new sign-in system for your app.
But what if your organization already uses a service such as Okta to handle user identity? With Firebase Custom Authentication, you can use any user identity service (including Okta) to authenticate with Firebase, and this post will show you how.
You’ll learn how to build a Firebase and Okta integration, which will have two components:
By the way, this approach can also be used with some modification for other identity services, such as Auth0, Azure Active Directory, or your own custom system.
Ready to get started? Great! But, before you write any code, you’ll need to set up your Okta and Firebase projects.
First, set up an Okta project on the Okta Developer site:
Set the Base URIs and Login redirect URIs to the location where you plan to host your web frontend (http://localhost:5000 if you’re using the Firebase Hosting emulator) and enable the Authorization Code grant type.
http://localhost:5000
When you’re done, take note of the app's Client ID for later.
Then, set up a Firebase project in the Firebase console:
If you plan to eventually host your web app with Firebase, you can automatically set up Firebase Hosting and simplify configuration by enabling Also set up Firebase Hosting for this app.
Finally, if you plan to deploy your token exchange endpoint as a Cloud Function:
Now that your projects are set up, you’ll write the crucial piece: the token exchange endpoint.
The job of the token exchange endpoint is to take a user’s Okta access token and, if it’s valid, produce a Firebase custom authentication token that represents the same user.
This endpoint needs to be able to verify the authenticity of the Okta access token. To accomplish this, use the Express.js middleware provided in Okta’s developer documentation (reproduced below, with minor modifications):
const OKTA_ORG_URL = // Your Okta org URL const OktaJwtVerifier = require('@okta/jwt-verifier'); const oktaJwtVerifier = new OktaJwtVerifier({ issuer: `${OKTA_ORG_URL}/oauth2/default` }); // Middleware to authenticate requests with an Okta access token. const oktaAuth = async (req, res, next) => { const authHeader = req.headers.authorization || ''; const match = authHeader.match(/Bearer (.+)/); if (!match) { res.status(401); return next('Unauthorized'); } const accessToken = match[1]; try { const jwt = await oktaJwtVerifier.verifyAccessToken( accessToken, 'api://default'); req.jwt = jwt; return next(); // Pass the request on to the main route. } catch (err) { console.log(err.message); res.status(401); return next('Unauthorized'); } }
Any endpoint protected by this middleware will require a valid Okta access token in the Authorization header. If the token is valid, it will insert the decoded token into the request before passing the request along by calling next().
Authorization
next()
Now, you can write the token exchange endpoint:
const express = require('express'); const app = express(); const cors = require('cors')({origin: 'https://YOUR_DOMAIN'}); const firebaseAdmin = require('firebase-admin'); const firebaseApp = firebaseAdmin.initializeApp(); // Get a Firebase custom auth token for the authenticated Okta user. // This endpoint uses the `oktaAuth` middleware defined above to // ensure requests have a valid Okta access token. app.get('/firebaseCustomToken', [cors, oktaAuth], async (req, res) => { const oktaUid = req.jwt.claims.uid; try { const firebaseToken = await firebaseApp.auth().createCustomToken(oktaUid); res.send(firebaseToken); } catch (err) { console.log(err.message); res.status(500).send('Error minting token.'); } });
This endpoint uses the Firebase Admin SDK to mint a Firebase custom authentication token using the user’s Okta UID. When you sign a user in with this token for the first time (on the frontend), Firebase Authentication will add a user record with the same UID to your project.
This process of using an Okta access token to acquire a Firebase custom token is the key idea behind integrating Okta and Firebase. But, let’s go one step further and write a simple web frontend to demonstrate the use of the endpoint.
The demo frontend is a plain HTML and JavaScript web app that uses the Firebase Authentication Web SDK and Okta’s sign-in widget library.
Start with two containers: one for authenticated user content and one for Okta’s sign-in widget:
<div id="authenticated-user-content" hidden> <h2>Authenticated with Firebase</h2> <p id="user-info"></p> <button onclick="firebase.auth().signOut();">Sign out</button> </div> <div id="signin-widget" hidden></div>
Set up a Firebase authentication state listener that shows some user profile information to signed-in users and Okta’s sign-in widget to signed-out users:
const oktaSignIn = new OktaSignIn({ baseUrl: OKTA_ORG_URL, redirectUri: window.location.url, authParams: { display: 'page', }, el: '#signin-widget', }); firebase.auth().onAuthStateChanged((user) => { if (user) { // User is signed in. Display some user profile information. document.getElementById('user-info').innerHTML = `Hi, ${user.displayName}! Your email address is ${user.email} and your UID is ${user.uid}.`; document.getElementById('authenticated-user-content').hidden = false; document.getElementById('signin-widget').hidden = true; } else { // User is signed out. Display the Okta sign-in widget. oktaSignIn.showSignInToGetTokens({ clientId: OKTA_CLIENT_ID, redirectUri: window.location.url, getAccessToken: true, getIdToken: true, scope: 'openid profile email', }); document.getElementById('authenticated-user-content').hidden = true; document.getElementById('signin-widget').hidden = false; } });
When a user signs in with Okta’s widget, their browser briefly redirects to Okta’s authorization server, and then, assuming the user signed in successfully, redirects back to your app with the response.
Use Okta’s sign-in library to get the Okta access token from the response and use the access token to get a Firebase custom token from your token exchange endpoint:
if (oktaSignIn.hasTokensInUrl()) { // Get the access token from Okta. const oktaTokenResponse = await oktaSignIn.authClient.token.parseFromUrl(); const accessToken = oktaTokenResponse.tokens.accessToken.value; // Use the access token to call the firebaseCustomToken endpoint. const firebaseTokenResponse = await fetch(CUSTOM_TOKEN_ENDPOINT, { headers: { 'Authorization': `Bearer ${accessToken}`, } }); const firebaseToken = await firebaseTokenResponse.text(); // (Continued below.) }
And finally, authenticate with Firebase using the custom token:
// (Continued from above.) try { await firebase.auth().signInWithCustomToken(firebaseToken); } catch (err) { console.error('Error signing in with custom token.'); }
When the call to signInWithCustomToken() completes, the auth state listener will detect the change and display the user’s profile information.
signInWithCustomToken()
At this point, the user is authenticated with Firebase and you can use any of Firebase’s authentication-enabled services, such as Realtime Database, Cloud Firestore, and Cloud Storage. See the Security Rules documentation for more information on granting resource access to authenticated users.
For the complete demo app and backend that the code snippets above came from, see the Authenticate with Firebase using Okta sample on GitHub.
We have great news for web developers that use Firebase Cloud Messaging to send notifications to clients! The FCM v1 REST API has integrated fully with the Web Notifications API. This integration allows you to set icons, images, actions and more for your Web notifications from your server! Better yet, as the Web Notifications API continues to grow and change, these options will be immediately available to you. You won't have to wait for an update to FCM to support them!
Below is a sample payload you can send to your web clients on Push API supported browsers. This notification would be useful for a web app that supports image posting. It can encourage users to engage with the app.
{ "message": { "webpush": { "notification": { "title": "Fish Photos 🐟", "body": "Thanks for signing up for Fish Photos! You now will receive fun daily photos of fish!", "icon": "firebase-logo.png", "image": "guppies.jpg", "data": { "notificationType": "fishPhoto", "photoId": "123456" }, "click_action": "https://example.com/fish_photos", "actions": [ { "title": "Like", "action": "like", "icon": "icons/heart.png" }, { "title": "Unsubscribe", "action": "unsubscribe", "icon": "icons/cross.png" } ] } }, "token": "<APP_INSTANCE_REGISTRATION_TOKEN>" } }
Notice that you are able to set new parameters, such as actions, which gives the user different ways to interact with the notification. In the example below, users have the option to choose from actions to like the photo or to unsubscribe.
To handle action clicks in your app, you need to add an event listener in the default firebase-messaging-sw.js file (or your custom service worker). If an action button was clicked, event.action will contain the string that identifies the clicked action. Here's how to handle the "like" and "unsubscribe" events on the client:
like
unsubscribe
// Retrieve an instance of Firebase Messaging so that it can handle background messages. const messaging = firebase.messaging(); // Add an event listener to handle notification clicks self.addEventListener('notificationclick', function(event) { if (event.action === 'like') { // Like button was clicked const photoId = event.notification.data.photoId; like(photoId); } else if (event.action === 'unsubscribe') { // Unsubscribe button was clicked const notificationType = event.notification.data.notificationType; unsubscribe(notificationType); } event.notification.close(); });
The SDK will still handle regular notification clicks and redirect the user to your click_action link if provided. To see more on how to handle click actions on the client, check out the guide.
Since different browsers support different parameters in different platforms, it's important to check out the browser compatibility documentation to ensure your notifications work as intended. Want to learn more about what the Send API can do? Check out the FCM Send API documentation and the Web Notifications API documentation. If you're using the FCM Send API and you incorporate the Web Notifications API in a cool way, then let us know! Find Firebase on Twitter at @Firebase, and Facebook and Google+ by searching "Firebase".
When I think back 10 years or so when I was writing code for a monolithic codebase written in C++, one of the worst memories I have of the time was waiting for a build to complete just to run a couple lines of new code. The pain was so common among engineers that it bore the creation of one of the all-time most popular XKCD comics:
My office at the time didn't have swords to play with, so waiting for a build to complete was really boring.
The pain is similar if I had to deploy my Cloud Functions code every time I want test a function. Fortunately, the Firebase CLI now has a local emulator that works for both HTTPS and other types of functions. This lets me run the functions on my development machine by emulating the events that would trigger it. You can read more about that in the documentation.
One of the emulator's handy features is the fact that it will reload any JavaScript files that change over time. So, if you modify the code of a function, you can test it right away in the emulator without having to restart it:
It's fast and easy, and keeps you "in the zone".
However, things become less easy when you switch from JavaScript to TypeScript. Running TypeScript on Cloud Functions requires a compilation phase to convert the TypeScript into JavaScript so that it can be executed by the emulator. It adds a bit of a hassle:
npm run build
There's an easier way to do this that doesn't involve running a command every time. The TypeScript compiler (tsc) has a special "watch mode" that lets it pick up changes to your TypeScript and automatically compile them. You can run it from the command line by changing to your functions folder and running this command:
tsc
./node_modules/.bin/tsc --watch
With this running alongside the local emulator, which automatically picks up changes to the compiled JavaScript, your development workflow goes back to this:
And, if you're using VSCode, you can even have it directly run tsc in watch mode. You can start it up with Tasks -> Run Build Task..., then choosing "tsc: watch".
It will fire up a command shell right in the IDE, run tsc --watch, and show you the output. You'll see messages like this, along with any compilation errors, any time a TypeScript file changes:
tsc --watch
2:38:38 PM - File change detected. Starting incremental compilation... 2:38:38 PM - Compilation complete. Watching for file changes.
Now, bear in mind that tsc in watch mode won't run TSLint against your code, so be sure to set that up in VSCode so you can always see its recommendations, as I showed in a prior blog post. If you're not linting your code, seriously consider setting that up, because you'll catch potential mistakes that could be costly in production.
Are you looking for something new to learn this year? Then let me suggest TypeScript for development with Cloud Functions!
Not long ago, the Cloud Functions for Firebase team released an update to the Firebase CLI that makes it easy for you to write your functions in TypeScript, rather than JavaScript. The Firebase team encourages you to consider switching to TypeScript, but I can imagine you might be reluctant to learn a new language, especially if you're already comfortable with JavaScript. The great news is that TypeScript offers you a bunch of benefits that are easy to start using today.
The language itself offers a bunch of features that make your code easier to read and write, and less error-prone:
There's also async/await from ECMAScript 2017, which helps you write asynchronous code more easily. The primary challenge with asynchronous code is management of promises, which is crucial to get right when writing Cloud Functions, but difficult to master. TypeScript makes that much easier.
But what I really want to dive into here is an excellent tool called TSLint that can check your TypeScript code for potential problems before you deploy to Cloud Functions. The Firebase CLI will prompt you to configure TSLint when you initialize Functions in a project using TypeScript, and we strongly recommend that you opt into it.
When you opt into TypeScript and TSLint in your Firebase project structure, the Firebase CLI will add and modify some project files when you run firebase init. First let's take a look at functions/package.json. In there, you'll see the following key:
firebase init
functions/package.json
"devDependencies": { "tslint": "^5.8.0", "typescript": "^2.6.2" },
This is where node pulls in TypeScript and TSLint for development. Notice that there are "devDependencies" that are separate from the normal "dependencies" that you use in your function code. devDependencies are only stored on your machine, and are made available as tools for development. They are not deployed with your code. Also in that file, notice there are two script definitions:
"scripts": { "lint": "./node_modules/.bin/tslint -p tslint.json", "build": "./node_modules/.bin/tsc", ... }
These give you the ability to run npm run lint and npm run build on the command line from your functions directory. The first check your code with TSLint, and the second will build it with the TypeScript compiler.
npm run lint
The next file to look at is firebase.json. This now has a predeploy hook that runs TSLint against your code, so if it has an error, the deploy will fail:
firebase.json
{ "functions": { "predeploy": [ "npm --prefix $RESOURCE_DIR run lint", "npm --prefix $RESOURCE_DIR run build" ] } }
The next file is functions/tsconfig.json. This contains the configuration for the TypeScript compiler.
functions/tsconfig.json
{ "compilerOptions": { "lib": ["es6"], "module": "commonjs", "noImplicitReturns": true, "outDir": "lib", "sourceMap": true, "target": "es6" }, "compileOnSave": true, "include": [ "src" ] }
I won't cover all the settings here, but it's important to note that the compiler will look for source TypeScript files under functions/src, and compile them to JavaScript into functions/lib, with ECMAScript 6 compatibility, as required by Cloud Functions. Cloud Functions currently runs Node 6, which means it natively understand ES6 code.
functions/src
functions/lib
Lastly, take a brief look at functions/tslint.json. This lists all the rules that are observed when TSLint checks your code. You can add new rules here, or remove the rules you don't want. I suggest leaving everything there, as the list of rules was curated by the Firebase team to help with writing functions. The "strict errors" whose values are set to true will cause compile time errors if violated. The warnings below that will just complain about possible issues, and it's the team's opinion that you should look into resolving them.
functions/tslint.json
true
Alrightey. Take a look at this function that populates a property called createdAt when a user node is created in Realtime Database. Do you see what's wrong here?
createdAt
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(event => { event.data.ref.update({ createdAt: Date.now() }) })
TSLint sees the issue, and its one of the most common mistakes made when writing functions. If you run npm run build on this code, you'll see this error in its output:
Promises must be handled appropriately
This error is triggered by the rule no-floating-promises. TSLint sees that event.data.ref.update returns a promise, but nothing is being done with it. The correct way to deal with promises for database triggers is to return it:
event.data.ref.update
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(event => { return event.data.ref.update({ createdAt: Date.now() }) })
If you're using async/await, you can also declare the function async and use await to return it:
async
await
export const onUserCreate = functions.database.ref('/users/{uid}').onCreate(async event => { await event.data.ref.update({ createdAt: Date.now() }) })
Proper handling of promises is an absolute requirement when dealing with Cloud Functions, and TSLint will point out where you're not doing so.
I do a lot of Android development, and I'm accustomed to Android Studio linting my code as I type it. This is valuable because I get instant feedback about where things could go wrong. On the Firebase team, a bunch of us use VSCode for editing TypeScript, and it will use TSLint to give you instant feedback. The TSLint extension is easy to install.
Go to View -> Extensions. Type "TSLint" into the search box on the left. Find the TSLint extension, and click the Install button. After it's installed, click the Reload button, and now your TypeScript will be marked with possible mistakes.
Here's an HTTPS function that fetches a user document from Firestore using the Admin SDK. It looks OK and works OK:
export const getUser = functions.https.onRequest((req, res) => { var uid = req.params.uid const doc = admin.firestore().doc(`users/${uid}`) doc.get().then(doc => { res.send(doc.data()) }).catch(error => { res.status(500).send(error) }) })
But when viewed in VSCode with TSLint markers, you'll see that it violates some best practices:
Notice the squiggly lines under var, uid, and doc. If you hover the mouse over the marked code, you'll see a message from TSLint:
var
uid
doc
That's TSLint using the prefer-const rule to tell you that it's better to use const instead of var to declare values that don't change. It's good for the readability of your code, and also prevents someone from making an accidental change to it later on.
const
The squiggly line under doc is TSLint using the no-shadowed-variable rule to point out that the doc parameter in the function passed to then() (a DeltaDocumentSnapshot object) is masking the doc constant in the outer scope (a DocumentReference object), making the latter completely unavailable for use. While this is not really a bug here, it can lead to confusion about which doc instance is being referred to at any given moment. Renaming either instance resolves the issue.
then()
Here's a lint-free version of the same function:
export const getUser = functions.https.onRequest((req, res) => { const uid = req.params.uid const doc = admin.firestore().doc(`users/${uid}`) doc.get().then(snapshot => { res.send(snapshot.data()) }).catch(error => { res.status(500).send(error) }) })
This makes TSLint happy, which should make you happy, because you're writing better code!
If you haven't started learning TypeScript yet, 2018 is a fine time to begin. It's easy to get started, because TypeScript is a strict superset of JavaScript, meaning that all your existing JavaScript code is already valid TypeScript. So you can simply rename your .js files to .ts and drop them into functions/src. Then, you can start using TypeScript language features as you wish. Let us know how it goes and shout out to @Firebase on Twitter.
Many Firebase developers focus on their apps alone, usually for Android, iOS, or the Web (or all three!). If you're like me, and you spend most of your time on the client side, it can be a hassle to also deal with any backend components. Now that we have Cloud Functions for Firebase, it's super easy and fun to write and deploy backend code in JavaScript without having to manage servers. Personally, my comfort zone is with Android. So, to get acquainted with Cloud Functions, I spent some time brushing up on my JavaScript and learning node.js, which Cloud Functions uses to run backend code.
As part of that period of ramping up, I learned a new way of managing asynchronous work in JavaScript. If you've had experience working with JavaScript in the browser or with Cloud Functions, you may already be familiar with a class called Promise. A promise is a very common way to handle async work. The closest concept for Android developers is the Task API that you sometimes use when dealing with Firebase APIs.
A promise lets you respond to a unit of work that's executing asynchronously, such as a database write or a network request. Cloud Functions understand promises; if you want a function to stay alive during async work, you can do this by returning a promise from the function (except for HTTP/S triggers, which require a response sent to the client). When you return a promise, be sure to return a promise that is "resolved" after all asynchronous work performed in that function is fully complete. Many developers have found this video from the Firebase channel on YouTube very helpful for understanding how promises work with Cloud Functions:
In the video, Jen describes two main ways of dealing with promises. First, there's using the then() method on a promise to chain items of work in sequence, where the next item of work depends on the results from the previous item. Second, there's Promise.all() which creates a new promise that resolves after multiple items of work happening in parallel are all complete. Between these two mechanisms, you can manage most work that normally occurs in a function. When the work is complete, you return the final promise from the function so that the Cloud Functions environment knows when to clean up.
Promise.all()
For simple functions, this works out pretty easily. But many of you (including myself) have discovered that complicated functions can be really difficult to manage when there's a lot of work (with lots of promises) going on. One of the symptoms of mismanaged promises is seeing the following error in the function log in the Firebase console:
Error: read ECONNRESET
This error could mean a few different things. In some cases it could be a bug in a client library. In other cases, it's a symptom of a common mistake when dealing with promises.
If a function is terminated before its network connections are finished, that means those open connections could be forced to close, leaving an ECONNRESET in the log. Here's a situation when that can occur.
ECONNRESET
Imagine you want to start three items of async work, and the work is kicked off in a function called doSomeAsync(), which returns a promise that's resolved upon completion, and you return the promise from the third task like this:
doSomeAsync()
doSomeAsync(x) doSomeAsync(y) return doSomeAsync(z)
Returning a Promise from the function is good. But Cloud Functions needs a promise that resolves when all of the work is fully complete. If we don't have a guarantee that the last item of work will always finish after the other two, then Cloud Functions may clean up the function prematurely, causing problems. Instead, we need to combine all of the promises from all of the work into a new promise that resolves only after all of the others resolve, like this:
const promise_x = doSomeAsync(x) const promise_y = doSomeAsync(y) const promise_z = doSomeAsync(z) return Promise.all([promise_x, promise_y, promise_z])
If you're not tracking every single item of asynchronous work that you kick off in a function, you could see the dreaded ECONNRESET error in your log, and things may not work the way you expect. It really can take some diligence to keep all your promises!
If you want to see an example of a more complicated function, I have an open source tic-tac-toe web game that I wrote for a session I gave at Google I/O '17 that talks about how it was built entirely with Firebase.
For more tutorials and content about Cloud Functions, and other Firebase products, be sure to check out the Firebase channel on YouTube and the Firebase Blog.
We are pleased to announce that we are taking our first steps towards open sourcing our client libraries. By making our SDKs open, we're aiming to show our commitment to greater transparency and to building a stronger developer community. To help further that goal, we'll be using GitHub as a core part of our own toolchain to enable all of you to contribute as well. As you find issues in our code, from inconsistent style to bugs, you can file issues through the standard GitHub issue tracker. You can also find our project in the Google Open Source directory. We're really looking forward to your pull requests!
We're starting by open sourcing several products in our iOS, JavaScript, Java, Node.js and Python SDKs. We'll be looking at open sourcing our Android SDK as well. The SDKs are being licensed under Apache 2.0, the same flexible license as existing Firebase open source projects like FirebaseUI.
Let's take a look at each repo:
https://github.com/firebase/firebase-ios-sdk
With the launch of the Firebase iOS 4.0 SDKs we have made several improvements to the developer experience, such as more idiomatic API names for our Swift users. By open sourcing our iOS SDKs we hope to provide an additional avenue for you to give us feedback on such features. For this first release we are open sourcing our Realtime Database, Auth, Cloud Storage and Cloud Messaging (FCM) SDKs, but going forward we intend to release more.
Because we aren't yet able to open source some of the Firebase components, the full product build process isn't available. While you can use this repo to build a FirebaseDev pod, our libraries distributed through CocoaPods will continue to be static frameworks for the time being. We are continually looking for ways to improve the developer experience for developers, however you integrate.
Our GitHub README provides more details on how you build, test and contribute to our iOS SDKs.
https://github.com/firebase/firebase-js-sdk
We are excited to announce that we are open sourcing our Realtime Database, Cloud Storage and Cloud Messaging (FCM) SDKs for JavaScript. We'll have a couple of improvements hot on the heels of this initial release, including open sourcing Firebase Authentication. We are also in the process of releasing the source maps for our components, which we expect would really improve the debuggability of your app.
Our GitHub repo includes instructions on how you can build, test and contribute.
Node.js: https://github.com/firebase/firebase-admin-node
Java: https://github.com/firebase/firebase-admin-java
Python: https://github.com/firebase/firebase-admin-python
We are happy to announce that all three of our Admin SDKs for accessing Firebase on privileged environments are now fully open source, including our recently-launched Python SDK. While we continue to explore supporting more languages, we encourage you to use our source as inspiration to enable Firebase for your environment. (And if you do, we'd love to hear about it!)
We're really excited to see what you do with the updated SDKs - as always reach out to us with feedback or questions in the Firebase-Talk Google Group, on Stack Overflow, via the Firebase Support team, or now on GitHub for SDK issues and pull requests! And to read about the other improvements to Firebase that launched at Google I/O, head over to the Firebase blog.