Yalnızca Firebase kimliği doğrulanmış kullanıcılara izin vermek için firebase Cloud Function HTTP uç noktası nasıl korunur?


142

Yeni firebase bulut işlevi ile HTTP uç noktalarımın bir kısmını firebase'e taşımaya karar verdim. Her şey harika çalışıyor ... Ama şu sorunum var. HTTP Tetikleyicileri (Cloud Functions) tarafından oluşturulmuş iki uç noktam var

  1. Kullanıcı oluşturmak için bir API uç noktası ve Firebase Admin SDK tarafından oluşturulan özel Jetonu döndürür.
  2. Belirli kullanıcı ayrıntılarını almak için bir API uç noktası.

İlk uç nokta iyi olsa da, ikinci uç noktam için onu yalnızca kimliği doğrulanmış kullanıcılar için korumak istiyorum. daha önce oluşturduğum jetona sahip biri anlamına geliyor.

Bunu çözmeye nasıl gideceğim?

Header parametrelerini bulut işlevinde kullanarak alabileceğimizi biliyorum.

request.get('x-myheader')

ancak uç noktayı gerçek zamanlı veri tabanını korumak gibi korumanın bir yolu var mı?



2
@AmineHarbaoui Aynı soruyu sormuştum. Bu sayfaya bakın: firebase.google.com/docs/auth/admin/verify-id-tokens
MichM

Yanıtlar:


138

Yapmaya çalıştığınız şey için resmi bir kod örneği var. HTTPS işlevinizi, istemcinin kimlik doğrulama sırasında aldığı belirteçle bir Yetkilendirme başlığını gerektirecek şekilde nasıl ayarlayacağınız gösterilmektedir. İşlev, belirteci doğrulamak için firebase-admin kitaplığını kullanır.

Ayrıca, uygulamanız Firebase istemci kitaplıklarını kullanabiliyorsa, bu standart metnin çoğunu kolaylaştırmak için " çağrılabilir işlevler " i kullanabilirsiniz.


2
Bu kod örneği hala geçerli mi? Hala bugün buna nasıl hitap edersiniz?
Gal Bracha

1
@GalBracha Bugün hala geçerli olmalı (31 Ekim 2017).
Doug Stevenson

@DougStevenson, bu 'console.log' çağrılarının performans üzerinde 'gözle görülür' bir etkisi olacak mı?
Sanka Darshana

1
Çağrılabilir işlevlerin kullanılması, ortak metni nasıl kolaylaştıracak? Bunların sadece "DİNLENMEZ" sunucu işlevleri olduğunu anladığım kadarıyla, burada nasıl bir ilişki içinde olduklarını gerçekten anlamıyorum. Teşekkürler.
1252748

2
@ 1252748 Bağlantılı belgeleri okursanız, netleşecektir. Kimlik doğrulama belirtecinin geçişini ve doğrulamasını otomatik olarak gerçekleştirir, böylece bu kodu her iki tarafa da yazmanız gerekmez.
Doug Stevenson

121

@Doug tarafından belirtildiği gibi, firebase-adminbir jetonu doğrulamak için kullanabilirsiniz . Hızlı bir örnek oluşturdum:

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));
  });
});

Yukarıdaki örnekte, CORS'u da etkinleştirdim, ancak bu isteğe bağlı. İlk önce Authorizationbaşlığı alırsınız ve token.

Ardından, firebase-adminbu belirteci doğrulamak için kullanabilirsiniz . Yanıtta o kullanıcı için kodu çözülmüş bilgileri alacaksınız. Aksi takdirde, belirteç geçerli değilse, bir hata atar.


13
Basit olduğu ve resmi örnekte olduğu gibi ifadeye bağlı olmadığı için oy verildi.
DarkNeuron

5
Korseler hakkında daha fazla açıklama yapabilir misin?
pete

@pete: cors, kaynaklar arası kaynak paylaşımını çözüyor. Bunun hakkında daha fazla bilgi edinmek için Google'ı arayabilirsiniz.
Lạng Hoàng

@pete Cors, farklı url'lerden o firebase arka uç uç noktasına ulaşmanıza izin verir.
Walter Monecke

6
Sen kullanabilirsiniz @RezaRahmati getIdToken()istemci tarafında (örneğin üzerine yöntemini firebase.auth().currentUser.getIdToken().then(token => console.log(token))) docs Firebase
Will

18

@Doug tarafından da belirtildiği gibi, bazı standart kodları istemcinizden ve sunucunuzdan hariç tutmak için Çağrılabilir İşlevleri kullanabilirsiniz .

Örnek çağrılabilir işlev:

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 };
});

Doğrudan müşterinizden şu şekilde çağrılabilir:

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

2

Yukarıdaki yöntemler , işlevin içindeki mantığı kullanarak kullanıcının kimliğini doğrular , bu nedenle, denetimi yapmak için işlevin hala çağrılması gerekir.

Bu tamamen iyi bir yöntem, ancak kapsamlılık adına bir alternatif var:

Bir işlevi böylece "özel" olarak ayarlayabilirsiniz olamaz kayıtlı kullanıcı (izinler hakkında karar) dışında çağrılabilir. Bu durumda, kimliği doğrulanmamış istekler, işlevin bağlamı dışında reddedilir ve işlev hiçbir şekilde çağrılmaz.

Burada (a) İşlevleri genel / özel olarak yapılandırma ve ardından (b) işlevleriniz için son kullanıcıların kimliğini doğrulama ile ilgili referanslar yer almaktadır .

Yukarıdaki belgelerin Google Cloud Platform için olduğunu ve aslında bunun işe yaradığını, çünkü her Firebase projesinin aynı zamanda bir GCP projesi olduğunu unutmayın. Bu yöntemle ilgili bir uyarı, yazarken yalnızca Google hesabı tabanlı kimlik doğrulamayla çalışmasıdır.


1

Express kullanarak bununla ilgili güzel bir resmi örnek var - gelecekte kullanışlı olabilir: https://github.com/firebase/functions-samples/blob/master/authorized-https-endpoint/functions/index.js (sadece aşağıya yapıştırılmıştır) kesinlikle)

Unutmayın exports.appaltında fonksiyonları kullanılabilir hale getirir /appsümüklü böcek (bu durumda yalnızca bir fonksiyonudur ve kullanılabilir altındadır <you-firebase-app>/app/helloondan kurtulmak için aslında doğrulama kalır aynı yönelik kısmı biraz (katman bölümünü Express yeniden yazmak gerekir -. Çok çalışır iyi ve yorumlar sayesinde oldukça anlaşılır).

/**
 * 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);

Kurtulmak için yeniden yazmam /app:

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

module.exports = {
  hello
}

0

Golang GCP işlevinde uygun firebase kimlik doğrulamasını almakta zorlanıyorum. Aslında bunun bir örneği yok, bu yüzden bu küçük kitaplığı oluşturmaya karar verdim: https://github.com/Jblew/go-firebase-auth-in-gcp-functions

Artık firebase-auth kullanarak kullanıcıların kimliklerini kolayca doğrulayabilirsiniz (bu, gcp-Authenticated-functions'dan farklıdır ve kimliğe duyarlı-proxy tarafından doğrudan desteklenmez).

Yardımcı programı kullanmanın bir örneği:

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
}

İşlevinizi --allow-unauthenticatedbayrakla dağıtmayı unutmayın (çünkü firebase kimlik doğrulaması işlev yürütme içinde gerçekleşir).

Umarım bu bana yardım ettiği için size yardımcı olur. Performans nedenleriyle bulut işlevleri için golang'ı kullanmaya kararlıydım - Jędrzej


0

Firebase'de kodunuzu ve çalışmanızı basitleştirmek için bu sadece bir mimari tasarım meselesi :

  1. Herkese açık siteler / içerikler için HTTPS tetikleyicileriniExpress kullanın . Yalnızca aynı siteyi veya yalnızca belirli bir siteyi kısıtlamak için CORS, güvenliğin bu yönünü kontrol etmek için kullanın . Bu mantıklı çünkü Expresssunucu tarafı oluşturma içeriği nedeniyle SEO için kullanışlıdır.
  2. Kullanıcı kimlik doğrulaması gerektiren uygulamalar için HTTPS Çağrılabilir Firebase İşlevleri'ni kullanın, ardından contexttüm zorluklardan kurtulmak için parametreyi kullanın. Bu aynı zamanda mantıklıdır, çünkü AngularJS ile oluşturulmuş bir Tek Sayfa Uygulaması - AngularJS SEO için kötüdür, ancak şifre korumalı bir uygulama olduğu için SEO'ya da çok fazla ihtiyacınız yoktur. Şablon oluşturmaya gelince, AngularJS yerleşik şablonlamaya sahiptir, bu nedenle sunucu tarafı şablonuna gerek yoktur Express. Firebase Callable Functions'ın yeterince iyi olması gerekir.

Yukarıdakileri akılda tutarak, daha fazla güçlük çekmeyin ve hayatı kolaylaştırın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.