-
-
Save boskiv/bff783d52eb3f5ccbd9ba1c176e10870 to your computer and use it in GitHub Desktop.
Firebase function to return an Auth0 custom token
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cors from 'cors'; | |
import Promise from 'bluebird'; | |
import axios from 'axios'; | |
import database from '../database/database.js'; | |
/** | |
* The goal of this function is to mint custom token that can be used to | |
* authenticate to Firebase. | |
* The function will be called through https, and will check the credentials of | |
* the requested user (through the usage of its Auth0 token). If the Auth0 token | |
* checks out, a new Firebase token will be mint and returned | |
**/ | |
const module = { | |
/** | |
* Main entry point for the Firebase function. Will wrap the call in the | |
* required CORS options to make it accessible through cross origin calls. | |
* Will resolve to the customToken needed to authenticate to Firebase as an | |
* admin | |
* | |
* @param {Request} req Request sent to the endpoint | |
* @param {Response} res Response returned by the call | |
* @returns {void} | |
**/ | |
onRequest(req, res) { | |
module.req = req; | |
module.res = res; | |
const wrapCorsRequest = cors({ origin: true }); | |
wrapCorsRequest(req, res, module.resolveToCustomToken); | |
}, | |
/** | |
* Will set the response to an object containing the custom token. This is | |
* were the main logic flow lies. | |
* | |
* @returns {void} | |
**/ | |
resolveToCustomToken() { | |
const originToken = module.getOriginUserToken(); | |
const originEmail = module.getOriginUserEmail(); | |
return module | |
.getAuth0User(originToken) | |
.then(userData => | |
module.assertDataIsFromOriginUser(userData, originEmail) | |
) | |
.then(() => module.mintCustomToken(originEmail)) | |
.then(customToken => { | |
module.res.send({ token: customToken }); | |
}); | |
}, | |
/** | |
* Get all user metadata from Auth0, based on the token specified. | |
* | |
* @param {String} accessToken The Auth0 access token of the user | |
* @returns {Promise} The metadata associated with that user | |
**/ | |
getAuth0User(accessToken) { | |
const url = 'https://algolia.auth0.com/userinfo'; | |
const headers = { Authorization: `Bearer ${accessToken}` }; | |
return axios({ | |
url, | |
headers, | |
}).then(response => response.data); | |
}, | |
/** | |
* Check that the data we got from Auth0 is matching the email we specified | |
* | |
* @param {Object} userData Data coming from Auth0 | |
* @param {String} originEmail Email specified in the request | |
* @returns {Promise} Resolves if they match, rejects otherwise | |
**/ | |
assertDataIsFromOriginUser(userData, originEmail) { | |
// The token is not valid for this user | |
if (userData.email !== originEmail) { | |
return Promise.reject( | |
new Error('Specified token does not match specified email') | |
); | |
} | |
return Promise.resolve(true); | |
}, | |
/** | |
* Mint a new admin token for the specified userName and resolve to it | |
* | |
* @param {String} userName Name of the token owner | |
* @returns {Promise.<String>} Token to authenticate to Firebase as an admin | |
**/ | |
mintCustomToken(userName) { | |
return database.init().then(() => database.generateAuthToken(userName)); | |
}, | |
/** | |
* Return the email sent in the request body | |
* | |
* @return {String} The email sent in the body | |
**/ | |
getOriginUserEmail() { | |
return module.req.body.email; | |
}, | |
/** | |
* Return the token sent in the request body | |
* | |
* @return {String} The token sent in the body | |
**/ | |
getOriginUserToken() { | |
return module.req.body.token; | |
}, | |
}; | |
export default module; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as firebaseAdmin from 'firebase-admin'; | |
import firebase from 'firebase'; | |
import config from '../config/config.js'; | |
import { map, isEmpty } from 'lodash'; | |
const module = { | |
database: null, | |
authId: 'dashboard', | |
/** | |
* Init the Firebase database. | |
* This must be called before calling any other method on this component. | |
* | |
* @returns {Promise.<this>} Resolved to the module properly initiated | |
**/ | |
init() { | |
// Already initiated before | |
if (module.database) { | |
return Promise.resolve(module); | |
} | |
return module.initializeAuthenticatedApp().then(() => { | |
module.database = firebase.database(); | |
return module; | |
}); | |
}, | |
/** | |
* Authenticate and initialize the app | |
* | |
* @returns {Promise} Resolves when successfully authenticated and with the | |
* app initiated | |
**/ | |
initializeAuthenticatedApp() { | |
const firebaseConfig = config.get().firebase; | |
return module.generateAuthToken(module.authId).then(token => { | |
firebase.initializeApp(firebaseConfig); | |
return firebase.auth().signInWithCustomToken(token); | |
}); | |
}, | |
/** | |
* Returns the options object to initialize a firebase admin app. | |
* | |
* @returns {Object} Options object to pass to firebaseAdmin.initializeApp() | |
**/ | |
getAdminCredentials() { | |
const firebaseConfig = config.get().firebase || {}; | |
const serviceAccountConfig = config.get().serviceAccount || {}; | |
return { | |
credential: firebaseAdmin.credential.cert(serviceAccountConfig), | |
databaseURL: firebaseConfig.databaseURL, | |
}; | |
}, | |
/** | |
* Initialize the firebaseAdmin app. Making sure only one app can be | |
* initialized at a time. | |
* | |
* @param {Object} options Options to init the Admin app | |
* @returns {void} | |
**/ | |
initFirebaseAdmin(options) { | |
if (!isEmpty(firebaseAdmin.apps)) { | |
return; | |
} | |
firebaseAdmin.initializeApp(options); | |
}, | |
/** | |
* Generates an auth token | |
* | |
* @param {String} tokenUser The user that will be bound to this token | |
* @returns {Promise.<String>} A custom token to be used for authentication | |
**/ | |
generateAuthToken(tokenUser) { | |
module.initFirebaseAdmin(module.getAdminCredentials()); | |
return firebaseAdmin.auth().createCustomToken(tokenUser); | |
}, | |
/** | |
* Get the list of all elements stored in a Firebase node | |
* | |
* @param {String} nodePath Path to the list of elements in Firebase | |
* @returns {Promise.<Array.<Object>>} List of elements under that node | |
**/ | |
getAll(nodePath) { | |
const rootNode = module.database.ref(`/${nodePath}`); | |
return rootNode.once('value').then(items => | |
map(items.val(), (item, key) => ({ | |
...item, | |
objectID: key, | |
})) | |
); | |
}, | |
/** | |
* Pushes the list of items to the specified node | |
* | |
* @param {String} nodePath Firebase database path where to add new items | |
* @param {Array.<Object>} items Items to add to the specified path | |
* @returns {Promise.<Array.<Object>>} The list of items initially passed | |
**/ | |
push(nodePath, items) { | |
const rootNode = module.database.ref(`/${nodePath}`); | |
return Promise.all(map(items, item => rootNode.push(item))).then( | |
() => items | |
); | |
}, | |
/** | |
* Update a specified list of path with values | |
* Each key of the `updates` key should be the objectID of the element, and | |
* each value an object of each fields to update | |
* | |
* @param {String} nodePath Firebase database path where to update items | |
* @param {Object} updates Object of path and values to update | |
* @returns {Promise.<Object>} The list of updates initially passed | |
**/ | |
update(nodePath, updates) { | |
const rootNode = module.database.ref(`/${nodePath}`); | |
return rootNode.update(updates).then(() => updates); | |
}, | |
/** | |
* Utility method for testing purpose. Allow clearing the internal cache to be | |
* able to call .init() several times | |
* | |
* @returns {void} | |
**/ | |
tearDown() { | |
module.database = null; | |
}, | |
}; | |
export default module; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment