Skip to content

Commit

Permalink
feat(login): add firebase auth w passwd
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelmord committed Jan 29, 2020
1 parent a4628d7 commit fc5d6a9
Show file tree
Hide file tree
Showing 26 changed files with 1,296 additions and 38 deletions.
8 changes: 6 additions & 2 deletions packages/poolbase-app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,9 @@ node_modules/
# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
# public env vars
!.env
# Local dotenv files. We're following the file structure used in
# create-react-app and documented in the Ruby dotenv:
# https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
.env.*local
14 changes: 11 additions & 3 deletions packages/poolbase-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
"description": "",
"main": "index.js",
"scripts": {
"dev": "next src/app",
"dev": "NODE_ENV=development next dev \"src/app\"",
"clean": "rimraf \"../../dist/app\"",
"build-app": "next build \"src/app\"",
"build-app": "NODE_ENV=production next build \"src/app\"",
"build-functions": "tsc --project src/functions",
"build-public": "cpx \"src/public/**/*.*\" \"../../dist/app/public\" -C",
"prebuild-install-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"../../dist/app/functions\" -C",
"build-install-deps": "cd \"../../dist/app/functions\" && yarn install",
"prebuild": "yarn clean",
"build": "yarn build-app && yarn build-functions && yarn build-public && yarn prebuild-install-deps",
"typecheck-app": "tsc --project src/app"
"typecheck-app": "tsc --project src/app",
"typecheck-functions": "tsc --project src/functions",
"type-check": "yarn typecheck-app && yarn typecheck-functions"
},
"keywords": [],
"author": "Andreas Sahle <andreas.sahle@gmail.com> (https://pixelmord.de/)",
Expand All @@ -22,11 +24,17 @@
"node": "10"
},
"dependencies": {
"cookie-session": "1.4.0",
"dotenv": "8.2.0",
"firebase": "^7.7.0",
"firebase-admin": "^8.9.2",
"firebase-functions": "^3.3.0",
"isomorphic-unfetch": "^3.0.0",
"lodash": "^4.17.15",
"next": "^9.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-firebaseui": "^4.1.0",
"theme-ui": "^0.3.1"
},
"devDependencies": {
Expand Down
21 changes: 21 additions & 0 deletions packages/poolbase-app/src/app/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# For variables you need accessible at build time, add the variable to
# next.config.js. For secret values in local development, add the variable
# to .env.local, outside of source control.

# Update these with your Firebase app's values.
FIREBASE_AUTH_DOMAIN=poolbase-123.firebaseapp.com
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-5mj0u@poolbase-123.iam.gserviceaccount.com
FIREBASE_PROJECT_ID=poolbase-123
FIREBASE_PUBLIC_API_KEY=AIzaSyAnbedWWD7dfug3DkkIahChaE2SjDxiaEk

# Create another file in this directory named ".env.local", which you
# should not include in source control. In .env.local, set these secret
# environment variables:

# Your Firebase private key.
# FIREBASE_PRIVATE_KEY=some-key-here

# Secrets used by cookie-session.
# SESSION_SECRET_CURRENT=someSecretValue
# SESSION_SECRET_PREVIOUS=anotherSecretValue
46 changes: 46 additions & 0 deletions packages/poolbase-app/src/app/components/FirebaseAuth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* globals window */
import React, { useEffect, useState } from 'react'
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'
import firebase from 'firebase/app'
import 'firebase/auth'
import initFirebase from '../utils/auth/initFirebase'

// Init the Firebase app.
initFirebase()

const firebaseAuthConfig = {
signInFlow: 'popup',
// Auth providers
// https://github.com/firebase/firebaseui-web#configure-oauth-providers
signInOptions: [
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
requireDisplayName: false,
},
],
signInSuccessUrl: '/',
credentialHelper: 'none',
}

const FirebaseAuth = () => {
// Do not SSR FirebaseUI, because it is not supported.
// https://github.com/firebase/firebaseui-web/issues/213
const [renderAuth, setRenderAuth] = useState(false)
useEffect(() => {
if (typeof window !== 'undefined') {
setRenderAuth(true)
}
}, [])
return (
<div>
{renderAuth ? (
<StyledFirebaseAuth
uiConfig={firebaseAuthConfig}
firebaseAuth={firebase.auth()}
/>
) : null}
</div>
)
}

export default FirebaseAuth
39 changes: 39 additions & 0 deletions packages/poolbase-app/src/app/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Responsible for setting environment variables.
// Note: this isn't strictly required for this example – you can
// inline your Firebase config or set environment variables howevever
// else you wish – but it's a convenient way to make sure the private
// key doesn't end up in source control.

const fs = require('fs');
const path = require('path');

const { NODE_ENV } = process.env
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
)
}

// Set env vars from appropiate `.env` files. We're following the
// file structure used in create-react-app and documented in the
// Ruby dotenv. See:
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
const dotEnvPath = path.resolve(__dirname, '.env');

const dotEnvFiles = [
`${dotEnvPath}.${NODE_ENV}.local`,
`${dotEnvPath}.${NODE_ENV}`,
// Don't include `.env.local` for the test environment.
NODE_ENV !== 'test' && `${dotEnvPath}.local`,
dotEnvPath,
].filter(Boolean)


dotEnvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
// eslint-disable-next-line global-require
require('dotenv').config({
path: dotenvFile,
})
}
})
10 changes: 10 additions & 0 deletions packages/poolbase-app/src/app/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type PropsWithAuthUserInfo = {
AuthUserInfo: {
AuthUser: {
id: string;
email: string;
emailVerified: boolean;
}
token: string;
}
};
10 changes: 10 additions & 0 deletions packages/poolbase-app/src/app/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
'use strict';

require('./env.js');
console.log(process.env.FIREBASE_AUTH_DOMAIN);

module.exports = {
distDir: '../../../../dist/app/functions/next',
// Public, build-time env vars.
// https://nextjs.org/docs#build-time-configuration
env: {
FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FIREBASE_PUBLIC_API_KEY: process.env.FIREBASE_PUBLIC_API_KEY,
},
};
7 changes: 3 additions & 4 deletions packages/poolbase-app/src/app/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import React from 'react';
import App from 'next/app';
import { ThemeProvider, Styled } from 'theme-ui';
import { ThemeProvider } from 'theme-ui';

import Header from '../components/Header';
import theme from '../theme';

class MyApp extends App {

render() {
const { Component, pageProps } = this.props;
return (
<ThemeProvider theme={theme}>
<Header />
<Styled.root as="main">
<main>
<Component {...pageProps} />
</Styled.root>
</main>
</ThemeProvider>
);
}
Expand Down
52 changes: 52 additions & 0 deletions packages/poolbase-app/src/app/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint react/no-danger: 0 */
import React from 'react'
import PropTypes from 'prop-types'
import { get } from 'lodash/object'
import Document, { DocumentContext, Html, Head, Main, NextScript } from 'next/document';

type CustomDocumentProps = {
AuthUserInfo: {
AuthUser: {
id: string;
email: string;
emailVerified: boolean;
}
token: string;
}
};

export default class CustomDocument extends Document<CustomDocumentProps> {
static async getInitialProps(ctx: DocumentContext) {
// Get the AuthUserInfo object. This is set if the server-rendered page
// is wrapped in the `withAuthUser` higher-order component.
const AuthUserInfo = get(ctx, 'myCustomData.AuthUserInfo', null);

const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps, AuthUserInfo };
}
render() {
// Store initial props from request data that we need to use again on
// the client. See:
// https://github.com/zeit/next.js/issues/3043#issuecomment-334521241
// https://github.com/zeit/next.js/issues/2252#issuecomment-353992669
// Alternatively, you could use a store, like Redux.
const { AuthUserInfo } = this.props;
return (
<Html>
<Head>
<script
id="__MY_AUTH_USER_INFO"
type="application/json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(AuthUserInfo, null, 2),
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
37 changes: 37 additions & 0 deletions packages/poolbase-app/src/app/pages/api/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import commonMiddleware from '../../utils/middleware/commonMiddleware'
import { verifyIdToken } from '../../utils/auth/firebaseAdmin'

const handler = (req, res) => {
if (!req.body) {
return res.status(400)
}

const { token } = req.body

// Here, we decode the user's Firebase token and store it in a cookie. Use
// express-session (or similar) to store the session data server-side.
// An alternative approach is to use Firebase's `createSessionCookie`. See:
// https://firebase.google.com/docs/auth/admin/manage-cookies
// Firebase docs:
// "This is a low overhead operation. The public certificates are initially
// queried and cached until they expire. Session cookie verification can be
// done with the cached public certificates without any additional network
// requests."
// However, in a serverless environment, we shouldn't rely on caching, so
// it's possible Firebase's `verifySessionCookie` will make frequent network
// requests in a serverless context.
return verifyIdToken(token)
.then(decodedToken => {
req.session.decodedToken = decodedToken
req.session.token = token
return decodedToken
})
.then(decodedToken => {
return res.status(200).json({ status: true, decodedToken })
})
.catch(error => {
return res.status(500).json({ error })
})
}

export default commonMiddleware(handler)
10 changes: 10 additions & 0 deletions packages/poolbase-app/src/app/pages/api/logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import commonMiddleware from '../../utils/middleware/commonMiddleware'

const handler = (req, res) => {
// Destroy the session.
// https://github.com/expressjs/cookie-session#destroying-a-session
req.session = null
res.status(200).json({ status: true })
}

export default commonMiddleware(handler)
17 changes: 17 additions & 0 deletions packages/poolbase-app/src/app/pages/auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import FirebaseAuth from '../components/FirebaseAuth'

const Auth = () => {
return (
<div>
<p>Sign in</p>
<div>
<FirebaseAuth />
</div>
</div>
)
}

Auth.propTypes = {}

export default Auth
Loading

0 comments on commit fc5d6a9

Please sign in to comment.