Now that we have the authentciation part covered we can focus on the authorization.
Protect Routes
To protect the routes we can use Angular Fire built in guards. They are easy to use (CanActivate guards). Basically you could write your own with ease as well. The code below does two things:
Note: at the time of writing the Angular Fire version (6.0.0) seems to have a bug that without any error or warning has an effect on the change detection. For me it just didn’t update the bindings in my template properly unless I hit the forward / backward button in the browser. It seems this will be fixed in 6.0.1
- If a unauthenticated user tries to access the content, he will be redirected to the login page
- If an authenticated user opens the application (or login page) he will be redirected to the lessons overview page.
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LessonsComponent } from './lessons/lessons.component'; import { LessonComponent } from './lesson/lesson.component'; import { LoginComponent } from './login/login.component'; import { AngularFireAuthGuard, redirectLoggedInTo, redirectUnauthorizedTo } from '@angular/fire/auth-guard'; const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']); const redirectLoggedInToLessons = () => redirectLoggedInTo(['lessons']); const routes: Routes = [ { path: '', redirectTo: 'login', pathMatch: 'full' }, { path: 'login', component: LoginComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectLoggedInToLessons } }, { path: 'lessons', component: LessonsComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin } }, { path: 'lesson/:lessonId', component: LessonComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin } } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Protect the data
We only want to allow authenticated users to access the lesson data and to read the images. No one should have access to write any data. This is something we must keep an eye on: if you misconfigure the rules some malicious user could steal, manipulate or delete data.
The following shows the configuration: write is always denied, read only if the request has an authentication.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow write: if false;
}
}
}
I implemented the same rule for the storage:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read : if request.auth != null;
allow write: if false;
}
}
}
Admin Access
Until now I created all the lessons and uploaded all the images manually. To make this process easier we will need to write some sort of editor / admin UI to manage the data. The special access rights for this *technical* user can be configured as well.
User Data
As soon as we want to keep track on the progress of the user we will need to rethink the rules as we want each individual user to be able to edit their profile. Unlike in classical architectures (where the access to the data is typically protected by an additional service layer) the browser directly connects to the database. Therefore we either add this extra layer (as a function for instance) or we protect the data using proper rules. You can actually write quite complex security rules that include data validation.