In this tutorial, we’ll build a Node.js app that leverages the Firebase Auth REST API to manage users and roles. In addition, 我们将看到如何使用API来授权(或不授权)哪些用户可以访问特定的资源.
Introduction
几乎每个应用程序都需要某种程度的授权系统. 在某些情况下,使用 Users table is enough, but often, 我们需要一个更细粒度的权限模型,以允许某些用户访问某些资源,并限制他们访问其他资源. Building a system to support the latter is not trivial and can be very time consuming. In this tutorial, we’ll learn how to build a role-based auth API using Firebase, 这将帮助我们快速启动和运行.
Role-based Auth
In this authorization model, access is granted to roles, instead of specific users, and a user can have one or more depending on how you design your permission model. Resources, on the other hand, require certain roles to allow a user to execute it.
Firebase
Firebase Authentication
In a nutshell, Firebase身份验证是一个可扩展的基于令牌的身份验证系统,并提供了与最常见的提供商(如Google)的开箱即用集成, Facebook, and Twitter, among others.
我们可以在声明中设置任何JSON值(例如.g., { role: 'admin' } or { role: 'manager' }).
Once set, custom claims will be included in the generated Firebase token, 我们可以读这个值来控制访问.
它还提供了非常慷慨的免费配额,在大多数情况下都绰绰有余.
Firebase Functions
Functions是一个完全托管的无服务器平台服务. We just need to write our code in Node.js and deploy it. Firebase负责按需扩展基础设施、服务器配置等. In our case, we’ll use it to build our API and expose it via HTTP to the web.
Firebase allows us to set express.js 应用程序作为不同路径的处理程序——例如,你可以创建一个Express应用程序并将其挂接到 /mypath, and all requests coming to this route will be handled by the app configured.
From within the context of a function, 你可以访问整个Firebase身份验证API, using the Admin SDK.
This is how we’ll create the user API.
What We’ll Build
So before we get started, let’s take a look at what we’ll build. We are going to create a REST API with the following endpoints:
Http Verb
Path
Description
Authorization
GET
/users
Lists all users
Only admins and managers have access
POST
/users
Creates new user
Only admins and managers have access
GET
/users/:id
Gets the :id user
管理员、管理员和与:id相同的用户都可以访问
PATCH
/users/:id
Updates the :id user
管理员、管理员和与:id相同的用户都可以访问
DELETE
/users/:id
Deletes the :id user
管理员、管理员和与:id相同的用户都可以访问
每个端点都将处理身份验证, validate authorization, perform the correspondent operation, and finally return a meaningful HTTP code.
我们将创建验证令牌所需的身份验证和授权函数,并检查声明是否包含执行操作所需的角色.
Building the API
In order to build the API, we’ll need:
A Firebase project
firebase-tools installed
First, log in to Firebase:
firebase login
Next, initialize a Functions project:
firebase init
? Which Firebase CLI features do you want to set up for this folder? ...
(O)功能:配置和部署云功能
? Select a default Firebase project for this directory: {your-project}
? 你想用什么语言来写云函数? TypeScript
? Do you want to use TSLint to catch probable bugs and enforce style? Yes
? 你想现在用npm安装依赖吗? Yes
At src/index.ts there’s a helloWorld example, which you can uncomment to validate that your Functions works. Then you can cd functions and run npm run serve. This command will transpile the code and start the local server.
Notice the function is exposed on the path defined as the name of it at 'index.ts: 'helloWorld'.
Creating a Firebase HTTP Function
Now let’s code our API. We are going to create an http Firebase function and hook it on /api path.
First, install npm install express.
On the src/index.ts we will:
初始化firebase-admin SDK模块 admin.initializeApp();
Set an Express app as the handler of our api http endpoint
从'firebase-functions'中导入* as函数;
import * as admin from 'firebase-admin';
import * as express from 'express';
admin.initializeApp();
const app = express();
export const api = functions.http.onRequest(app);
Now, all requests going to /api will be handled by the app instance.
The next thing we’ll do is configure the app 实例来支持CORS并添加JSON主体解析器中间件. This way we can make requests from any URL and parse JSON formatted requests.
We’ll first install required dependencies.
npm install --save cors body-parser
npm install --save-dev @types/cors
And then:
//...
import * as cors from 'cors';
import * as bodyParser from 'body-parser';
//...
const app = express();
app.use(bodyParser.json());
app.use(cors({ origin: true }));
export const api = functions.http.onRequest(app);
最后,我们将配置路由 app will handle.
//...
import { routesConfig } from './users/routes-config';
//…
app.use(cors({ origin: true }));
routesConfig(app)
export const api = functions.http.onRequest(app);
Firebase Functions allows us to set an Express app as the handler, and any path after the one you set up at functions.http.onRequest(app);—in this case, api—will also be handled by the app. 这允许我们编写特定的端点,例如 api/users 并为每个HTTP动词设置处理程序,这是我们接下来要做的.
Let’s create the file src/users/routes-config.ts
Here, we’ll set a create handler at POST '/users'
import { Application } from "express";
import { create} from "./controller";
导出routesConfig(app: Application) {
app.post('/users',
create
);
}
Now, we’ll create the src/users/controller.ts file.
In this function, 我们首先验证所有字段都在请求体中, and next, 我们创建用户并设置自定义声明.
We are just passing { role } in the setCustomUserClaims-其他字段已经由Firebase设置.
现在,让我们通过添加授权来保护处理程序. 要做到这一点,我们将向我们的 create endpoint. With express.js, you can set a chain of handlers that will be executed in order. 在处理程序中,您可以执行代码并将其传递给 next() handler or return a response. 我们要做的是首先验证用户,然后验证它是否被授权执行.
在这个函数上,我们将验证 authorization bearer token in the request header. Then we’ll decode it with admin.auth().verifyidToken() and persist the user’s uid, role, and email in the res.locals 变量,稍后我们将使用它来验证授权.
In the case the token is invalid, we return a 401 response to the client:
在这个处理程序中,我们从中提取用户信息 res.locals 我们设置先前并验证它是否具有执行操作所需的角色,或者在操作允许同一用户执行的情况下, we validate that the ID on the request params is the same as the one in the auth token. If the user doesn’t have the required role, we’ll return a 403.
使用这两个方法,我们将能够对请求进行身份验证并在给定的情况下对它们进行授权 role in the incoming token. That’s great, but since Firebase doesn’t let us set custom claims from the project console,我们将无法执行这些端点. 为了绕过这个问题,我们可以从Firebase身份验证控制台创建一个根用户
And set an email comparison in the code. Now, when firing requests from this user, we’ll be able to execute all operations.
Now we can run the function locally. To do that, first you need to set up the account key 以便能够在本地连接认证API. Then run:
npm run serve
Deploy the API
Great! 现在我们已经使用Firebase的基于角色的身份验证API编写了应用程序, 我们可以将它部署到网络上并开始使用它. 使用Firebase进行部署非常简单,我们只需要运行 firebase deploy. Once the deploy is completed, we can access our API at the published URL.
Once our API is deployed, 在本教程中,我们有几种方法来使用它, 我将介绍如何通过Postman或从Angular应用中使用它.
If we enter the List All Users URL (/api/users) on any browser, we’ll get the following:
The reason for this is when sending the request from a browser, 我们正在执行一个没有验证头的GET请求. 这意味着我们的API实际上是按预期工作的!
Our API is secured via tokens—in order to generate such a token, we need to call Firebase’s Client SDK and log in with a valid user/password credential. When successful, Firebase将在响应中发送一个令牌,然后我们可以将其添加到我们想要执行的任何后续请求的标头中.
From an Angular App
在本教程中,我将介绍从Angular应用中使用API的重要部分. The full repository can be accessed here, 如果你需要一个循序渐进的教程,教你如何创建Angular应用并配置@angular/fire来使用, it you can check this post.
So, back to signing in, we’ll have a SignInComponent with a 让用户输入用户名和密码.
//...
//...
And on the class, we signInWithEmailAndPassword using the AngularFireAuth service.
Once the interceptor is set, we can make requests to our API from httpClient. For example, here’s a UsersService 其中我们调用列表all users,根据用户ID获取用户,创建用户,并更新用户.
从“@angular/core”中导入{Injectable};
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class UserFormService {
_BS = new BehaviorSubject({title: ", user: {}});
constructor() { }
edit(user) {
this._BS.next({ title: 'Edit User', user });
}
create() {
this._BS.next({title: 'Create User', User: null});
}
get title$() {
return this._BS.asObservable().pipe(
map(uf => uf.title)
);
}
get user$() {
return this._BS.asObservable().pipe(
map(uf => uf.user)
);
}
}
Back in the main component, let’s add the buttons to call those actions. In this case, “Edit User” will only be available for the logged-in user. You can go ahead and add the functionality to edit other users if you need to!
Firebase是一套云产品,可帮助您快速构建无服务器移动或web应用程序. It provides most of the common services involved on every app (database, authorization, storage, hosting).
How do I get Firebase Auth API?
You can create a project with your Google account at firebase.google.com. 一旦项目被创建,你就可以打开Firebase Auth并开始在你的应用中使用它.
Which is better, Firebase or AWS?
Firebase is Google-backed product, and one of which Google is trying to grow and add more and more features. AWS Amplify is a similar product, mostly targeted to mobile apps. Both are great products, with Firebase being an older product with more features.
Firebase有两个数据库:Realtime Database和Firestore. Both are NoSQL databases with similar features and different pricing models. Firestore支持更好的查询功能,而且这两个数据库的设计使得查询延迟不受数据库大小的影响.