Securing Graphiql with firebase login

Tagging film content posted on 28th July 2019


In Firebase auth for graphql clients I covered how to use firebase client side auth to lock down your Apollo Graphql server. Developers and other admins are probably going to need to use the Graphiql tool too for ad hoc querying and troubleshooting. The problem is, you don't want to leave that publicly open, and yet the graphiql standard app doesn't have any auth built in. 

Luckily, you can put a simple wrapper around graphiql which will insist on authentication, and once authenticated you can use the technique described in Firebase auth for graphql clients to accept graphiql connections only from specific users. As a further lockdown, you can lock down the production API not to allow Introspection queries and turn off graphql support, and have a staging instance of the API to deal with Graphiql requests.

Tech

My client side UI is entirely in Vue, but it turns out that the graphiql plug in is react based, so I'll write this one in React. Luckily Google have an example firebase auth for react, so I can pretty much use that as a starting point. For a limited tech user base, it doesn't need to be fancy, and I can use the same firebase auth database as for the main Vue UI. I'm also using Material-ui for React, but will just stick with the Google example for the Login page styling. The simplest way to build a react act from scratch is to use this template, and then get rid of what you dont need, then modify as follows.

The source

Index.html
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<div id="app"></div>

app.css
firebaseui-styling.global
These comes directly from the Google Example with no changes

index.js
import React from 'react';
import ReactDOM from 'react-dom';

import 'graphiql/graphiql.css';
import 'codemirror/theme/solarized.css';
import 'typeface-roboto';
import Giql from './Giql.jsx';

ReactDOM.render(<Giql />, document.querySelector('#app'));

secrets.js
This contains my firebase project credentials and an apiKey that the API server has authorized for graphiql access

dependencies
  "dependencies": {
    "@material-ui/core": "^4.3.0",
    "firebase": "^6.3.3",
    "graphiql": "^0.13.2",
    "graphql": "^14.4.2",
    "isomorphic-fetch": "^2.2.1",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.1",
    "typeface-roboto": "^0.0.75",
    "react-firebaseui": "^3.1.2"
  },

Giql.jsx
This is all there is to the app - with the uri and port of the API set appropriately. For testing I'm attaching to a local version of the API. In real life, I'd be using a staging instance. Of interest here
  • Firebase provide a simple prewritten UI. It's not that pretty, but it'll do for this purpose. Most of the auth code is taken from the Google example.
  • firebaseAuth() is used to create a userID token that the api already knows how to decode and validate as desribed in Firebase auth for graphql clients
  • The graphiql wrapper is pretty minimal, with the only tweak being to modify the GraphIql Logo component to hold the logged in user avatar instead. This is clickable to give a way to sign off if necessary.
// React core.
import React from 'react';
import GraphiQL from 'graphiql';
import Avatar from '@material-ui/core/Avatar';

// Firebase.
import firebase from 'firebase/app';
import 'firebase/auth';
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth';

// Styles
import styles from './app.css'; 
import './firebaseui-styling.global.css'; 

// Get the Firebase config 
import { getFirebaseConfig, getGraphiqlKey } from './secrets';
const mode = 'lv';

// Instantiate a Firebase app.
const firebaseApp = firebase.initializeApp(getFirebaseConfig({ mode }));


const graphQLFetcher = (graphQLParams) =>
  firebaseApp.auth().currentUser.getIdToken()
    .then(token => fetch('http://localhost:8081/', {
      method: 'post',
      headers: {
        'x-fid-idtoken': token,
        'x-fid-apikey': getGraphiqlKey({ mode }),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(graphQLParams)
    }))
    .then(response => response.json());


export default class Login extends React.Component {
  uiConfig = {
    signInFlow: 'popup',
    signInOptions: [
      firebase.auth.GoogleAuthProvider.PROVIDER_ID,
      firebase.auth.EmailAuthProvider.PROVIDER_ID,
    ],
    callbacks: {
      signInSuccessWithAuthResult: () => false,
    }
  }

  state = {
    isSignedIn: undefined
  };

  componentDidMount() {
    this.unregisterAuthObserver = firebaseApp.auth().onAuthStateChanged((user) => {
      this.setState({ isSignedIn: !!user });
    });
  }

  componentWillUnmount() {
    this.unregisterAuthObserver();
  }

  render() {
    return (
      this.state.isSignedIn
        ? <div>
            <GraphiQL
              editorTheme="solarized"
              fetcher={ graphQLFetcher }
            >
              <GraphiQL.Logo>
                <Avatar
                  alt={ firebaseApp.auth().currentUser.displayName }
                src={ firebaseApp.auth().currentUser.photoURL }
                onClick={ () => { firebaseApp.auth().signOut() }}
                />
              </GraphiQL.Logo>
            </GraphiQL>
          </div>
        :
      <div className={ styles.container }>
        <div className={ styles.caption }>You need to be signed in with your filmID password to use graphiql</div>
        <div>
          <StyledFirebaseAuth className={ styles.firebaseUi } uiConfig={ this.uiConfig }
            firebaseAuth={ firebaseApp.auth() } />
        </div>
      </div>
    );
  }
}

Screenshot

And here's what it looks like when logged in - Graphiql with firebase auth!


When not logged in, it's not so pretty but it'll do the job.



More

Since G+ is closed, you can now star and follow post announcements and discussions on github, here 

Learning Apps Script, (and transitioning from VBA) are covered comprehensively in my my book, Going Gas - from VBA to Apps script, All formats are available from O'ReillyAmazon and all good bookshops. You can also read a preview on O'Reilly

If you prefer Video style learning I also have two courses available. also published by O'Reilly.
Google Apps Script for Developers and Google Apps Script for Beginners.

Comments