Slide 1

Slide 1 text

Bangkok Protect your backends with Firebase App Check Jirawat Karanwittayakarn Google Developer Expert in Firebase

Slide 2

Slide 2 text

Release and Monitor Crashlytics Performance Monitoring Test Lab Engage In-App Messaging Predictions Cloud Messaging Remote Config A/B Testing Dynamic Links Build Auth Cloud Functions Cloud Firestore Cloud Storage Hosting Realtime Database

Slide 3

Slide 3 text

Firebase Security

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

User Data + Security Rules Firebase Auth Firebase Security

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Firebase App Check

Slide 8

Slide 8 text

Firebase Google Cloud API Endpoints

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Firebase Resources Cloud Firestore Cloud Storage for Firebase Realtime Database Cloud Functions for Firebase

Slide 12

Slide 12 text

App Check Attestation Providers

Slide 13

Slide 13 text

Attestation Providers SafetyNet Device Check reCAPTCHA V3 Custom Attestation New App Attest reCAPTCHA Enterprise New Play Integrity New reCAPTCHA V3 SafetyNet Device Check

Slide 14

Slide 14 text

Only supported on iOS 14+ Device Check App Attest And/Or?

Slide 15

Slide 15 text

To support prior versions to iOS 11, use custom attestation. Device Check Supported on iOS 11+ Only supported on iOS 14+ App Attest And

Slide 16

Slide 16 text

App Check Token and TTL

Slide 17

Slide 17 text

SafetyNet App Check App Attestation Cloud Firestore App Check Resource Access

Slide 18

Slide 18 text

App Check Backend Issues App Check token with TTL in Attestation flow App Check token grants access to Firestore while TTL is valid

Slide 19

Slide 19 text

App Check Backend App Check token exceeds TTL Re-perform Attestation flow to keep valid App Check token Issues App Check token with TTL in Attestation flow

Slide 20

Slide 20 text

App Check Backend Refreshed App Check token with valid TTL Re-perform Attestation flow to keep valid App Check token Issues App Check token with TTL in Attestation flow

Slide 21

Slide 21 text

30 Min 7 Days Short TTL Long TTL Recommend 1 hr for most apps Custom TTL

Slide 22

Slide 22 text

Custom TTL

Slide 23

Slide 23 text

App Check Enforcement

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Secure New Apps Before Launch Migrate Existing Apps

Slide 26

Slide 26 text

App Check Service Access User Data + Security Rules Firebase Auth Better together Firebase Security

Slide 27

Slide 27 text

Custom Backend Integration

Slide 28

Slide 28 text

const express = require('express'); const app = express(); const firebaseAdmin = require('firebase-admin'); const firebaseApp = firebaseAdmin.initializeApp(); const appCheckVerification = async (req, res, next) => { // implementation details next slide. } app.get('/yourApiEndpoint', [appCheckVerification], (req, res) => { // Handle request. }); App Check in the Backend Service

Slide 29

Slide 29 text

const appCheckVerification = async (req, res, next) => { const appCheckToken = req.header('X-Firebase-AppCheck'); if (!appCheckToken) { res.status(401); return next('Unauthorized'); } try { const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken); // If verifyToken() succeeds, continue with the next middleware function return next(); } catch (err) { res.status(401); return next('Unauthorized'); } } App Check in the Backend Service

Slide 30

Slide 30 text

const appCheckVerification = async (req, res, next) => { const appCheckToken = req.header('X-Firebase-AppCheck'); if (!appCheckToken) { res.status(401); return next('Unauthorized'); } try { const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken); // If verifyToken() succeeds, continue with the next middleware function return next(); } catch (err) { res.status(401); return next('Unauthorized'); } } App Check in the Backend Service

Slide 31

Slide 31 text

const appCheckVerification = async (req, res, next) => { const appCheckToken = req.header('X-Firebase-AppCheck'); if (!appCheckToken) { res.status(401); return next('Unauthorized'); } try { const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken); // If verifyToken() succeeds, continue with the next middleware function return next(); } catch (err) { res.status(401); return next('Unauthorized'); } } App Check in the Backend Service

Slide 32

Slide 32 text

const appCheckVerification = async (req, res, next) => { const appCheckToken = req.header('X-Firebase-AppCheck'); if (!appCheckToken) { res.status(401); return next('Unauthorized'); } try { const appCheckClaims = await firebaseAdmin.appCheck().verifyToken(appCheckToken); // If verifyToken() succeeds, continue with the next middleware function return next(); } catch (err) { res.status(401); return next('Unauthorized'); } } App Check in the Backend Service

Slide 33

Slide 33 text

x Custom Backend Resources

Slide 34

Slide 34 text

class ApiWithAppCheckExample { interface YourExampleBackendService { @GET("yourExampleEndpoint") fun exampleData( @Header("X-Firebase-AppCheck") appCheckToken: String ): Call> } var yourExampleBackendService: YourExampleBackendService = Retrofit.Builder() .baseUrl("https://yourbackend.example.com/") .build() .create(YourExampleBackendService::class.java) fun callApiExample() { FirebaseAppCheck.getInstance().getAppCheckToken(false) .addOnSuccessListener { tokenResponse -> val appCheckToken = tokenResponse.token val apiCall = yourExampleBackendService.exampleData(appCheckToken) // ... } } } Custom Backend for Android

Slide 35

Slide 35 text

class ApiWithAppCheckExample { interface YourExampleBackendService { @GET("yourExampleEndpoint") fun exampleData( @Header("X-Firebase-AppCheck") appCheckToken: String ): Call> } var yourExampleBackendService: YourExampleBackendService = Retrofit.Builder() .baseUrl("https://yourbackend.example.com/") .build() .create(YourExampleBackendService::class.java) fun callApiExample() { FirebaseAppCheck.getInstance().getAppCheckToken(false) .addOnSuccessListener { tokenResponse -> val appCheckToken = tokenResponse.token val apiCall = yourExampleBackendService.exampleData(appCheckToken) // ... } } } Custom Backend for Android

Slide 36

Slide 36 text

class ApiWithAppCheckExample { interface YourExampleBackendService { @GET("yourExampleEndpoint") fun exampleData( @Header("X-Firebase-AppCheck") appCheckToken: String ): Call> } var yourExampleBackendService: YourExampleBackendService = Retrofit.Builder() .baseUrl("https://yourbackend.example.com/") .build() .create(YourExampleBackendService::class.java) fun callApiExample() { FirebaseAppCheck.getInstance().getAppCheckToken(false) .addOnSuccessListener { tokenResponse -> val appCheckToken = tokenResponse.token val apiCall = yourExampleBackendService.exampleData(appCheckToken) // ... } } } Custom Backend for Android

Slide 37

Slide 37 text

class ApiWithAppCheckExample { interface YourExampleBackendService { @GET("yourExampleEndpoint") fun exampleData( @Header("X-Firebase-AppCheck") appCheckToken: String ): Call> } var yourExampleBackendService: YourExampleBackendService = Retrofit.Builder() .baseUrl("https://yourbackend.example.com/") .build() .create(YourExampleBackendService::class.java) fun callApiExample() { FirebaseAppCheck.getInstance().getAppCheckToken(false) .addOnSuccessListener { tokenResponse -> val appCheckToken = tokenResponse.token val apiCall = yourExampleBackendService.exampleData(appCheckToken) // ... } } } Custom Backend for Android

Slide 38

Slide 38 text

x Custom Backend Resources

Slide 39

Slide 39 text

AppCheck.appCheck().token(forcingRefresh: false) { token, error in guard error == nil else { return } guard let token = token else { return } let tokenString = token.token let url = URL(string: "https://yourbackend.example.com/yourApiEndpoint")! var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue(tokenString, forHTTPHeaderField: "X-Firebase-AppCheck") let task = URLSession.shared.dataTask(with: request) { data, response, error in // Handle response from your backend. } task.resume() } Custom Backend for iOS

Slide 40

Slide 40 text

AppCheck.appCheck().token(forcingRefresh: false) { token, error in guard error == nil else { return } guard let token = token else { return } let tokenString = token.token let url = URL(string: "https://yourbackend.example.com/yourApiEndpoint")! var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue(tokenString, forHTTPHeaderField: "X-Firebase-AppCheck") let task = URLSession.shared.dataTask(with: request) { data, response, error in // Handle response from your backend. } task.resume() } Custom Backend for iOS

Slide 41

Slide 41 text

AppCheck.appCheck().token(forcingRefresh: false) { token, error in guard error == nil else { return } guard let token = token else { return } let tokenString = token.token let url = URL(string: "https://yourbackend.example.com/yourApiEndpoint")! var request = URLRequest(url: url) request.httpMethod = "GET" request.setValue(tokenString, forHTTPHeaderField: "X-Firebase-AppCheck") let task = URLSession.shared.dataTask(with: request) { data, response, error in // Handle response from your backend. } task.resume() } Custom Backend for iOS

Slide 42

Slide 42 text

x Custom Backend Resources

Slide 43

Slide 43 text

const callApiWithAppCheckExample = async () => { let appCheckTokenResponse; try { appCheckTokenResponse = await firebase.appCheck().getToken(false); } catch (err) { return; } const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', { headers: { 'X-Firebase-AppCheck': appCheckTokenResponse.token, } }); // Handle response from your backend. }; Custom Backend for Web

Slide 44

Slide 44 text

const callApiWithAppCheckExample = async () => { let appCheckTokenResponse; try { appCheckTokenResponse = await firebase.appCheck().getToken(false); } catch (err) { return; } const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', { headers: { 'X-Firebase-AppCheck': appCheckTokenResponse.token, } }); // Handle response from your backend. }; Custom Backend for Web

Slide 45

Slide 45 text

x Custom Backend Resources

Slide 46

Slide 46 text

void callApiExample() async { final appCheckToken = await FirebaseAppCheck.instance.getToken(); if (appCheckToken != null) { final response = await http.get( Uri.parse("https://yourbackend.example.com/yourExampleEndpoint"), headers: {"X-Firebase-AppCheck": appCheckToken}, ); } else { // Error: couldn't get an App Check token. } } Custom Backend for Flutter

Slide 47

Slide 47 text

void callApiExample() async { final appCheckToken = await FirebaseAppCheck.instance.getToken(); if (appCheckToken != null) { final response = await http.get( Uri.parse("https://yourbackend.example.com/yourExampleEndpoint"), headers: {"X-Firebase-AppCheck": appCheckToken}, ); } else { // Error: couldn't get an App Check token. } } Custom Backend for Flutter

Slide 48

Slide 48 text

void callApiExample() async { final appCheckToken = await FirebaseAppCheck.instance.getToken(); if (appCheckToken != null) { final response = await http.get( Uri.parse("https://yourbackend.example.com/yourExampleEndpoint"), headers: {"X-Firebase-AppCheck": appCheckToken}, ); } else { // Error: couldn't get an App Check token. } } Custom Backend for Flutter

Slide 49

Slide 49 text

Apigee Integration

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Demo

Slide 54

Slide 54 text

Follow us Youtube.com/FirebaseThailand Fb.com/FirebaseThailand Fb.com/groups/FirebaseDevTh Medium.com/FirebaseThailand