Firebase 인증 된 사용자 만 허용하도록 Firebase Cloud Function HTTP 엔드 포인트를 보호하는 방법은 무엇입니까?


141

새로운 firebase 클라우드 기능을 사용하여 일부 HTTP 엔드 포인트를 firebase로 옮기기로 결정했습니다. 모든 것이 잘 작동합니다 ...하지만 다음과 같은 문제가 있습니다. HTTP 트리거 (클라우드 함수)로 두 개의 엔드 포인트 빌드가 있습니다.

  1. 사용자를 생성하고 Firebase Admin SDK에서 생성 한 맞춤 토큰을 반환하는 API 엔드 포인트입니다.
  2. 특정 사용자 세부 사항을 가져 오는 API 엔드 포인트입니다.

첫 번째 끝점은 괜찮지 만 두 번째 끝점은 인증 된 사용자 만 보호하려고합니다. 내가 이전에 생성 한 토큰을 가진 사람을 의미합니다.

이 문제를 해결하려면 어떻게해야합니까?

클라우드 함수에서 헤더 매개 변수를 사용할 수 있다는 것을 알고 있습니다.

request.get('x-myheader')

그러나 실시간 데이터베이스를 보호하는 것처럼 엔드 포인트를 보호 할 수있는 방법이 있습니까?


Firebase Admin SDK가 첫 번째 API에서 생성 한 커스텀 토큰을 어떻게 얻었습니까
Amine Harbaoui

2
@ AmineHarbaoui 나는 같은 질문을했다. 이 페이지를 참조하십시오 firebase.google.com/docs/auth/admin/verify-id-tokens
MichM

답변:


137

수행하려는 작업에 대한 공식 코드 샘플 이 있습니다. 인증하는 동안 클라이언트가 수신 한 토큰이있는 Authorization 헤더를 요구하도록 HTTPS 기능을 설정하는 방법이 설명되어 있습니다. 이 기능은 firebase-admin 라이브러리를 사용하여 토큰을 확인합니다.

또한 앱에서 Firebase 클라이언트 라이브러리를 사용할 수있는 경우 ' 호출 가능 함수 '를 사용하여이 상용구를 훨씬 쉽게 만들 수 있습니다.


2
이 코드 샘플이 여전히 유효합니까? 아직도이 문제를 어떻게 해결할 수 있습니까?
Gal Bracha

1
@GalBracha 오늘도 유효합니다 (2017 년 10 월 31 일).
Doug Stevenson

@DougStevenson은 이러한 'console.log'호출이 성능에 '눈에 띄는'영향을 미치나요?
Sanka Darshana

1
호출 가능한 기능을 사용하면 상용구가 어떻게 더 쉬워 집니까? 내가 이해하는 것들은 단지 "REST가 아닌"서버 기능이라는 것에서, 이것이 그들이 어떻게 여기에 관련되는지 이해하지 못한다. 감사.
1252748

2
@ 1252748 링크 된 문서를 읽으면 명확 해집니다. 인증 토큰의 전달 및 유효성 검사를 자동으로 처리하므로 양쪽에서 해당 코드를 작성할 필요가 없습니다.
더그 스티븐슨

121

@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헤더 를 가져 와서 를 찾으십시오 token.

그런 다음 firebase-admin해당 토큰을 확인하는 데 사용할 수 있습니다 . 응답에서 해당 사용자에 대한 디코딩 된 정보를 얻을 수 있습니다. 그렇지 않으면 토큰이 유효하지 않으면 오류가 발생합니다.


13
간단하고 공식 예와 같이 표현에 의존하지 않기 때문에 찬성했습니다.
DarkNeuron

5
오호에 대해 더 자세히 설명해 주시겠습니까?
pete

@ pete : cors는 출처 간 리소스 공유를 해결하고 있습니다. 구글에 대해 더 많이 알 수 있습니다.
Lạng Hoàng

@pete Cors를 사용하면 다른 URL에서 해당 Firebase 백엔드 엔드 포인트를 칠 수 있습니다.
Walter Monecke

6
당신은 사용할 수 있습니다 @RezaRahmati getIdToken()클라이언트 측 (예에 방법 firebase.auth().currentUser.getIdToken().then(token => console.log(token))) 문서 중포 기지

18

@Doug에서 언급했듯이 Callable Functions 를 사용 하여 클라이언트와 서버에서 상용구 코드제외 할 수 있습니다 .

호출 가능 함수 예 :

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

  // do your things..
  const uid = context.auth.uid;
  const query = data.query;

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

다음과 같이 클라이언트에서 직접 호출 할 수 있습니다.

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

2

위의 메소드 는 함수 내부 의 논리를 사용하여 사용자를 인증 하므로 검사를 수행하려면 함수를 계속 호출해야합니다.

그것은 완전히 훌륭한 방법이지만 이해를 위해 대안이 있습니다.

등록 된 사용자 (권한을 결정) 가 아닌 한 호출 할 수 없도록 기능을 "비공개"로 설정할 수 있습니다 . 이 경우 인증되지 않은 요청은 함수 컨텍스트 외부에서 거부되며 함수가 전혀 호출 되지 않습니다 .

다음은 (a) 기능을 퍼블릭 / 프라이빗 으로 구성한 다음 (b) 기능에 대한 최종 사용자 인증에 대한 참조 입니다.

모든 중포 기지 프로젝트이기 때문에 문서가 위의이 작품은, 실제로 Google 클라우드 플랫폼 용이며, 참고 GCP 프로젝트. 이 방법과 관련하여주의해야 할 점은 서면으로 Google 계정 기반 인증에서만 작동한다는 것입니다.


1

Express를 사용하는 멋진 공식 예가 있습니다. https://github.com/firebase/functions-samples/blob/master/authorized-https-endpoint/functions/index.js (아래에 붙여 넣기) 확실히)

슬러그에서 exports.app함수를 사용할 수 있음 을 명심하십시오 /app(이 경우 하나의 함수 만 있고 아래에서 사용할 수 있습니다 <you-firebase-app>/app/hello.) 제거하려면 실제로 Express 부분을 약간 다시 작성해야합니다 (유효성 검사를위한 미들웨어 부분은 동일하게 유지됩니다-매우 효과적입니다) 좋았고 의견 덕분에 이해할 수 있습니다).

/**
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();

// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const validateFirebaseIdToken = async (req, res, next) => {
  console.log('Check if request is authorized with Firebase ID token');

  if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
      !(req.cookies && req.cookies.__session)) {
    console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
        'Make sure you authorize your request by providing the following HTTP header:',
        'Authorization: Bearer <Firebase ID Token>',
        'or by passing a "__session" cookie.');
    res.status(403).send('Unauthorized');
    return;
  }

  let idToken;
  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
    console.log('Found "Authorization" header');
    // Read the ID Token from the Authorization header.
    idToken = req.headers.authorization.split('Bearer ')[1];
  } else if(req.cookies) {
    console.log('Found "__session" cookie');
    // Read the ID Token from cookie.
    idToken = req.cookies.__session;
  } else {
    // No cookie
    res.status(403).send('Unauthorized');
    return;
  }

  try {
    const decodedIdToken = await admin.auth().verifyIdToken(idToken);
    console.log('ID Token correctly decoded', decodedIdToken);
    req.user = decodedIdToken;
    next();
    return;
  } catch (error) {
    console.error('Error while verifying Firebase ID token:', error);
    res.status(403).send('Unauthorized');
    return;
  }
};

app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
  res.send(`Hello ${req.user.name}`);
});

// This HTTPS endpoint can only be accessed by your Firebase Users.
// Requests need to be authorized by providing an `Authorization` HTTP header
// with value `Bearer <Firebase ID Token>`.
exports.app = functions.https.onRequest(app);

제거하기 위해 다시 작성 /app:

const hello = functions.https.onRequest((request, response) => {
  res.send(`Hello ${req.user.name}`);
})

module.exports = {
  hello
}

0

golang GCP 기능에서 적절한 firebase 인증을 받기 위해 고심하고 있습니다. 실제로 이에 대한 예는 없으므로이 작은 라이브러리를 빌드하기로 결정했습니다. https://github.com/Jblew/go-firebase-auth-in-gcp-functions

이제 firebase-auth를 사용하여 사용자를 쉽게 인증 할 수 있습니다 (gcp 인증 기능과 다르며 ID 인식 프록시가 직접 지원하지 않음).

다음은이 유틸리티를 사용하는 예입니다.

import (
  firebaseGcpAuth "github.com/Jblew/go-firebase-auth-in-gcp-functions"
  auth "firebase.google.com/go/auth"
)

func SomeGCPHttpCloudFunction(w http.ResponseWriter, req *http.Request) error {
   // You need to provide 1. Context, 2. request, 3. firebase auth client
  var client *auth.Client
    firebaseUser, err := firebaseGcpAuth.AuthenticateFirebaseUser(context.Background(), req, authClient)
    if err != nil {
    return err // Error if not authenticated or bearer token invalid
  }

  // Returned value: *auth.UserRecord
}

--allow-unauthenticatedfirebase 인증이 함수 실행 내에서 발생하기 때문에 플래그로 함수를 배포하는 것을 명심하십시오 .

그것이 도움이 되었기를 바랍니다. 성능상의 이유로 클라우드 기능에 golang을 사용하기로 결정했습니다. — Jędrzej


0

Firebase에서는 코드와 작업을 단순화하기 위해 건축 설계 의 문제입니다 .

  1. 공개적으로 액세스 가능한 사이트 / 콘텐츠의 경우 와 함께 HTTPS 트리거를 사용하십시오Express . 동일한 사이트 또는 특정 사이트 만 제한하려면을 사용 CORS하여이 보안 측면을 제어하십시오. 이는 Express서버 측 렌더링 컨텐츠로 인해 SEO에 유용하기 때문에 의미 가 있습니다.
  2. 사용자 인증이 필요한 앱의 경우 HTTPS Callable Firebase Functions 를 사용한 다음 context매개 변수를 사용하여 모든 번거 로움을 저장하십시오. AngularJS로 빌드 된 단일 페이지 앱과 같이 AngularJS는 SEO에는 좋지 않지만 암호로 보호되는 앱이므로 SEO도 많이 필요하지 않습니다. 템플릿과 관련하여 AngularJS에는 템플릿이 내장되어 있으므로을 사용하여 서버 측 템플릿이 필요하지 않습니다 Express. 그러면 Firebase Callable Functions가 충분해야합니다.

위의 사항을 염두에두고 더 이상 번거 로움을 없애고 인생을 더 편하게 만듭니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.