Angular Dart: Guarding Routes with Google Auth UI

📅 June 01, 2019

👷 Chris Power

I’ve been working a lot with Google’s Dart lately, and I must say, its pretty impressive. The language is easy to pick up. It feels like Javascript with types, but with better syntax than Typescript (better to me, anyway). I have a project I’m working on written with Angular Dart, and I’m using Google’s Firebase as a backend. For Auth, I’m naturally using Google’s own Firebase Auth UI. All of these packages working together have been showing serious promise, and I’ve really been enjoying this app I’m working on.

There are; however, some setbacks. I have found that while documentation for AngularDart is really solid, it is also simultaneously quite sparse. Whenever you have a scenario that is a little outside a basic application, the docs seem to fall a bit short, and you need to put together answers from stack overflow, and random API docs.

For example, the wonderful Angular Dart Router has basically no documentation on its pub page. This became a problem when I asked the question

How do I guard routes for auth with firebase auth ui and angular router?

The logic is pretty straightforward, but the lack of documentation makes things hard. Essentially, you have to listen for a user from some service, auth_service. If that user is logged in, you allow a route to be transitioned, otherwise, you don’t.

In my application. I am using Firebase Dart UI to authenticate users. When a user is not logged in, I want to display a link to log them in. Otherwise, they are free to do whatever they want.

To accomplish this, I created an authentication service. It looks something like this:

  // example auth_service.dart
  import 'package:firebase/firebase.dart' as fb;

  fb.User user;
  fb.GoogleAuthProvider fbGoogleAuthProvider;
  fb.Auth _fbAuth;
  
  AuthService() {
    _fbAuth = fb.auth();
    _fbAuth.onAuthStateChanged.listen(_authChanged);
  }
  
  Future getCurrentUser() async {
    if (user == null) {
      await for (var newUser in _fbAuth.onAuthStateChanged) {
        user = newUser;
        return newUser;
      }
    } else {
      return user;
    }
  }
  
  void _authChanged(fb.User fbUser) {
    user = fbUser;
  }

  Future<bool> isLoggedIn() async {
    fb.User currentUser = await getCurrentUser();
    return currentUser != null;
  }

Lets unpack what we have above.

  1. We set up our class with firebase User, and Auth variables.
  2. Next, We have a constructor set up a Listener for the onAuthSTateChanged call. Therefore, whenever the auth state changes, we call a function that sets the user on the AuthService. This is essentially how we get currentUser.
  3. Then, we have a help function isLoggedIn that checks if a user exists on the AuthService. If so, it returns the user, otherwise it will wait for the authState to change.

With this setup, we now have an asynchronous way to check for a current user. Now all we have to do is guard our routes to make sure an unauthenticated user cannot view anything they shouldn’t. To accomplish this, we need to use an Angular Router lifecycle hook.

For our purposes, we can use the CanActivate hook.

class SomeComponent implements CanActivate {
  import 'src/services/auth_service.dart';
  
  final AuthService _authService;

  SomeComponent(this._authService);
  
  
  Future<bool> canActivate(_, RouterState current) async {
    bool loggedIn = await _authService.isLoggedIn();
    return loggedIn;
  }
}

Now we are guarding our routes while waiting for a user to log in! There is definitely some cleanup to do with this code. And I’m sure there are more elegant ways to handle this situation, but it works for now and appears pretty solid.

Lets Work Together

We're trusted by large, medium, and small companies all over the world

Have something you're working on?

Tell Us About It