Account linking with Google Sign-In (Dialogflow)

Google Sign-In for the Assistant provides the simplest and easiest user experience to users and developers both for account linking and account creation. Your Action can request access to your user's Google profile during a conversation, including the user's name, email address, and profile picture.

The profile information can be used to create a personalized user experience in your Action. If you have apps on other platforms and they use Google Sign-In, you can also find and link to an existing user's account, create a new account, and establish a direct channel of communication to the user.

To perform account linking with Google Sign-In, you ask the user to give consent to access their Google profile. You then use the information in their profile, for example their email address, to identify the user in your system.

Follow the steps in the following sections to add Google Sign-In account linking to your Action.

Configure the project

To configure your project to use Google Sign-In account linking, follow these steps:

  1. Open the Actions Console and select a project.
  2. Click the Developtab and choose Account linking.
  3. Enable the switch next to Account linking.
  4. In the Account creationsection, select Yes .
  5. In Linking type, select Google Sign In .

  6. Open Client Informationand take note of the value of Client ID issued by Google to your Actions.

  7. Click Save.

Start the authentication flow

Use the Account Sign-in helper intent to start the authentication flow.

After the user authorizes your action to access their Google profile, you will receive a Google ID token that contains the user's Google profile information in every subsequent request to your action.

To access the user's profile information, you need to first validate and decode the token by doing the following:

  1. Use a JWT-decoding library for your language to decode the token, and use Google's public keys (available in JWK or PEM format) to verify the token's signature.
  2. Verify that the token's issuer ( iss field in the decoded token) is https://accounts.google.com and that the audience ( aud field in the decoded token) is the value of Client ID issued by Google to your Actions, which is assigned to your project in the Actions on Google console.

The following is an example of a decoded token:

 { 
  
 "sub" 
 : 
  
 1234567890 
 , 
  
 // The unique ID of the user's Google Account 
  
 "iss" 
 : 
  
 "https://accounts.google.com" 
 , 
  
 // The token's issuer 
  
 "aud" 
 : 
  
 "123-abc.apps.googleusercontent.com" 
 , 
  
 // Client ID assigned to your Actions project 
  
 "iat" 
 : 
  
 233366400 
 , 
  
 // Unix timestamp of the token's creation time 
  
 "exp" 
 : 
  
 233370000 
 , 
  
 // Unix timestamp of the token's expiration time 
  
 "name" 
 : 
  
 "Jan Jansen" 
 , 
  
 "given_name" 
 : 
  
 "Jan" 
 , 
  
 "family_name" 
 : 
  
 "Jansen" 
 , 
  
 "email" 
 : 
  
 "jan@gmail.com" 
 , 
  
 // If present, the user's email address 
  
 "locale" 
 : 
  
 "en_US" 
 } 

If you use the Actions on Google client library for Node.js or the Java client library , it takes care of validating and decoding the token for you, and gives you access to the profile content, as shown in the following code snippets. Note that the JSON below describes a webhook request for Dialogflow and Actions SDK respectively.

The following snippets use Dialogflow for sign-in:

The following snippets use Actions SDK for sign-in:

Handle data access requests

To handle data access request, just verify that the user asserted by the Google ID token is already present in your database. The following snippet of code shows an example of how to check if a user account already exists in a Firestore database.

Node.js
 const 
  
 admin 
  
 = 
  
 require 
 ( 
 'firebase-admin' 
 ); 
 const 
  
 functions 
  
 = 
  
 require 
 ( 
 'firebase-functions' 
 ); 
 admin 
 . 
 initializeApp 
 (); 
 const 
  
 auth 
  
 = 
  
 admin 
 . 
 auth 
 (); 
 const 
  
 db 
  
 = 
  
 admin 
 . 
 firestore 
 (); 
 // 
  
 Save 
  
 the 
  
 user 
  
 in 
  
 the 
  
 Firestore 
  
 DB 
  
 after 
  
 successful 
  
 signin 
 app 
 . 
 intent 
 ( 
 'Get Sign In' 
 , 
  
 async 
  
 ( 
 conv 
 , 
  
 params 
 , 
  
 signin 
 ) 
  
 => 
  
 { 
  
 if 
  
 ( 
 signin 
 . 
 status 
  
 !== 
  
 'OK' 
 ) 
  
 { 
  
 return 
  
 conv 
 . 
 close 
 ( 
 ` 
 Let 
 's try again next time.`); 
  
 } 
  
 const 
  
 color 
  
 = 
  
 conv 
 . 
 data 
 [ 
 Fields 
 . 
 COLOR 
 ]; 
  
 const 
  
 { 
 email 
 } 
  
 = 
  
 conv 
 . 
 user 
 ; 
  
 if 
  
 ( 
 ! 
 conv 
 . 
 data 
 . 
 uid 
  
 && 
  
 email 
 ) 
  
 { 
  
 try 
  
 { 
  
 conv 
 . 
 data 
 . 
 uid 
  
 = 
  
 ( 
 await 
  
 auth 
 . 
 getUserByEmail 
 ( 
 email 
 )) 
 . 
 uid 
 ; 
  
 } 
  
 catch 
  
 ( 
 e 
 ) 
  
 { 
  
 if 
  
 ( 
 e 
 . 
 code 
  
 !== 
  
 'auth/user-not-found' 
 ) 
  
 { 
  
 throw 
  
 e 
 ; 
  
 } 
  
 // 
  
 If 
  
 the 
  
 user 
  
 is 
  
 not 
  
 found 
 , 
  
 create 
  
 a 
  
 new 
  
 Firebase 
  
 auth 
  
 user 
  
 // 
  
 using 
  
 the 
  
 email 
  
 obtained 
  
 from 
  
 the 
  
 Google 
  
 Assistant 
  
 conv 
 . 
 data 
 . 
 uid 
  
 = 
  
 ( 
 await 
  
 auth 
 . 
 createUser 
 ({ 
 email 
 })) 
 . 
 uid 
 ; 
  
 } 
  
 } 
  
 if 
  
 ( 
 conv 
 . 
 data 
 . 
 uid 
 ) 
  
 { 
  
 conv 
 . 
 user 
 . 
 ref 
  
 = 
  
 db 
 . 
 collection 
 ( 
 'users' 
 ) 
 . 
 doc 
 ( 
 conv 
 . 
 data 
 . 
 uid 
 ); 
  
 } 
  
 conv 
 . 
 close 
 ( 
 ` 
 I 
  
 saved 
  
 $ 
 { 
 color 
 } 
  
 as 
  
 your 
  
 favorite 
  
 color 
  
 for 
  
 next 
  
 time 
 . 
 ` 
 ); 
 }); 
 // 
  
 Retrieve 
  
 the 
  
 user 
 's favorite color if an account exists, ask if it doesn' 
 t 
 . 
 app 
 . 
 intent 
 ( 
 'Default Welcome Intent' 
 , 
  
 async 
  
 ( 
 conv 
 ) 
  
 => 
  
 { 
  
 const 
  
 { 
 payload 
 } 
  
 = 
  
 conv 
 . 
 user 
 . 
 profile 
 ; 
  
 const 
  
 name 
  
 = 
  
 payload 
  
 ? 
  
 ` 
  
 $ 
 { 
 payload 
 . 
 given_name 
 } 
 ` 
  
 : 
  
 '' 
 ; 
  
 conv 
 . 
 ask 
 ( 
 ` 
 Hi 
 $ 
 { 
 name 
 } 
 ! 
 ` 
 ); 
  
 // 
  
 conv 
 . 
 user 
 . 
 ref 
  
 contains 
  
 the 
  
 id 
  
 of 
  
 the 
  
 record 
  
 for 
  
 the 
  
 user 
  
 in 
  
 a 
  
 Firestore 
  
 DB 
  
 if 
  
 ( 
 conv 
 . 
 user 
 . 
 ref 
 ) 
  
 { 
  
 const 
  
 doc 
  
 = 
  
 await 
  
 conv 
 . 
 user 
 . 
 ref 
 . 
 get 
 (); 
  
 if 
  
 ( 
 doc 
 . 
 exists 
 ) 
  
 { 
  
 const 
  
 color 
  
 = 
  
 doc 
 . 
 data 
 ()[ 
 Fields 
 . 
 COLOR 
 ]; 
  
 return 
  
 conv 
 . 
 ask 
 ( 
 ` 
 Your 
  
 favorite 
  
 color 
  
 was 
  
 $ 
 { 
 color 
 } 
 . 
  
 ` 
  
 + 
  
 'Tell me a color to update it.' 
 ); 
  
 } 
  
 } 
  
 conv 
 . 
 ask 
 ( 
 ` 
 What 
 's your favorite color?`); 
 }); 
Java
 private 
  
 class 
  
 FirestoreManager 
  
 { 
  
 private 
  
 final 
  
 Firestore 
  
 db 
 ; 
  
 private 
  
 final 
  
 DocumentReference 
  
 userDocRef 
 ; 
  
 private 
  
 final 
  
 String 
  
 uid 
 ; 
  
 public 
  
 FirestoreManager 
 ( 
 String 
  
 databaseUrl 
 , 
  
 String 
  
 email 
 ) 
  
 throws 
  
 IOException 
 , 
  
 FirebaseAuthException 
  
 { 
  
 if 
  
 ( 
 FirebaseApp 
 . 
 getApps 
 () 
 . 
 isEmpty 
 ()) 
  
 { 
  
 // 
  
 Use 
  
 the 
  
 application 
  
 default 
  
 credentials 
  
 ( 
 works 
  
 on 
  
 GCP 
  
 based 
  
 hosting 
 ) 
 . 
  
 FirebaseOptions 
  
 options 
  
 = 
  
 new 
  
 FirebaseOptions 
 . 
 Builder 
 () 
  
 . 
 setCredentials 
 ( 
 GoogleCredentials 
 . 
 getApplicationDefault 
 ()) 
  
 . 
 setDatabaseUrl 
 ( 
 databaseUrl 
 ) 
  
 . 
 build 
 (); 
  
 FirebaseApp 
 . 
 initializeApp 
 ( 
 options 
 ); 
  
 } 
  
 this 
 . 
 db 
  
 = 
  
 FirestoreClient 
 . 
 getFirestore 
 (); 
  
 UserRecord 
  
 userRecord 
 ; 
  
 try 
  
 { 
  
 userRecord 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 () 
 . 
 getUserByEmail 
 ( 
 email 
 ); 
  
 } 
  
 catch 
  
 ( 
 FirebaseAuthException 
  
 e 
 ) 
  
 { 
  
 if 
  
 ( 
 e 
 . 
 getErrorCode 
 () 
  
 == 
  
 FIREBASE_USER_NOT_FOUND_ERROR 
 ) 
  
 { 
  
 UserRecord 
 . 
 CreateRequest 
  
 createRequest 
  
 = 
  
 new 
  
 UserRecord 
 . 
 CreateRequest 
 () 
 . 
 setEmail 
 ( 
 email 
 ); 
  
 userRecord 
  
 = 
  
 FirebaseAuth 
 . 
 getInstance 
 () 
 . 
 createUser 
 ( 
 createRequest 
 ); 
  
 } 
  
 else 
  
 { 
  
 throw 
  
 e 
 ; 
  
 } 
  
 } 
  
 uid 
  
 = 
  
 userRecord 
 . 
 getUid 
 (); 
  
 userDocRef 
  
 = 
  
 db 
 . 
 collection 
 ( 
 FIRESTORE_USERS_PATH 
 ) 
 . 
 document 
 ( 
 uid 
 ); 
  
 } 
  
 public 
  
 String 
  
 readUserColor 
 () 
  
 throws 
  
 ExecutionException 
 , 
  
 InterruptedException 
  
 { 
  
 ApiFuture<DocumentSnapshot> 
  
 future 
  
 = 
  
 userDocRef 
 . 
 get 
 (); 
  
 // 
  
 future 
 . 
 get 
 () 
  
 blocks 
  
 on 
  
 response 
  
 DocumentSnapshot 
  
 document 
  
 = 
  
 future 
 . 
 get 
 (); 
  
 if 
  
 ( 
 document 
 . 
 exists 
 ()) 
  
 { 
  
 return 
  
 document 
 . 
 get 
 ( 
 COLOR_KEY 
 ) 
 . 
 toString 
 (); 
  
 } 
  
 else 
  
 { 
  
 return 
  
 "" 
 ; 
  
 } 
  
 } 
  
 public 
  
 Timestamp 
  
 writeUserColor 
 ( 
 String 
  
 color 
 ) 
  
 throws 
  
 ExecutionException 
 , 
  
 InterruptedException 
  
 { 
  
 Map<String 
 , 
  
 Object 
>  
 docData 
  
 = 
  
 new 
  
 HashMap 
<> (); 
  
 docData 
 . 
 put 
 ( 
 COLOR_KEY 
 , 
  
 color 
 ); 
  
 ApiFuture<WriteResult> 
  
 future 
  
 = 
  
 userDocRef 
 . 
 set 
 ( 
 docData 
 ); 
  
 // 
  
 future 
 . 
 get 
 () 
  
 blocks 
  
 on 
  
 response 
  
 return 
  
 future 
 . 
 get 
 () 
 . 
 getUpdateTime 
 (); 
  
 } 
 } 
 @ 
 ForIntent 
 ( 
 "Get Sign In" 
 ) 
 public 
  
 ActionResponse 
  
 getSignIn 
 ( 
 ActionRequest 
  
 request 
 ) 
  
 { 
  
 LOGGER 
 . 
 info 
 ( 
 "Get sign in intent start." 
 ); 
  
 ResponseBuilder 
  
 responseBuilder 
  
 = 
  
 getResponseBuilder 
 ( 
 request 
 ); 
  
 if 
  
 ( 
 request 
 . 
 isSignInGranted 
 ()) 
  
 { 
  
 String 
  
 color 
  
 = 
  
 request 
 . 
 getConversationData 
 () 
 . 
 get 
 ( 
 COLOR_KEY 
 ) 
 . 
 toString 
 (); 
  
 GoogleIdToken 
 . 
 Payload 
  
 profile 
  
 = 
  
 getUserProfile 
 ( 
 request 
 . 
 getUser 
 () 
 . 
 getIdToken 
 ()); 
  
 try 
  
 { 
  
 FirestoreManager 
  
 firestoreManager 
  
 = 
  
 new 
  
 FirestoreManager 
 ( 
 DATABASE_URL 
 , 
  
 profile 
 . 
 getEmail 
 ()); 
  
 saveColor 
 ( 
 firestoreManager 
 , 
  
 color 
 ); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 LOGGER 
 . 
 error 
 ( 
 e 
 . 
 toString 
 ()); 
  
 } 
  
 responseBuilder 
  
 . 
 add 
 ( 
 "I saved " 
  
 + 
  
 color 
  
 + 
  
 " as your favorite color for next time." 
 ) 
  
 . 
 endConversation 
 (); 
  
 } 
  
 else 
  
 { 
  
 responseBuilder 
 . 
 add 
 ( 
 "Let's try again next time" 
 ); 
  
 } 
  
 LOGGER 
 . 
 info 
 ( 
 "Get sign in intent end." 
 ); 
  
 return 
  
 responseBuilder 
 . 
 build 
 (); 
 } 
 private 
  
 void 
  
 saveColor 
 ( 
 FirestoreManager 
  
 firestoreManager 
 , 
  
 String 
  
 color 
 ) 
  
 { 
  
 try 
  
 { 
  
 Timestamp 
  
 updateTime 
  
 = 
  
 firestoreManager 
 . 
 writeUserColor 
 ( 
 color 
 ); 
  
 LOGGER 
 . 
 info 
 ( 
 String 
 . 
 format 
 ( 
 "Update time: 
 %s 
 " 
 , 
  
 updateTime 
 . 
 toString 
 ())); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 LOGGER 
 . 
 error 
 ( 
 e 
 . 
 toString 
 ()); 
  
 } 
 } 
Design a Mobile Site
View Site in Mobile | Classic
Share by: