3

I have an angular app that is protected with Identity Aware Proxy (IAP). I am trying to add Firebase to this app in order to use firestore for a component using AngularFire. I don't want to make the user log in twice, so I thought about using IAP to authenticate with Firebase.

I've tried:

  • Letting GCP do its magic and see if the user is automatically inserted the Firebase Auth module - it isn't.

  • I've tried using the token you get from IAP in the GCP_IAAP_AUTH_TOKEN cookie with the signInWithCustomToken method - doesn't work, invalid token.

  • I've tried using getRedirectResult after logging in through IAP to if it's injected there - it isn't.

I've spent days trying to get this to work, I've had a colleague look at it as well, but it just doesn't seem possible. Now, as a last resort, I'm writing here to see if someone knows if it's even possible.

If not, I will have to suggest to the team to switch auth method and get rid of IAP, but I'd rather keep it.


More info:

Environment: NodeJS 10 on App Engine Flexible

Angular version: 7.x.x

AngularFire version: 5.2.3

Notes: I do not have a backend, because I want to use this component standalone and at most with a couple of Cloud Functions if need be. I am trying to use Firestore as a "backend".

Andy
  • 352
  • 1
  • 14
  • Did you find any solution for it? I've been trying to do the same without success. – Estevão Lucas May 15 '20 at 22:16
  • So, IIRC, you can't use the token from IAP directly, so you'll have to mint your own token in your backend and send that to the frontend as described in Frank's answer. We've changed our solution to not use firebase for now, so I ended up just straight up not using it. But I remember being able to log in with the custom minted token. Hope that helps :) – Andy May 16 '20 at 10:34
  • 1
    I manage to use the id token from IAP and authenticate the firebase with GoogleAuthProvider's credentials. I added my solution as a reply here. I hope it can be useful for somebody, since the documentation for those things a hard to find. Thank you. – Estevão Lucas May 19 '20 at 18:40
  • I have no idea how you managed to find that, I researched this for days. Nice one, hopefully it will help others. – Andy May 27 '20 at 10:18

3 Answers3

3

I managed to authenticate on Firebase automatically using the id token from the authentication made for Cloud IAP.

I just needed to use Google API Client Library for JavaScript

1) Add the Google JS library to your page i.e. in

<script src="https://apis.google.com/js/platform.js"></script>

2) Load the OAuth2 library, gapi.auth2

gapi.load('client:auth2', callback)
gapi.auth2.init()

3) Grab the id token from GoogleAuth:

const auth = gapi.auth2.getAuthInstance() 
const token = auth.currentUser.get().getAuthResponse().id_token;

4) Pass the token to GoogleAuthProvider's credential

const credential = firebase.auth.GoogleAuthProvider.credential(token);

5) Authenticate on Firebase using the credential

firebase.auth().signInAndRetrieveDataWithCredential(credential)

Putting everything together on an Angular component, this is what I have (including a sign out method)

import { Component, isDevMode, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import * as firebase from 'firebase/app';

// TODO: move this all to some global state logic

@Component({
  selector: 'app-sign-in-page',
  templateUrl: './sign-in-page.component.html',
  styleUrls: ['./sign-in-page.component.scss']
})
export class SignInPageComponent implements OnInit {
  GoogleAuth?: gapi.auth2.GoogleAuth = null;

  constructor(public auth: AngularFireAuth, private router: Router) { }

  async ngOnInit(): Promise<void> {
    // The build is restricted by Cloud IAP on non-local environments. Google
    // API Client is used to take the id token from IAP's authentication and
    // auto authenticate Firebase.
    //
    // GAPI auth: https://developers.google.com/identity/sign-in/web/reference#gapiauth2authorizeparams-callback
    // GoogleAuthProvider: https://firebase.google.com/docs/reference/js/firebase.auth.GoogleAuthProvider

    if (isDevMode()) return;

    await this.loadGapiAuth();

    this.GoogleAuth = gapi.auth2.getAuthInstance();

    // Prevents a reauthentication and a redirect from `/signout` to `/dashboard` route
    if (this.GoogleAuth && this.router.url === "/signin") {
      const token = this.GoogleAuth.currentUser.get().getAuthResponse().id_token;
      const credential = firebase.auth.GoogleAuthProvider.credential(token);

      this.auth.onAuthStateChanged((user) => {
        if (user) this.router.navigate(["/dashboard"]);
      });

      this.auth.signInAndRetrieveDataWithCredential(credential)
    }
  }

  // Sign in button, which calls this method, should only be displayed for local
  // environment where Cloud IAP isn't setup
  login() {
    this.auth.useDeviceLanguage();
    const provider = new firebase.auth.GoogleAuthProvider();
    provider.addScope("profile");
    provider.addScope("email");
    this.auth.signInWithRedirect(provider);
  }

  logout() {
    this.auth.signOut();

    if (this.GoogleAuth) {
      // It isn't a real sign out, since there's no way yet to sign out user from IAP
      // https://issuetracker.google.com/issues/69698275

      // Clearing the cookie does not change the fact that the user is still
      // logged into Google Accounts. When the user goes to your website again,
      // opens a new tab, etc. The user is still authenticated with Google and
      // therefore is still authenticated with Google IAP.
      window.location.href = "/?gcp-iap-mode=CLEAR_LOGIN_COOKIE"
    }
  }

  private async loadGapiAuth() {
    await new Promise((resolve) => gapi.load('client:auth2', resolve));
    await new Promise((resolve) => gapi.auth2.init(GAPI_CONFIG).then(resolve));
  }
}
Estevão Lucas
  • 3,470
  • 29
  • 35
  • Basically same as this https://fireship.io/snippets/how-to-use-google-apis-or-gapi-with-firebase-auth/ – Estevão Lucas May 20 '20 at 18:28
  • Nice, thanks for providing this! If I ever get back to Firebase, I'll definitely try out this approach. If it works and seems secure, I'll mark this as the correct answer, but until then, or until someone else has looked at it and confirmed it, I'll leave the other one as accepted. – Andy May 27 '20 at 10:14
1

I'm not experienced with Google Identity Aware Product, but my expectation is that you'll have to implement a custom provider for Firebase Authentication. The key part that you're missing now is a server-side code that take the information from the IAP token and mints a valid Firebase token from that. You then pass that token back to the client, which can use it to sign in with signInWithCustomToken.

Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645
  • IAP uses OIDC to authenticate. Should a token from OIDC not be usable in Firebase? – Andy Jan 09 '20 at 09:43
  • Also, if I mint my own token, then the token from IAP doesn't really matter anymore, and then it's kinda unrelated to my question.. – Andy Jan 09 '20 at 10:00
1

given the nature of IAP and Firebase, it seems not to be possible. The workaround could be just as mentioned in previous comments, to implement a custom provider, but you should mint your own token. Then maybe, re-thinking your solution if maybe this is the best way to achieve your goals.

Joss Baron
  • 1,188
  • 8
  • 12