[ad_1]
I created a simple login page with FirebaseUI for Web:
<!DOCTYPE html> <html> <head> <!-- meta and title --> <!-- update the version number as needed --> <script defer src="/__/firebase/5.9.2/firebase-app.js"></script> <!-- include only the Firebase features as you need --> <script defer src="/__/firebase/5.9.2/firebase-auth.js"></script> <script defer src="/__/firebase/5.9.2/firebase-firestore.js"></script> <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script> <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" /> <!-- initialize the SDK after all desired features are loaded --> <script defer src="/__/firebase/init.js"></script> <style media="screen" type="text/css"> /* styling */ </style> </head> <body> <div id="message"> <h2>Welcome</h2> <h1>Firebase Login</h1> <div id="info"></div> </div> <div id="firebaseui-auth-container"></div> <p id="load">Firebase SDK Loading…</p> <script> // see https://stackoverflow.com/a/2117523/5168962 function uuidv4() { return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => ( c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4))) ).toString(16) ); } document.addEventListener("DOMContentLoaded", function() { try { let app = firebase.app(); let db = firebase.firestore(); let features = ["auth"].filter( feature => typeof app[feature] === "function" ); document.getElementById( "load" ).innerHTML = `Firebase SDK loaded with ${features.join(", ")}`; firebase.auth().onAuthStateChanged( function(user) { if (user) { // User is signed in. var uid = user.uid; // Generate auth code which we will later use to get our auth_token. let authCode = uuidv4(); // Save the code in our database. db.collection(`auth_codes`) .doc(uid) .set( { code: authCode, uid: uid, created: firebase.firestore.FieldValue.serverTimestamp() }, { merge: true } ); // Send token back to client const urlParams = new URLSearchParams(window.location.search); // State sent by Alexa which we have to return. const state = urlParams.get("state"); // Redirect uri sent by Alexa. const redirect_uri = urlParams.get("redirect_uri"); // Combine all the uri elements. let url = redirect_uri + "?state=" + state + "&code=" + authCode; // Redirect window.location.href = url; } else { // User is signed out, so we show the Firebase UI. // FirebaseUI config. var uiConfig = { signInOptions: [ // Leave the lines as is for the providers you want to offer your users. firebase.auth.EmailAuthProvider.PROVIDER_ID ], callbacks: { // Turn of FirebaseUI redirect. signInSuccessWithAuthResult: function( authResult, redirectUrl ) { return false; } }, credentialHelper: firebaseui.auth.CredentialHelper.NONE }; // Initialize the FirebaseUI Widget using Firebase. var ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start("#firebaseui-auth-container", uiConfig); } }, function(error) { console.log(error); document.getElementById("info").textContent = "Error: " + error; } ); } catch (e) { console.error(e); document.getElementById("load").innerHTML = "Error loading the Firebase SDK, check the console."; } }); </script> </body> </html>
4. Configure Alexa Account Linking
Select Auth Code Grant
and set the Authorization URI
= https://<project_name>.firebaseapp.com and the Access Token URI
= https://us-central1-<project_name>.cloudfunctions.net/auth_token.
5. Use the custom token in the Alexa Skill
Now comes the most important part. This took me hours of googling.
const Alexa = require("ask-sdk-core"); const firebase = require("firebase"); // Required for side-effects require("firebase/auth"); require("firebase/firestore"); // Initialize Cloud Firestore through Firebase var config = { // ... }; firebase.initializeApp(config); var db = firebase.firestore(); const LaunchRequestHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === "LaunchRequest"; }, async handle(handlerInput) { console.log("LaunchRequest"); var speechText = "Please link your account!"; const accessToken = handlerInput.requestEnvelope.context.System.user.accessToken; // Test if user has linked his account. if (!accessToken) { return handlerInput.responseBuilder.speak(speechText).getResponse(); } // Alexa will save our custom token in the access token field, so we can use it here. await firebase .auth() .signInWithCustomToken(accessToken) .catch((error) => { // Handle Errors here. var errorCode = error.code; var errorMessage = error.message; console.log(error.message); return handlerInput.responseBuilder.speak(speechText).getResponse(); }); let user = firebase.auth().currentUser; if (user) { // login successful speechText = `Welcome ${user.displayName}!`; } else { // No user is signed in. speechText = `Welcome, please link your account!`; } // ATTENTION: This is very important. Without this the function will time out. await firebase.auth().signOut(); return handlerInput.responseBuilder.speak(speechText).getResponse(); } };
Please mind the firebase.auth().signOut();
at line 55. Without this, the AWS Lambda will not finish and thus you’ll get an error like Task timed out after 8.01 seconds
.
Once you have set up this structure it’s very easy to develop the rest of the Alexa Skill. I am positively surprised how advanced the Alexa ecosystem is and I am happy to dive deeper. On my developing journey, I will add further articles about my findings and the quirks of Skill development.
Make sure to follow me so you won’t miss any future advancements.
This article has been published from the source link without modifications to the text. Only the deadline has been changed.