Browse Source

Add a handlebar template sample.

Change-Id: I2a61c0128c62ad3dd5d430053b1d5691e8551f21
master
Nicolas Garnier 8 years ago
parent
commit
3af6b1e25a
  1. 4
      README.md
  2. 38
      template-handlebars/README.md
  3. 11
      template-handlebars/firebase.json
  4. 74
      template-handlebars/functions/firebaseUser.js
  5. 40
      template-handlebars/functions/index.js
  6. 13
      template-handlebars/functions/package.json
  7. 58
      template-handlebars/functions/views/layouts/main.handlebars
  8. 11
      template-handlebars/functions/views/user.handlebars
  9. BIN
      template-handlebars/public/firebase-logo.png
  10. 19
      template-handlebars/public/main.css

4
README.md

@ -45,6 +45,10 @@ Only users who pass a valid Firebase ID token as a Bearer token in the `Authoriz
Checking the ID token is done with an ExpressJs middleware that also passes the decoded ID token in the Express request object.
Uses an HTTP trigger.
### [Server-side generated pages w/ Handlebars templating and user sessions](/template-handlebars)
This samples shows how to serve server-side generated HTML pages using the [HandlebarsJs](http://handlebarsjs.com/) templating system and serve User specific content by always passing the Firebase ID token in a `__session` cookie.
### [Google Assistant says ordinal of given number](/assistant-say-number)
This sample shows how to create an action for the Google Home/Assistant using the Actions SDK hosted on Cloud Functions. The sample action asks users to say a number and reads out the ordinal of that number.

38
template-handlebars/README.md

@ -0,0 +1,38 @@
# Server-side generated pages w/ Handlebars templating and user sessions
This samples shows how to serve server-side generated HTML pages using the [HandlebarsJs](http://handlebarsjs.com/) templating system.
It also shows how to serve user specific content by passing the Firebase ID token of the signed-in user in a `__session` cookie.
Checking and decoding the ID token passed in the `__session` cookie is done with an ExpressJs middleware.
Some custom scripts in [functions/views/layouts/main.handlebars] maintain the Firebase ID token in the `__session` cookie.
## Setting up the sample
1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).
1. Enable the **Google** Provider in the **Auth** section.
1. Clone or download this repo and open the `template-handlebars` directory.
1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.
1. Configure the CLI locally by using `firebase use --add` and select your project in the list.
1. Install dependencies locally by running: `cd functions; npm install; cd -`
## Deploy and test
This sample comes with a web-based UI for testing the function. To test it out:
1. Deploy your project using `firebase deploy`
1. Open the app using `firebase open hosting:site`, this will open a browser.
1. Sign in the web app in the browser using Google Sign-In and some user information will be displayed on a server-side generated page.
## Contributing
We'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).
## License
© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.

11
template-handlebars/firebase.json

@ -0,0 +1,11 @@
{
"hosting": {
"public": "public",
"rewrites": [
{
"source":"**",
"function":"app"
}
]
}
}

74
template-handlebars/functions/firebaseUser.js

@ -0,0 +1,74 @@
/**
* 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 admin = require('firebase-admin');
const cookieParser = require('cookie-parser')();
// Express middleware that checks if a Firebase ID Tokens is passed in the `Authorization` HTTP
// header or the `__session` cookie and decodes it.
// 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 = (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
getIdTokenFromRequest(req, res).then(idToken => {
if (idToken) {
addDecodedIdTokenToRequest(idToken, req).then(() => {
next();
});
} else {
next();
}
});
};
/**
* Returns a Promise with the Firebase ID Token if found in the Authorization or the __session cookie.
*/
function getIdTokenFromRequest(req, res) {
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
return Promise.resolve(req.headers.authorization.split('Bearer ')[1]);
}
return new Promise(function(resolve) {
cookieParser(req, res, () => {
if (req.cookies && req.cookies.__session) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
resolve(req.cookies.__session);
} else {
resolve();
}
});
});
}
/**
* Returns a Promise with the Decoded ID Token and adds it to req.user.
*/
function addDecodedIdTokenToRequest(idToken, req) {
return admin.auth().verifyIdToken(idToken).then(decodedIdToken => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
}).catch(error => {
console.error('Error while verifying Firebase ID token:', error);
});
}
exports.validateFirebaseIdToken = validateFirebaseIdToken;

40
template-handlebars/functions/index.js

@ -0,0 +1,40 @@
/**
* 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(functions.config().firebase);
const express = require('express');
const exphbs = require('express-handlebars');
const app = express();
const firebaseUser = require('./firebaseUser');
app.engine('handlebars', exphbs({defaultLayout: 'main'}));
app.set('view engine', 'handlebars');
app.use(firebaseUser.validateFirebaseIdToken);
app.get('/', (req, res) => {
console.log('Signed-in user:', req.user);
res.render('user', {
user: req.user
});
});
// 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);

13
template-handlebars/functions/package.json

@ -0,0 +1,13 @@
{
"name": "template-handlebars-functions",
"description": "Use handlebars templating engine on Firebase hosting with Cloud functions",
"dependencies": {
"cookie-parser": "^1.4.3",
"cors": "^2.8.1",
"express": "^4.14.1",
"express-handlebars": "^3.0.0",
"firebase-admin": "^4.1.2",
"firebase-functions": "^0.5.1",
"handlebars": "^4.0.8"
}
}

58
template-handlebars/functions/views/layouts/main.handlebars

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example App</title>
<!-- Import and configure the Firebase SDK -->
<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->
<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->
<script defer src="/__/firebase/3.9.0/firebase-app.js"></script>
<script defer src="/__/firebase/3.9.0/firebase-auth.js"></script>
<script defer src="/__/firebase/init.js"></script>
<script>
function checkCookie() {
// Checks if it's likely that there is a signed-in Firebase user and the session cookie expired.
// In that case we'll hide the body of the page until it will be reloaded after the cookie has been set.
var hasSessionCookie = document.cookie.indexOf('__session=') !== -1;
var isProbablySignedInFirebase = typeof Object.keys(localStorage).find(function (key) {
return key.startsWith('firebase:authUser')
}) !== 'undefined';
if (!hasSessionCookie && isProbablySignedInFirebase) {
var style = document.createElement('style');
style.id = '__bodyHider';
style.appendChild(document.createTextNode('body{display:none}'));
document.head.appendChild(style);
}
}
checkCookie();
document.addEventListener('DOMContentLoaded', function() {
// Make sure the Firebase ID Token is always passed as a cookie.
firebase.auth().addAuthTokenListener(function (idToken) {
var hadSessionCookie = document.cookie.indexOf('__session=') !== -1;
document.cookie = '__session=' + idToken + ';max-age=' + (idToken ? 3600 : 0);
// If there is a change in the auth state compared to what's in the session cookie we'll reload after setting the cookie.
if ((!hadSessionCookie && idToken) || (hadSessionCookie && !idToken)) {
window.location.reload(true);
} else {
// In the rare case where there was a user but it could not be signed in (for instance the account has been deleted).
// We un-hide the page body.
var style = document.getElementById('__bodyHider');
if (style) {
document.head.removeChild(style);
}
}
});
});
</script>
<link rel="stylesheet" href="/main.css">
</head>
<body>
{{{body}}}
</body>
</html>

11
template-handlebars/functions/views/user.handlebars

@ -0,0 +1,11 @@
{{#if user}}
<h1>Hello {{user.name}}!</h1>
Here is your UID: {{user.uid}}
<br><br>
<button onclick="firebase.auth().signOut()">Sign-out</button>
{{^}}
<h1>Hey there!</h1>
Please sign-in!
<br><br>
<button onclick="firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())">Sign-in with Google</button>
{{/if}}

BIN
template-handlebars/public/firebase-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

19
template-handlebars/public/main.css

@ -0,0 +1,19 @@
/**
* Copyright 2017 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.
*/
html, body {
font-family: 'Roboto', 'Helvetica', sans-serif;
}
Loading…
Cancel
Save