Most of the serverless platforms have their own forms of authentication, but it might not support the specific service that you are looking to use. Lets go through how we can build a react single page app, hosting on firebase, that talks to the unsplash service directly. It will be hosted on firebase stoage, and with a tiny bit of firebase functions to tie it together.
How oauth works
Here is the overall process:
There are 3 entities – the user’s browser, our server, and the third party service.
- On user action, such as pressing a Log in With Google button, a request is made to your server.
- A signed request is generated which includes the access levels that we wish to be granted. The client ID and client secret, which are provided by the service provider (under something like Create An App) are used to sign this request so that the server knows which app is requesting permission.
- The browser is redirected to the third party service with this request.
- The user interacts with the third party service, and grants us the permission.
- The browser is redirected to the “call back url”, which is generally needs to be white listed at the same place you got your client id and secrets.
- The authcode is then used by our server to request an access_token from the service. authcode are very short lived so prevent play back attacks since they are exposed to the browser. Access tokens are longer lived and aren’t generally exposed to the browser.
- The server (optionally) gets information for the account, for example name and avatar photos, to configure a local account.
- A logged in version of the site is returned to the user’s browser.
In this scenario:
- The user’s password is never seen by our server.
- The user doesn’t need to remember yet another password for your site.
- The access token is never seen by the user.
- Access Tokens can be revoked by the third-party service at any time.
- Access Tokens can be tied to a developer, so bad actors on the server level can be locked out.
- The third party service can grant fine grained control of what data they grant access to.
What do we need to implement
- A client ID from the third party service.
- A client secret from the third party service.
- An auth method to contruct the initial requests.
- An auth_callback method to process the final request.
- An auth callback url that is granted access by the third party service’s app configuration.
The first 2 are created at a third party service. For this example, I’m going to choose firebase to host our functions and website, and Unsplash since it’s a pretty fun third-party service that’s a bit off the beaten path and its not too hard to get an app registered.
Create the app on the third party service
Go ahead and follow that the unsplash link and register as a developer.
Then press the big
New Application button and verify that you are going to follow the rules. Give your application a name and a quick description, then create!
Note your Application Key and Secret. This could aslo be called your client id and secret. We’ll need those later. Set your callback url to
https://localhost:3000/auth_callback. Once we deploy to firebase we’ll need to go back with our actual URL, but this is what we’ll use fore testing.
We are going to use
create-react-app and then going to add firebase to it.
Now we setup our “server” on the FireBase console Create a new application. Select the web integration, and copy the example config into
src/firebaseConfig.js, something like below:
If you haven’t already, it’s time to install the
firebase-tools page. I’m using yarn here:
And then lets add firebase to our project:
This will first prompt you to login to firebase so your machine has development credentials. Then
init will configure your local project with the correct configuration.
- Select at least functions and hosting.
- Choose the application that you just created.
- Yes to ESLint.
- Yes to install dependencies.
buildas the hosting directory.
- Yes to single page app.
- No to overwriting
Firebase should now be configured. Lets do a quick test of the deploy.
And open your browser to the url that was
firebase deploy spit out at the end. If you see a blank screen, double check that in
firebase.json you have
build set as the
public directory in hosting.
Set the secrets for your firebase functions
We are going to store the oauth secrets inside of firebase to keep them seperate from your code. These are the application id and application secret that we got from unsplash above that you should have taken note off. Lets set those in firebase now (and be sure you enter in your own secrets!)
We can then pull these secrets back down locally into a firebase env file so that when we are testing out our firebase code it will behave like it will in production.
We don’t want these files to go into git so
$ echo functions/.runtimeconfig.json >> .gitignore
Setup firebase functions
Lets go into the
functions directory and add a few npm modules. One for the http, and the other for the oauth flow.
Now lets create a simple app that we can use to test out our install. Replace
firebase serve --only functions to start up the api locally. Be sure to check out the url that the proxy code is running on. In my case, it’s
http://localhost:5001/honey-b6642/us-central1/oauth. Once this is running you should be able to go to the url listed and see the client id from the configuration.
Setup the react app
We need to point our react code to our firebase functions, and we are going to put that information into
env files so that there is one place to swap them out later. These need to be prefaced with
REACT_APP_ in order to play well with the
create-react-app build process. Create a
.env.development file with your information in it
We also don’t want this file in source control:
Once we deploy the server to fire base, we can point this to our production instances. Also, we will create another
First lets add some small styling to that our eyes won’t hurt during development.
src/index.scss, and include the bootstrap sass files. This is a bit overkill at the moment, but it will set things up for easy customization going forward.
Be sure to update
src/index.js to point to the correct style sheet, change
Now we can build out a scaffolding for
Now start up the server using
yarn start, and press the
Connect button. You should now see the response from the firebase function running locally!
Implementing the OAuth Flow
Now that all the pieces are in place, it’s time to start implementing the oauth logic inside of the firebase functions.
First lets add a few libraries to the
$ cd functions $ yarn add simple-oauth2 randomstring
Also edit the local
functions/.runtimeconfig.json to include the callback url that we are going to pass to unsplash. Mine looks like:
Now lets write some code! Lets replace what we have in
functions/index.js with a method to create a authorization request to unplash and then redirect the user’s browser to it.
First we setup
simpleOauth. We are using the configuration id and secret that we got from unsplash, and the endpoints that are specified in the documentation for the
authorize path. When we get a
/auth request we use create an authorization request using that configuration and then redirect the browser to it.
We haven’t written a callback handler yet, but lets try it out. First go to unsplash and log out of your account to see the whole flow. Then go to http://localhost:3000 and press the auth button. You’ll be prompted to log in, and when you do you should see:
If you don’t, a couple of things to check
- Is your client id and secret being set correctly?
- Is your redirect_uri configured correctly?
- Are you passing the correct scope? (In this case,
OK, now it’s time to write the callback method!
When you press “Grant Access” on Unsplash, it will generate an authorization code and redirect the user’s browser back to this function. Here we use that code to get an access token from unsplash that we can then use to make requests on the user’s behalf.
Using the token
In the scenario we outlined at the top, the server would store the access_token and connect to the server with authenticated requests from there. In the case of firebase, we’d great something like an Anaonymous account, store the token there, and then proxy requests from the server to the third party service never exposing the access_token to the browser. This is a clean way to do this so that the access_token is never exposed.
But it makes it more complicated, so we are going to hack through it for demo purposes to just finish up the demo. The code that we are writing is going to connect to the unsplash api from the browser directly, so we’ll change our fireback function code to redirect back to our static app with an url that contains the access_token.
So, lets have the code redirect back to our react site with the query string as the parameter.
First lets add where we’d like it to go to the configuration in
Then lets change our function to redirect instead of spitting out json. So in
Now we need to update our react code so that it knows what to do with that access token. Lets first add a node module that understands how to parse query strings:
And replace the App class in
src/App.js with the following:
We need to create that
Unsplash component, so lets do that now inside of
src/Unsplash.js (and don’t forget to
import this at the top of
The first thing that we are doing is creating a utility function that uses the
fetch function and passes the
Bearer token, a/k/a
access_token to the api. In the
componentDidMount method of the
Unsplash class, we use that function to initiate the request. Once we get that data, we show the rather bare bones
Profile component that has a name and a picture in it.
Proof of concept: working locally!
Deploying everything to firebase
Now lets package this up to run on production. First we need to tell the react app where the firebase functions are. Go to the FireBase Console, select your project, and find the functions admin panel on the left side. See where the are deployed. Mine is
https://us-central1-honey-b6642.cloudfunctions.net/oauth, so in
.env.production lets add:
Now lets kick off a production build of the react app:
Now we need to setup the config for the firebase functions. Lets make sure that we have an entry for everything that is in our
functions/.runtimeconfig.json file. First set the static_site_url value to the url you are looking at, in my case
For the redirect_uri, use the same as the
REACT_APP_BASE_URL above but add
/callback. Mine is
Once that is done, lets push both the html/css/js code as well as the functions to firebase:
If you have the problem listed here, update
functions/package.json to downgrade
yarn in the
functions directory, and retry the firebase deploy.
Finally, we need to whitelist our callback URL inside of the Unsplash Application. Follow that link, select your application, and add that same callback url from above after the localhost one that should already be there.
Now lets test it out! Make sure that you have billing enabled in FireBase, otherwise you won’t be able to make external requests and you will get a host not found error.
The code is available on GitHub here: https://github.com/wschenk/unsplash_api_firebase
We’ve covered a lot of ground here!
- Overview of the OAuth protocol
- Hosting a static create-react-app site on firebase
- Building firebase functions and testing them locally
- Environment config with create-react-app in production and development
- Environment config with firebase functions, also in production and development
- A quick and easy way to pass the access_token back to our react app
- How to use the
window.fetchto get authenticated data from an API.