Bergabunglah dengan kami secara langsung dan online di Firebase Summit pada 18 Oktober 2022. Pelajari cara Firebase dapat membantu Anda mempercepat pengembangan aplikasi, merilis aplikasi dengan percaya diri, dan menentukan skala dengan mudah. Daftar sekarang

Perluas Firebase Authentication dengan memblokir Cloud Functions,Perpanjang Firebase Authentication dengan memblokir Cloud Functions

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Jika Anda telah mengupgrade ke Firebase Authentication dengan Identity Platform , Anda dapat memperluas Firebase Authentication menggunakan pemblokiran Cloud Functions .

Fungsi pemblokiran memungkinkan Anda mengeksekusi kode khusus yang mengubah hasil pendaftaran pengguna atau masuk ke aplikasi Anda. Misalnya, Anda dapat mencegah pengguna mengautentikasi jika mereka tidak memenuhi kriteria tertentu, atau memperbarui informasi pengguna sebelum mengembalikannya ke aplikasi klien Anda.

Sebelum kamu memulai

Untuk menggunakan fungsi pemblokiran, Anda harus mengupgrade project Firebase ke Firebase Authentication dengan Identity Platform. Jika Anda belum melakukan upgrade, lakukan terlebih dahulu.

Memahami fungsi pemblokiran

Anda dapat mendaftarkan fungsi pemblokiran untuk dua acara:

  • beforeCreate : Dipicu sebelum pengguna baru disimpan ke database Firebase Authentication, dan sebelum token dikembalikan ke aplikasi klien Anda.

  • beforeSignIn : Dipicu setelah kredensial pengguna diverifikasi, tetapi sebelum Firebase Authentication mengembalikan token ID ke aplikasi klien Anda. Jika aplikasi Anda menggunakan autentikasi multifaktor, fungsi akan terpicu setelah pengguna memverifikasi faktor kedua. Perhatikan bahwa membuat pengguna baru juga memicu beforeSignIn , selain beforeCreate .

Ingatlah hal-hal berikut saat menggunakan fungsi pemblokiran:

  • Fungsi Anda harus merespons dalam 7 detik. Setelah 7 detik, Firebase Authentication mengembalikan kesalahan, dan operasi klien gagal.

  • Kode respons HTTP selain 200 diteruskan ke aplikasi klien Anda. Pastikan kode klien Anda menangani kesalahan apa pun yang dapat dikembalikan oleh fungsi Anda.

  • Fungsi berlaku untuk semua pengguna di proyek Anda, termasuk semua yang ada di penyewa . Firebase Authentication memberikan informasi tentang pengguna ke fungsi Anda, termasuk penyewa tempat mereka berada, sehingga Anda dapat meresponsnya dengan tepat.

  • Menautkan penyedia identitas lain ke akun akan memicu kembali semua fungsi beforeSignIn terdaftar.

  • Otentikasi anonim dan kustom tidak memicu fungsi pemblokiran.

Terapkan dan daftarkan fungsi pemblokiran

Untuk memasukkan kode kustom Anda ke dalam alur autentikasi pengguna, terapkan dan daftarkan fungsi pemblokiran. Setelah fungsi pemblokiran Anda diterapkan dan didaftarkan, kode kustom Anda harus berhasil diselesaikan agar autentikasi dan pembuatan pengguna berhasil.

Terapkan fungsi pemblokiran

Anda menerapkan fungsi pemblokiran dengan cara yang sama seperti Anda menerapkan fungsi apa pun. (lihat halaman Memulai Cloud Functions untuk detailnya). Kesimpulan:

  1. Tulis Cloud Functions yang menangani event beforeCreate , event beforeSignIn , atau keduanya.

    Misalnya, untuk memulai, Anda dapat menambahkan fungsi tanpa operasi berikut ke index.js :

    const functions = require('firebase-functions');
    
    exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
      // TODO
    });
    
    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      // TODO
    });
    

    Contoh di atas telah menghilangkan implementasi logika auth kustom. Lihat bagian berikut untuk mempelajari cara menerapkan fungsi pemblokiran dan skenario Umum untuk contoh spesifik.

  2. Terapkan fungsi Anda menggunakan Firebase CLI:

    firebase deploy --only functions
    

    Anda harus menerapkan kembali fungsi Anda setiap kali Anda memperbaruinya.

Daftarkan fungsi pemblokiran

  1. Buka halaman Setelan Otentikasi Firebase di konsol Firebase.

  2. Pilih tab Fungsi pemblokiran .

  3. Daftarkan fungsi pemblokiran Anda dengan memilihnya dari menu tarik-turun di bawah Sebelum pembuatan akun (beforeCreate) atau Sebelum masuk (beforeSignIn) .

  4. Simpan perubahan Anda.

Mendapatkan informasi pengguna dan konteks

Peristiwa beforeSignIn dan beforeCreate menyediakan objek User dan EventContext yang berisi informasi tentang pengguna yang masuk. Gunakan nilai ini dalam kode Anda untuk menentukan apakah akan mengizinkan operasi untuk dilanjutkan.

Untuk daftar properti yang tersedia di objek User , lihat referensi API UserRecord .

Objek EventContext berisi properti berikut:

Nama Keterangan Contoh
locale Lokal aplikasi. Anda dapat menyetel lokal menggunakan SDK klien, atau dengan meneruskan header lokal di REST API. fr atau sv-SE
ipAddress Alamat IP perangkat tempat pengguna akhir mendaftar atau masuk. 114.14.200.1
userAgent Agen pengguna memicu fungsi pemblokiran. Mozilla/5.0 (X11; Linux x86_64)
eventId Pengidentifikasi unik acara. rWsyPtolplG2TBFoOkkgyg
eventType Jenis acara. Ini memberikan informasi tentang nama acara, seperti beforeSignIn atau beforeCreate , dan metode masuk terkait yang digunakan, seperti Google atau email/sandi. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Selalu USER . USER
resource Proyek atau penyewa Firebase Authentication. projects/ project-id /tenants/ tenant-id
timestamp Waktu peristiwa dipicu, diformat sebagai string RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Sebuah objek yang berisi informasi tentang pengguna. AdditionalUserInfo
credential Objek yang berisi informasi tentang kredensial pengguna. AuthCredential

Memblokir pendaftaran atau masuk

Untuk memblokir upaya pendaftaran atau masuk, lemparkan HttpsError di fungsi Anda. Sebagai contoh:

Node.js

throw new functions.auth.HttpsError('permission-denied');

Tabel berikut mencantumkan kesalahan yang dapat Anda timbulkan, bersama dengan pesan kesalahan defaultnya:

Nama Kode Pesan
invalid-argument 400 Klien menetapkan argumen yang tidak valid.
failed-precondition 400 Permintaan tidak dapat dijalankan dalam status sistem saat ini.
out-of-range 400 Klien menentukan rentang yang tidak valid.
unauthenticated 401 Token OAuth tidak ada, tidak valid, atau kedaluwarsa.
permission-denied 403 Klien tidak memiliki izin yang memadai.
not-found 404 Sumber daya yang ditentukan tidak ditemukan.
aborted 409 Konflik konkurensi, seperti konflik baca-ubah-tulis.
already-exists 409 Sumber daya yang coba dibuat oleh klien sudah ada.
resource-exhausted 429 Entah dari kuota sumber daya atau mencapai pembatasan tingkat.
cancelled 499 Permintaan dibatalkan oleh klien.
data-loss 500 Kehilangan data yang tidak dapat dipulihkan atau kerusakan data.
unknown 500 Kesalahan server tidak dikenal.
internal 500 Kesalahan server dari dalam.
not-implemented 501 Metode API tidak diterapkan oleh server.
unavailable 503 Layanan tidak tersedia.
deadline-exceeded 504 Batas waktu permintaan terlampaui.

Anda juga dapat menentukan pesan kesalahan khusus:

Node.js

throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');

Contoh berikut menunjukkan cara memblokir pengguna yang tidak berada dalam domain tertentu agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  // (If the user is authenticating within a tenant context, the tenant ID can be determined from
  // user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')

  // Only users of a specific domain can sign up.
  if (user.email.indexOf('@acme.com') === -1) {
    throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Terlepas dari apakah Anda menggunakan pesan default atau kustom, Cloud Functions membungkus kesalahan dan mengembalikannya ke klien sebagai kesalahan internal. Sebagai contoh:

throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);

Aplikasi Anda harus menangkap kesalahan, dan menanganinya dengan tepat. Sebagai contoh:

JavaScript

// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
  .then((result) => {
    result.user.getIdTokenResult()
  })
  .then((idTokenResult) => {
    console.log(idTokenResult.claim.admin);
  })
  .catch((error) => {
    if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
      // Display error.
    } else {
      // Registration succeeds.
    }
  });

Memodifikasi pengguna

Alih-alih memblokir upaya pendaftaran atau masuk, Anda dapat mengizinkan operasi untuk melanjutkan, tetapi memodifikasi objek User yang disimpan ke database Firebase Authentication dan dikembalikan ke klien.

Untuk memodifikasi pengguna, kembalikan objek dari event handler Anda yang berisi bidang yang akan dimodifikasi. Anda dapat mengubah bidang berikut:

  • displayName
  • disabled
  • emailVerified
  • photoURL
  • customClaims
  • sessionClaims ( beforeSignIn saja)

Dengan pengecualian sessionClaims , semua bidang yang dimodifikasi disimpan ke database Firebase Authentication, yang berarti mereka disertakan pada token respons dan bertahan di antara sesi pengguna.

Contoh berikut menunjukkan cara menetapkan nama tampilan default:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: user.displayName || 'Guest';
  };
});

Jika Anda mendaftarkan event handler untuk beforeCreate dan beforeSignIn , perhatikan bahwa beforeSignIn dijalankan setelah beforeCreate . Bidang pengguna yang diperbarui di beforeCreate terlihat di beforeSignIn . Jika Anda menyetel bidang selain sessionClaims di kedua pengendali peristiwa, nilai yang disetel di beforeSignIn menimpa nilai yang disetel di beforeCreate . Untuk sessionClaims saja, mereka disebarkan ke klaim token sesi saat ini, tetapi tidak dipertahankan atau disimpan dalam database.

Misalnya, jika ada sessionClaims yang disetel, beforeSignIn akan mengembalikannya dengan klaim beforeCreate , dan mereka akan digabungkan. Saat digabungkan, jika kunci sessionClaims cocok dengan kunci di customClaims , customClaims yang cocok akan ditimpa dalam klaim token oleh kunci sessionClaims . Namun, kunci customClaims yang ditimpa akan tetap ada di database untuk permintaan di masa mendatang.

Kredensial dan data OAuth yang didukung

Anda dapat meneruskan kredensial dan data OAuth ke fungsi pemblokiran dari berbagai penyedia identitas. Tabel berikut menunjukkan kredensial dan data apa yang didukung untuk setiap penyedia identitas:

Penyedia Identitas Token ID Token Akses Waktu kedaluwarsa Rahasia Token Segarkan Token Klaim Masuk
Google Ya Ya Ya Tidak Ya Tidak
Facebook Tidak Ya Ya Tidak Tidak Tidak
Twitter Tidak Ya Tidak Ya Tidak Tidak
GitHub Tidak Ya Tidak Tidak Tidak Tidak
Microsoft Ya Ya Ya Tidak Ya Tidak
LinkedIn Tidak Ya Ya Tidak Tidak Tidak
Yahoo Ya Ya Ya Tidak Ya Tidak
apel Ya Ya Ya Tidak Ya Tidak
SAML Tidak Tidak Tidak Tidak Tidak Ya
OIDC Ya Ya Ya Tidak Ya Ya

Segarkan token

Untuk menggunakan token penyegaran dalam fungsi pemblokiran, Anda harus terlebih dahulu mencentang kotak di halaman Fungsi pemblokiran di Firebase console.

Token penyegaran tidak akan dikembalikan oleh penyedia identitas mana pun saat masuk secara langsung dengan kredensial OAuth, seperti token ID atau token akses. Dalam situasi ini, kredensial OAuth sisi klien yang sama akan diteruskan ke fungsi pemblokiran.

Bagian berikut menjelaskan setiap jenis penyedia identitas serta kredensial dan data yang didukungnya.

Penyedia OIDC generik

Saat pengguna masuk dengan penyedia OIDC generik, kredensial berikut akan diteruskan:

  • Token ID : Disediakan jika aliran id_token dipilih.
  • Token akses : Disediakan jika aliran kode dipilih. Perhatikan bahwa aliran kode saat ini hanya didukung melalui REST API.
  • Segarkan token : Disediakan jika cakupan offline_access dipilih.

Contoh:

const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Google

Saat pengguna masuk dengan Google, kredensial berikut akan diteruskan:

  • tanda pengenal
  • Token akses
  • Segarkan token : Hanya diberikan jika parameter khusus berikut diminta:
    • access_type=offline
    • prompt=consent , jika pengguna sebelumnya menyetujui dan tidak ada ruang lingkup baru yang diminta

Contoh:

const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
  'access_type': 'offline',
  'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);

Pelajari lebih lanjut tentang token penyegaran Google .

Facebook

Saat pengguna masuk dengan Facebook, kredensial berikut akan diteruskan:

  • Token akses : Token akses dikembalikan yang dapat ditukar dengan token akses lain. Pelajari lebih lanjut tentang berbagai jenis token akses yang didukung oleh Facebook dan bagaimana Anda dapat menukarnya dengan token berumur panjang .

GitHub

Saat pengguna masuk dengan GitHub, kredensial berikut akan diteruskan:

  • Token akses : Tidak kedaluwarsa kecuali dicabut.

Microsoft

Saat pengguna masuk dengan Microsoft, kredensial berikut akan diteruskan:

  • tanda pengenal
  • Token akses
  • Segarkan token : Diteruskan ke fungsi pemblokiran jika cakupan offline_access dipilih.

Contoh:

const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Yahoo

Saat pengguna masuk dengan Yahoo, kredensial berikut akan diteruskan tanpa parameter atau cakupan khusus:

  • tanda pengenal
  • Token akses
  • Segarkan token

LinkedIn

Saat pengguna masuk dengan LinkedIn, kredensial berikut akan diteruskan:

  • Token akses

apel

Saat pengguna masuk dengan Apple, kredensial berikut akan diteruskan tanpa parameter atau cakupan khusus:

  • tanda pengenal
  • Token akses
  • Segarkan token

Skenario umum

Contoh berikut menunjukkan beberapa kasus penggunaan umum untuk fungsi pemblokiran:

Hanya mengizinkan pendaftaran dari domain tertentu

Contoh berikut menunjukkan cara mencegah pengguna yang bukan bagian dari domain example.com agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (!user.email || user.email.indexOf('@example.com') === -1) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Memblokir pengguna dengan email yang belum diverifikasi untuk mendaftar

Contoh berikut menunjukkan cara mencegah pengguna dengan email yang belum diverifikasi agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unverified email "${user.email}"`);
  }
});

Memerlukan verifikasi email pada pendaftaran

Contoh berikut menunjukkan cara meminta pengguna untuk memverifikasi email mereka setelah mendaftar:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  const locale = context.locale;
  if (user.email && !user.emailVerified) {
    // Send custom email verification on sign-up.
    return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
      return sendCustomVerificationEmail(user.email, link, locale);
    });
  }
});

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
 if (user.email && !user.emailVerified) {
   throw new functions.auth.HttpsError(
     'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
  }
});

Memperlakukan email penyedia identitas tertentu sebagai terverifikasi

Contoh berikut menunjukkan cara memperlakukan email pengguna dari penyedia identitas tertentu sebagai terverifikasi:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
    return {
      emailVerified: true,
    };
  }
});

Memblokir masuk dari alamat IP tertentu

Contoh berikut cara memblokir masuk dari rentang alamat IP tertentu:

Node.js

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
  if (isSuspiciousIpAddress(context.ipAddress)) {
    throw new functions.auth.HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});

Menyetel klaim kustom dan sesi

Contoh berikut menunjukkan cara menetapkan klaim kustom dan sesi:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'saml.my-provider-id') {
    return {
      // Employee ID does not change so save in persistent claims (stored in
      // Auth DB).
      customClaims: {
        eid: context.credential.claims.employeeid,
      },
      // Copy role and groups to token claims. These will not be persisted.
      sessionClaims: {
        role: context.credential.claims.role,
        groups: context.credential.claims.groups,
      }
    }
  }
});

Melacak alamat IP untuk memantau aktivitas mencurigakan

Anda dapat mencegah pencurian token dengan melacak alamat IP tempat pengguna masuk, dan membandingkannya dengan alamat IP pada permintaan berikutnya. Jika permintaan tampak mencurigakan — misalnya, IP berasal dari wilayah geografis yang berbeda — Anda dapat meminta pengguna untuk masuk lagi.

  1. Gunakan klaim sesi untuk melacak alamat IP yang digunakan pengguna untuk masuk:

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. Saat pengguna mencoba mengakses sumber daya yang memerlukan autentikasi dengan Firebase Authentication, bandingkan alamat IP dalam permintaan dengan IP yang digunakan untuk masuk:

    Node.js

    app.post('/getRestrictedData', (req, res) => {
      // Get the ID token passed.
      const idToken = req.body.idToken;
      // Verify the ID token, check if revoked and decode its payload.
      admin.auth().verifyIdToken(idToken, true).then((claims) => {
        // Get request IP address
        const requestIpAddress = req.connection.remoteAddress;
        // Get sign-in IP address.
        const signInIpAddress = claims.signInIpAddress;
        // Check if the request IP address origin is suspicious relative to
        // the session IP addresses. The current request timestamp and the
        // auth_time of the ID token can provide additional signals of abuse,
        // especially if the IP address suddenly changed. If there was a sudden
        // geographical change in a short period of time, then it will give
        // stronger signals of possible abuse.
        if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) {
          // Suspicious IP address change. Require re-authentication.
          // You can also revoke all user sessions by calling:
          // admin.auth().revokeRefreshTokens(claims.sub).
          res.status(401).send({error: 'Unauthorized access. Please login again!'});
        } else {
          // Access is valid. Try to return data.
          getData(claims).then(data => {
            res.end(JSON.stringify(data);
          }, error => {
            res.status(500).send({ error: 'Server error!' })
          });
        }
      });
    });
    

Menyaring foto pengguna

Contoh berikut menunjukkan cara membersihkan foto profil pengguna:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.photoURL) {
    return isPhotoAppropriate(user.photoURL)
      .then((status) => {
        if (!status) {
          // Sanitize inappropriate photos by replacing them with guest photos.
          // Users could also be blocked from sign-up, disabled, etc.
          return {
            photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
          };
        }
      });
});

Untuk mempelajari lebih lanjut tentang cara mendeteksi dan membersihkan gambar, lihat dokumentasi Cloud Vision .

Mengakses kredensial OAuth penyedia identitas pengguna

Contoh berikut menunjukkan cara mendapatkan token penyegaran untuk pengguna yang masuk dengan Google, dan menggunakannya untuk memanggil API Google Kalender. Token penyegaran disimpan untuk akses offline.

Node.js

const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
  keys.web.client_id,
  keys.web.client_secret
);

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'google.com') {
    // Store the refresh token for later offline use.
    // These will only be returned if refresh tokens credentials are included
    // (enabled by Cloud console).
    return saveUserRefreshToken(
        user.uid,
        context.credential.refreshToken,
        'google.com'
      )
      .then(() => {
        // Blocking the function is not required. The function can resolve while
        // this operation continues to run in the background.
        return new Promise((resolve, reject) => {
          // For this operation to succeed, the appropriate OAuth scope should be requested
          // on sign in with Google, client-side. In this case:
          // https://www.googleapis.com/auth/calendar
          // You can check granted_scopes from within:
          // context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).

          // Set access token/refresh token.
          oAuth2Client.setCredentials({
            access_token: context.credential.accessToken,
            refresh_token: context.credential.refreshToken,
          });
          const calendar = google.calendar('v3');
          // Setup Onboarding event on user's calendar.
          const event = {/** ... */};
          calendar.events.insert({
            auth: oauth2client,
            calendarId: 'primary',
            resource: event,
          }, (err, event) => {
            // Do not fail. This is a best effort approach.
            resolve();
          });
      });
    })
  }
});
,

Jika Anda telah mengupgrade ke Firebase Authentication dengan Identity Platform , Anda dapat memperluas Firebase Authentication menggunakan pemblokiran Cloud Functions .

Fungsi pemblokiran memungkinkan Anda mengeksekusi kode khusus yang mengubah hasil pendaftaran pengguna atau masuk ke aplikasi Anda. Misalnya, Anda dapat mencegah pengguna mengautentikasi jika mereka tidak memenuhi kriteria tertentu, atau memperbarui informasi pengguna sebelum mengembalikannya ke aplikasi klien Anda.

Sebelum kamu memulai

Untuk menggunakan fungsi pemblokiran, Anda harus mengupgrade project Firebase ke Firebase Authentication dengan Identity Platform. Jika Anda belum melakukan upgrade, lakukan terlebih dahulu.

Memahami fungsi pemblokiran

Anda dapat mendaftarkan fungsi pemblokiran untuk dua acara:

  • beforeCreate : Dipicu sebelum pengguna baru disimpan ke database Firebase Authentication, dan sebelum token dikembalikan ke aplikasi klien Anda.

  • beforeSignIn : Dipicu setelah kredensial pengguna diverifikasi, tetapi sebelum Firebase Authentication mengembalikan token ID ke aplikasi klien Anda. Jika aplikasi Anda menggunakan autentikasi multifaktor, fungsi akan terpicu setelah pengguna memverifikasi faktor kedua. Perhatikan bahwa membuat pengguna baru juga memicu beforeSignIn , selain beforeCreate .

Ingatlah hal-hal berikut saat menggunakan fungsi pemblokiran:

  • Fungsi Anda harus merespons dalam 7 detik. Setelah 7 detik, Firebase Authentication mengembalikan kesalahan, dan operasi klien gagal.

  • Kode respons HTTP selain 200 diteruskan ke aplikasi klien Anda. Pastikan kode klien Anda menangani kesalahan apa pun yang dapat dikembalikan oleh fungsi Anda.

  • Fungsi berlaku untuk semua pengguna di proyek Anda, termasuk semua yang ada di penyewa . Firebase Authentication memberikan informasi tentang pengguna ke fungsi Anda, termasuk penyewa tempat mereka berada, sehingga Anda dapat meresponsnya dengan tepat.

  • Menautkan penyedia identitas lain ke akun akan memicu kembali semua fungsi beforeSignIn terdaftar.

  • Otentikasi anonim dan kustom tidak memicu fungsi pemblokiran.

Terapkan dan daftarkan fungsi pemblokiran

Untuk memasukkan kode kustom Anda ke dalam alur autentikasi pengguna, terapkan dan daftarkan fungsi pemblokiran. Setelah fungsi pemblokiran Anda diterapkan dan didaftarkan, kode kustom Anda harus berhasil diselesaikan agar autentikasi dan pembuatan pengguna berhasil.

Terapkan fungsi pemblokiran

Anda menerapkan fungsi pemblokiran dengan cara yang sama seperti Anda menerapkan fungsi apa pun. (lihat halaman Memulai Cloud Functions untuk detailnya). Kesimpulan:

  1. Tulis Cloud Functions yang menangani event beforeCreate , event beforeSignIn , atau keduanya.

    Misalnya, untuk memulai, Anda dapat menambahkan fungsi tanpa operasi berikut ke index.js :

    const functions = require('firebase-functions');
    
    exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
      // TODO
    });
    
    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      // TODO
    });
    

    Contoh di atas telah menghilangkan implementasi logika auth kustom. Lihat bagian berikut untuk mempelajari cara menerapkan fungsi pemblokiran dan skenario Umum untuk contoh spesifik.

  2. Terapkan fungsi Anda menggunakan Firebase CLI:

    firebase deploy --only functions
    

    Anda harus menerapkan kembali fungsi Anda setiap kali Anda memperbaruinya.

Daftarkan fungsi pemblokiran

  1. Buka halaman Setelan Otentikasi Firebase di konsol Firebase.

  2. Pilih tab Fungsi pemblokiran .

  3. Daftarkan fungsi pemblokiran Anda dengan memilihnya dari menu tarik-turun di bawah Sebelum pembuatan akun (beforeCreate) atau Sebelum masuk (beforeSignIn) .

  4. Simpan perubahan Anda.

Mendapatkan informasi pengguna dan konteks

Peristiwa beforeSignIn dan beforeCreate menyediakan objek User dan EventContext yang berisi informasi tentang pengguna yang masuk. Gunakan nilai ini dalam kode Anda untuk menentukan apakah akan mengizinkan operasi untuk dilanjutkan.

Untuk daftar properti yang tersedia di objek User , lihat referensi API UserRecord .

Objek EventContext berisi properti berikut:

Nama Keterangan Contoh
locale Lokal aplikasi. Anda dapat menyetel lokal menggunakan SDK klien, atau dengan meneruskan header lokal di REST API. fr atau sv-SE
ipAddress Alamat IP perangkat tempat pengguna akhir mendaftar atau masuk. 114.14.200.1
userAgent Agen pengguna memicu fungsi pemblokiran. Mozilla/5.0 (X11; Linux x86_64)
eventId Pengidentifikasi unik acara. rWsyPtolplG2TBFoOkkgyg
eventType Jenis acara. Ini memberikan informasi tentang nama acara, seperti beforeSignIn atau beforeCreate , dan metode masuk terkait yang digunakan, seperti Google atau email/sandi. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Selalu USER . USER
resource Proyek atau penyewa Firebase Authentication. projects/ project-id /tenants/ tenant-id
timestamp Waktu peristiwa dipicu, diformat sebagai string RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Sebuah objek yang berisi informasi tentang pengguna. AdditionalUserInfo
credential Objek yang berisi informasi tentang kredensial pengguna. AuthCredential

Memblokir pendaftaran atau masuk

Untuk memblokir upaya pendaftaran atau masuk, lemparkan HttpsError di fungsi Anda. Sebagai contoh:

Node.js

throw new functions.auth.HttpsError('permission-denied');

Tabel berikut mencantumkan kesalahan yang dapat Anda timbulkan, bersama dengan pesan kesalahan defaultnya:

Nama Kode Pesan
invalid-argument 400 Klien menetapkan argumen yang tidak valid.
failed-precondition 400 Permintaan tidak dapat dijalankan dalam status sistem saat ini.
out-of-range 400 Klien menentukan rentang yang tidak valid.
unauthenticated 401 Token OAuth tidak ada, tidak valid, atau kedaluwarsa.
permission-denied 403 Klien tidak memiliki izin yang memadai.
not-found 404 Sumber daya yang ditentukan tidak ditemukan.
aborted 409 Konflik konkurensi, seperti konflik baca-ubah-tulis.
already-exists 409 Sumber daya yang coba dibuat oleh klien sudah ada.
resource-exhausted 429 Entah dari kuota sumber daya atau mencapai pembatasan tingkat.
cancelled 499 Permintaan dibatalkan oleh klien.
data-loss 500 Kehilangan data yang tidak dapat dipulihkan atau kerusakan data.
unknown 500 Kesalahan server tidak dikenal.
internal 500 Kesalahan server dari dalam.
not-implemented 501 Metode API tidak diterapkan oleh server.
unavailable 503 Layanan tidak tersedia.
deadline-exceeded 504 Batas waktu permintaan terlampaui.

Anda juga dapat menentukan pesan kesalahan khusus:

Node.js

throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');

Contoh berikut menunjukkan cara memblokir pengguna yang tidak berada dalam domain tertentu agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  // (If the user is authenticating within a tenant context, the tenant ID can be determined from
  // user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')

  // Only users of a specific domain can sign up.
  if (user.email.indexOf('@acme.com') === -1) {
    throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Terlepas dari apakah Anda menggunakan pesan default atau kustom, Cloud Functions membungkus kesalahan dan mengembalikannya ke klien sebagai kesalahan internal. Sebagai contoh:

throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);

Aplikasi Anda harus menangkap kesalahan, dan menanganinya dengan tepat. Sebagai contoh:

JavaScript

// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
  .then((result) => {
    result.user.getIdTokenResult()
  })
  .then((idTokenResult) => {
    console.log(idTokenResult.claim.admin);
  })
  .catch((error) => {
    if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
      // Display error.
    } else {
      // Registration succeeds.
    }
  });

Memodifikasi pengguna

Alih-alih memblokir upaya pendaftaran atau masuk, Anda dapat mengizinkan operasi untuk melanjutkan, tetapi memodifikasi objek User yang disimpan ke database Firebase Authentication dan dikembalikan ke klien.

Untuk memodifikasi pengguna, kembalikan objek dari event handler Anda yang berisi bidang yang akan dimodifikasi. Anda dapat mengubah bidang berikut:

  • displayName
  • disabled
  • emailVerified
  • photoURL
  • customClaims
  • sessionClaims ( beforeSignIn saja)

Dengan pengecualian sessionClaims , semua bidang yang dimodifikasi disimpan ke database Firebase Authentication, yang berarti mereka disertakan pada token respons dan bertahan di antara sesi pengguna.

Contoh berikut menunjukkan cara menetapkan nama tampilan default:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: user.displayName || 'Guest';
  };
});

Jika Anda mendaftarkan event handler untuk beforeCreate dan beforeSignIn , perhatikan bahwa beforeSignIn dijalankan setelah beforeCreate . Bidang pengguna yang diperbarui di beforeCreate terlihat di beforeSignIn . Jika Anda menyetel bidang selain sessionClaims di kedua pengendali peristiwa, nilai yang disetel di beforeSignIn menimpa nilai yang disetel di beforeCreate . Untuk sessionClaims saja, mereka disebarkan ke klaim token sesi saat ini, tetapi tidak dipertahankan atau disimpan dalam database.

Misalnya, jika ada sessionClaims yang disetel, beforeSignIn akan mengembalikannya dengan klaim beforeCreate , dan mereka akan digabungkan. Saat digabungkan, jika kunci sessionClaims cocok dengan kunci di customClaims , customClaims yang cocok akan ditimpa dalam klaim token oleh kunci sessionClaims . Namun, kunci customClaims yang ditimpa akan tetap ada di database untuk permintaan di masa mendatang.

Kredensial dan data OAuth yang didukung

Anda dapat meneruskan kredensial dan data OAuth ke fungsi pemblokiran dari berbagai penyedia identitas. Tabel berikut menunjukkan kredensial dan data apa yang didukung untuk setiap penyedia identitas:

Penyedia Identitas Token ID Token Akses Waktu kedaluwarsa Rahasia Token Segarkan Token Klaim Masuk
Google Ya Ya Ya Tidak Ya Tidak
Facebook Tidak Ya Ya Tidak Tidak Tidak
Twitter Tidak Ya Tidak Ya Tidak Tidak
GitHub Tidak Ya Tidak Tidak Tidak Tidak
Microsoft Ya Ya Ya Tidak Ya Tidak
LinkedIn Tidak Ya Ya Tidak Tidak Tidak
Yahoo Ya Ya Ya Tidak Ya Tidak
apel Ya Ya Ya Tidak Ya Tidak
SAML Tidak Tidak Tidak Tidak Tidak Ya
OIDC Ya Ya Ya Tidak Ya Ya

Segarkan token

Untuk menggunakan token penyegaran dalam fungsi pemblokiran, Anda harus terlebih dahulu mencentang kotak di halaman Fungsi pemblokiran di Firebase console.

Token penyegaran tidak akan dikembalikan oleh penyedia identitas mana pun saat masuk secara langsung dengan kredensial OAuth, seperti token ID atau token akses. Dalam situasi ini, kredensial OAuth sisi klien yang sama akan diteruskan ke fungsi pemblokiran.

Bagian berikut menjelaskan setiap jenis penyedia identitas serta kredensial dan data yang didukungnya.

Penyedia OIDC generik

Saat pengguna masuk dengan penyedia OIDC generik, kredensial berikut akan diteruskan:

  • Token ID : Disediakan jika aliran id_token dipilih.
  • Token akses : Disediakan jika aliran kode dipilih. Perhatikan bahwa aliran kode saat ini hanya didukung melalui REST API.
  • Segarkan token : Disediakan jika cakupan offline_access dipilih.

Contoh:

const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Google

Saat pengguna masuk dengan Google, kredensial berikut akan diteruskan:

  • tanda pengenal
  • Token akses
  • Segarkan token : Hanya diberikan jika parameter khusus berikut diminta:
    • access_type=offline
    • prompt=consent , jika pengguna sebelumnya menyetujui dan tidak ada ruang lingkup baru yang diminta

Contoh:

const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
  'access_type': 'offline',
  'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);

Pelajari lebih lanjut tentang token penyegaran Google .

Facebook

Saat pengguna masuk dengan Facebook, kredensial berikut akan diteruskan:

  • Token akses : Token akses dikembalikan yang dapat ditukar dengan token akses lain. Pelajari lebih lanjut tentang berbagai jenis token akses yang didukung oleh Facebook dan bagaimana Anda dapat menukarnya dengan token berumur panjang .

GitHub

Saat pengguna masuk dengan GitHub, kredensial berikut akan diteruskan:

  • Token akses : Tidak kedaluwarsa kecuali dicabut.

Microsoft

Saat pengguna masuk dengan Microsoft, kredensial berikut akan diteruskan:

  • tanda pengenal
  • Token akses
  • Segarkan token : Diteruskan ke fungsi pemblokiran jika cakupan offline_access dipilih.

Contoh:

const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Yahoo

Saat pengguna masuk dengan Yahoo, kredensial berikut akan diteruskan tanpa parameter atau cakupan khusus:

  • tanda pengenal
  • Token akses
  • Segarkan token

LinkedIn

Saat pengguna masuk dengan LinkedIn, kredensial berikut akan diteruskan:

  • Token akses

apel

Saat pengguna masuk dengan Apple, kredensial berikut akan diteruskan tanpa parameter atau cakupan khusus:

  • tanda pengenal
  • Token akses
  • Segarkan token

Skenario umum

Contoh berikut menunjukkan beberapa kasus penggunaan umum untuk fungsi pemblokiran:

Hanya mengizinkan pendaftaran dari domain tertentu

Contoh berikut menunjukkan cara mencegah pengguna yang bukan bagian dari domain example.com agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (!user.email || user.email.indexOf('@example.com') === -1) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Memblokir pengguna dengan email yang belum diverifikasi untuk mendaftar

Contoh berikut menunjukkan cara mencegah pengguna dengan email yang belum diverifikasi agar tidak mendaftar ke aplikasi Anda:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unverified email "${user.email}"`);
  }
});

Memerlukan verifikasi email pada pendaftaran

Contoh berikut menunjukkan cara meminta pengguna untuk memverifikasi email mereka setelah mendaftar:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  const locale = context.locale;
  if (user.email && !user.emailVerified) {
    // Send custom email verification on sign-up.
    return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
      return sendCustomVerificationEmail(user.email, link, locale);
    });
  }
});

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
 if (user.email && !user.emailVerified) {
   throw new functions.auth.HttpsError(
     'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
  }
});

Memperlakukan email penyedia identitas tertentu sebagai terverifikasi

Contoh berikut menunjukkan cara memperlakukan email pengguna dari penyedia identitas tertentu sebagai terverifikasi:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
    return {
      emailVerified: true,
    };
  }
});

Memblokir masuk dari alamat IP tertentu

Contoh berikut cara memblokir masuk dari rentang alamat IP tertentu:

Node.js

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
  if (isSuspiciousIpAddress(context.ipAddress)) {
    throw new functions.auth.HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});

Menyetel klaim kustom dan sesi

Contoh berikut menunjukkan cara menetapkan klaim kustom dan sesi:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'saml.my-provider-id') {
    return {
      // Employee ID does not change so save in persistent claims (stored in
      // Auth DB).
      customClaims: {
        eid: context.credential.claims.employeeid,
      },
      // Copy role and groups to token claims. These will not be persisted.
      sessionClaims: {
        role: context.credential.claims.role,
        groups: context.credential.claims.groups,
      }
    }
  }
});

Melacak alamat IP untuk memantau aktivitas mencurigakan

Anda dapat mencegah pencurian token dengan melacak alamat IP tempat pengguna masuk, dan membandingkannya dengan alamat IP pada permintaan berikutnya. Jika permintaan tampak mencurigakan — misalnya, IP berasal dari wilayah geografis yang berbeda — Anda dapat meminta pengguna untuk masuk lagi.

  1. Gunakan klaim sesi untuk melacak alamat IP yang digunakan pengguna untuk masuk:

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. Saat pengguna mencoba mengakses sumber daya yang memerlukan autentikasi dengan Firebase Authentication, bandingkan alamat IP dalam permintaan dengan IP yang digunakan untuk masuk:

    Node.js

    app.post('/getRestrictedData', (req, res) => {
      // Get the ID token passed.
      const idToken = req.body.idToken;
      // Verify the ID token, check if revoked and decode its payload.
      admin.auth().verifyIdToken(idToken, true).then((claims) => {
        // Get request IP address
        const requestIpAddress = req.connection.remoteAddress;
        // Get sign-in IP address.
        const signInIpAddress = claims.signInIpAddress;
        // Check if the request IP address origin is suspicious relative to
        // the session IP addresses. The current request timestamp and the
        // auth_time of the ID token can provide additional signals of abuse,
        // especially if the IP address suddenly changed. If there was a sudden
        // geographical change in a short period of time, then it will give
        // stronger signals of possible abuse.
        if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) {
          // Suspicious IP address change. Require re-authentication.
          // You can also revoke all user sessions by calling:
          // admin.auth().revokeRefreshTokens(claims.sub).
          res.status(401).send({error: 'Unauthorized access. Please login again!'});
        } else {
          // Access is valid. Try to return data.
          getData(claims).then(data => {
            res.end(JSON.stringify(data);
          }, error => {
            res.status(500).send({ error: 'Server error!' })
          });
        }
      });
    });
    

Menyaring foto pengguna

Contoh berikut menunjukkan cara membersihkan foto profil pengguna:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.photoURL) {
    return isPhotoAppropriate(user.photoURL)
      .then((status) => {
        if (!status) {
          // Sanitize inappropriate photos by replacing them with guest photos.
          // Users could also be blocked from sign-up, disabled, etc.
          return {
            photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
          };
        }
      });
});

Untuk mempelajari lebih lanjut tentang cara mendeteksi dan membersihkan gambar, lihat dokumentasi Cloud Vision .

Mengakses kredensial OAuth penyedia identitas pengguna

Contoh berikut menunjukkan cara mendapatkan token penyegaran untuk pengguna yang masuk dengan Google, dan menggunakannya untuk memanggil API Google Kalender. Token penyegaran disimpan untuk akses offline.

Node.js

const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
  keys.web.client_id,
  keys.web.client_secret
);

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'google.com') {
    // Store the refresh token for later offline use.
    // These will only be returned if refresh tokens credentials are included
    // (enabled by Cloud console).
    return saveUserRefreshToken(
        user.uid,
        context.credential.refreshToken,
        'google.com'
      )
      .then(() => {
        // Blocking the function is not required. The function can resolve while
        // this operation continues to run in the background.
        return new Promise((resolve, reject) => {
          // For this operation to succeed, the appropriate OAuth scope should be requested
          // on sign in with Google, client-side. In this case:
          // https://www.googleapis.com/auth/calendar
          // You can check granted_scopes from within:
          // context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).

          // Set access token/refresh token.
          oAuth2Client.setCredentials({
            access_token: context.credential.accessToken,
            refresh_token: context.credential.refreshToken,
          });
          const calendar = google.calendar('v3');
          // Setup Onboarding event on user's calendar.
          const event = {/** ... */};
          calendar.events.insert({
            auth: oauth2client,
            calendarId: 'primary',
            resource: event,
          }, (err, event) => {
            // Do not fail. This is a best effort approach.
            resolve();
          });
      });
    })
  }
});