Skip to content

Instantly share code, notes, and snippets.

@talk2MeGooseman
Last active November 16, 2022 15:24
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save talk2MeGooseman/ff6e13629b48ef2ac603341c33d56f8c to your computer and use it in GitHub Desktop.
Save talk2MeGooseman/ff6e13629b48ef2ac603341c33d56f8c to your computer and use it in GitHub Desktop.
Firebase Cloud Function 3rd Party Oauth Flow For The Web
const functions = require('firebase-functions');
var admin = require("firebase-admin");
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
var serviceAccount = require("./service-account.json");
const APP_NAME = "twitch-playground";
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://twitch-playground.firebaseio.com"
});
const OAUTH_REDIRECT_URI = `https://localhost:5000/popup.html`;
const OAUTH_SCOPES = 'user_read';
/**
* Creates a configured simple-oauth2 client for Twitch.
*/
function twitchOAuth2Client() {
// Twitch OAuth 2 setup
// TODO: Configure the `twitch.client_id` and `twitch.client_secret` Google Cloud environment variables.
const credentials = {
client: {
id: functions.config().twitch.client_id,
secret: functions.config().twitch.client_secret
},
auth: {
tokenHost: 'https://api.twitch.tv/kraken/',
tokenPath: '/oauth2/token'
}
};
return require('simple-oauth2').create(credentials);
}
/**
* Redirects the User to the Twitch authentication consent screen. Also the 'state' cookie is set for later state
* verification.
*/
exports.redirect = functions.https.onRequest((req, res) => {
const oauth2 = twitchOAuth2Client();
cookieParser()(req, res, () => {
const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
console.log('Setting verification state:', state);
res.cookie('state', state.toString(), {maxAge: 3600000, secure: true, httpOnly: true});
const redirectUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: OAUTH_REDIRECT_URI,
scope: OAUTH_SCOPES,
state: state
});
console.log('Redirecting to:', redirectUri);
res.redirect(redirectUri);
});
});
/**
* Exchanges a given Twitch auth code passed in the 'code' URL query parameter for a Firebase auth token.
* The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.
* The Firebase custom auth token, display name, photo URL and Twitch acces token are sent back in a JSONP callback
* function with function name defined by the 'callback' query parameter.
*/
exports.token = functions.https.onRequest((req, res) => {
const oauth2 = twitchOAuth2Client();
try {
cookieParser()(req, res, () => {
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
oauth2.authorizationCode.getToken({
code: req.query.code,
redirect_uri: OAUTH_REDIRECT_URI
}).then(results => {
console.log('Auth code exchange result received:', results);
// We have an Twitch access token and the user identity now.
const accessToken = results.access_token;
const twitchUserID = 1;
const profilePic = 'www.google.com';
const userName = 'need to do';
// Create a Firebase account and get the Custom Auth Token.
createFirebaseAccount(twitchUserID, userName, profilePic, accessToken).then(firebaseToken => {
// Serve an HTML page that signs the user in and updates the user profile.
res.jsonp({token: firebaseToken});
});
});
});
} catch (error) {
return res.jsonp({error: error.toString});
}
});
/**
* Creates a Firebase account with the given user profile and returns a custom auth token allowing
* signing-in this account.
* Also saves the accessToken to the datastore at /twitchAccessToken/$uid
*
* @returns {Promise<string>} The Firebase custom auth token in a promise.
*/
function createFirebaseAccount(twitchID, displayName, photoURL, accessToken) {
// The UID we'll assign to the user.
const uid = `twitch:${twitchID}`;
// Save the access token tot he Firebase Realtime Database.
const databaseTask = admin.database().ref(`/twitchAccessToken/${uid}`)
.set(accessToken);
// Create or update the user account.
const userCreationTask = admin.auth().updateUser(uid, {
displayName: displayName,
photoURL: photoURL
}).catch(error => {
// If user does not exists we create it.
if (error.code === 'auth/user-not-found') {
return admin.auth().createUser({
uid: uid,
displayName: displayName,
photoURL: photoURL
});
}
throw error;
});
// Wait for all async task to complete then generate and return a custom auth token.
return Promise.all([userCreationTask, databaseTask]).then(() => {
// Create a Firebase custom auth token.
return admin.auth().createCustomToken(uid).then((token) => {
console.log('Created Custom token for UID "', uid, '" Token:', token);
return token;
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment