使用新的firebase云功能,我决定将一些HTTP端点移动到firebase。 一切都很好……但我有以下问题。我有两个端点构建HTTP触发器(云函数)

用于创建用户并返回自定义令牌的API端点 由Firebase Admin SDK生成。 获取特定用户详细信息的API端点。

虽然第一个端点很好,但对于第二个端点,我希望仅为经过身份验证的用户保护它。意思是拥有我之前生成的令牌的人。

我该怎么解呢?

我知道我们可以在云函数中使用Header参数

request.get('x-myheader')

但是有没有一种方法可以像保护实时数据库一样保护端点呢?


当前回答

你可以把它当作一个函数返回布尔值。如果用户验证与否,您将继续或停止您的API。此外,您还可以从变量decode返回声明或用户结果

const authenticateIdToken = async (
    req: functions.https.Request,
    res: functions.Response<any>
) => {
    try {
        const authorization = req.get('Authorization');
        if (!authorization) {
            res.status(400).send('Not Authorized User');
            return false;
        }
        const tokenId = authorization.split('Bearer ')[1];

        return await auth().verifyIdToken(tokenId)
            .then((decoded) => {
                return true;
            })
            .catch((err) => {
                res.status(401).send('Not Authorized User')
                return false;
            });
    } catch (e) {
        res.status(400).send('Not Authorized User')
        return false;
    }
}

其他回答

你可以把它当作一个函数返回布尔值。如果用户验证与否,您将继续或停止您的API。此外,您还可以从变量decode返回声明或用户结果

const authenticateIdToken = async (
    req: functions.https.Request,
    res: functions.Response<any>
) => {
    try {
        const authorization = req.get('Authorization');
        if (!authorization) {
            res.status(400).send('Not Authorized User');
            return false;
        }
        const tokenId = authorization.split('Bearer ')[1];

        return await auth().verifyIdToken(tokenId)
            .then((decoded) => {
                return true;
            })
            .catch((err) => {
                res.status(401).send('Not Authorized User')
                return false;
            });
    } catch (e) {
        res.status(400).send('Not Authorized User')
        return false;
    }
}

这里有很多很棒的信息真的对我很有帮助,但是我认为,对于那些第一次使用Angular尝试使用它的人来说,分解一个简单的工作示例可能会很好。谷歌Firebase文档可以在https://firebase.google.com/docs/auth/admin/verify-id-tokens#web上找到。

//#### YOUR TS COMPONENT FILE #####
import { Component, OnInit} from '@angular/core';
import * as firebase from 'firebase/app';
import { YourService } from '../services/yourservice.service';

@Component({
  selector: 'app-example',
  templateUrl: './app-example.html',
  styleUrls: ['./app-example.scss']
})

export class AuthTokenExample implements OnInit {

//property
idToken: string;

//Add your service
constructor(private service: YourService) {}

ngOnInit() {

    //get the user token from firebase auth
    firebase.auth().currentUser.getIdToken(true).then((idTokenData) => {
        //assign the token to the property
        this.idToken = idTokenData;
        //call your http service upon ASYNC return of the token
        this.service.myHttpPost(data, this.idToken).subscribe(returningdata => {
            console.log(returningdata)
        });

    }).catch((error) => {
        // Handle error
        console.log(error);
    });

  }

}

//#### YOUR SERVICE #####
//import of http service
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class MyServiceClass {

    constructor(private http: HttpClient) { }

  //your myHttpPost method your calling from your ts file
  myHttpPost(data: object, token: string): Observable<any> {

    //defining your header - token is added to Authorization Bearer key with space between Bearer, so it can be split in your Google Cloud Function
    let httpOptions = {
        headers: new HttpHeaders({
            'Content-Type': 'application/json',
         'Authorization': 'Bearer ' + token
        })
    }

    //define your Google Cloud Function end point your get from creating your GCF
    const endPoint = ' https://us-central1-your-app.cloudfunctions.net/doSomethingCool';

    return this.http.post<string>(endPoint, data, httpOptions);

  }

}


//#### YOUR GOOGLE CLOUD FUNCTION 'GCF' #####
//your imports
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});


exports.doSomethingCool = functions.https.onRequest((req, res) => {

//cross origin middleware
    cors(req, res, () => {

        //get the token from the service header by splitting the Bearer in the Authorization header 
        const tokenId = req.get('Authorization').split('Bearer ')[1];

        //verify the authenticity of token of the user
        admin.auth().verifyIdToken(tokenId)
            .then((decodedToken) => {
                //get the user uid if you need it.
               const uid = decodedToken.uid;

                //do your cool stuff that requires authentication of the user here.

            //end of authorization
            })
            .catch((error) => {
                console.log(error);
            });

    //end of cors
    })

//end of function
})

正如@Doug提到的, 你可以使用可调用函数来从你的客户端和服务器中排除一些样板代码。

示例可调用函数:

export const getData = functions.https.onCall((data, context) => {
  // verify Firebase Auth ID token
  if (!context.auth) {
    return { message: 'Authentication Required!', code: 401 };
  }

  /** This scope is reachable for authenticated users only */

  return { message: 'Some Data', code: 200 };
});

它可以直接从你的客户端调用,像这样:

firebase.functions().httpsCallable('getData')({query}).then(result => console.log(result));

正如@Doug所提到的,您可以使用firebase-admin来验证令牌。我举了一个简单的例子:

exports.auth = functions.https.onRequest((req, res) => {
  cors(req, res, () => {
    const tokenId = req.get('Authorization').split('Bearer ')[1];
    
    return admin.auth().verifyIdToken(tokenId)
      .then((decoded) => res.status(200).send(decoded))
      .catch((err) => res.status(401).send(err));
  });
});

在上面的例子中,我也启用了CORS,但这是可选的。首先,获取Authorization头并找出令牌。

然后,您可以使用firebase-admin来验证该令牌。您将在响应中获得该用户的解码信息。否则,如果令牌无效,它将抛出一个错误。

上述方法使用函数内部的逻辑对用户进行身份验证,因此仍然必须调用函数来进行检查。

这是一个非常好的方法,但为了全面起见,还有另一种选择:

你可以将一个函数设置为“私有”,这样除了注册用户(由你决定权限),它不能被调用。在这种情况下,在函数上下文中拒绝未经身份验证的请求,并且根本不会调用函数。

这里引用(a)将函数配置为公共/私有,然后(b)对最终用户进行函数的身份验证。

请注意,上面的文档是针对谷歌云平台的,实际上,这是可行的,因为每个Firebase项目也是一个GCP项目。与此方法相关的一个警告是,在撰写本文时,它仅适用于基于google帐户的身份验证。