Skip to content
Open

Auth #73

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
##########################################
# Don't worry these are all dummy values #
##########################################

# Token for Github
GH_TOKEN=ghp_7mc40X3r56mB9UQtqL1i148oruieBvaH20P4R50

# Your website's domain with protocol (https/http)
NEXT_PUBLIC_VERCEL_URL=https://shreshth.dev

# Which GitHub repo to pull issues from
GH_USER_REPO=shreshthmohan/next-blog




FIREBASE_CLIENT_EMAIL=firebase-adminsdk-ed23a@next-blog-auth-4d67d.iam.gserviceaccount.com
NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY=AIzaSyAPcyOVyJKiKLrxJn_flq-QY0ZeCOC6Xty
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=next-blog-auth-4d67d.firebaseapp.com
NEXT_PUBLIC_FIREBASE_DATABASE_URL=https://next-blog-auth-4d67d.firebaseio.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=next-blog-auth-4d67d

# Your Firebase private key.
# When using Vercel, add the private key with double quotes
# via the CLI, not the Vercel dashboard:
# vercel secrets add firebase-private-key '"my-key-here"'
# Then, use `JSON.parse` in the app. See:
# https://github.com/vercel/vercel/issues/749#issuecomment-707515089
FIREBASE_PRIVATE_KEY='"-----BEGIN PRIVATE KEY-----\nMIIEvAIB430972ryuweihfjdksy54qw0BAQEFAASCBKYwggSiAgEAAoIBAQDJ64DDuka+89oD\nsLgldKTx0fwEBnjDuXX+vBuar8pd0byy+Qp2T2UxKFrasYMZZgVwvzJAeokp0oQG\nSlOoHTbsh/B+uZq6MbrE6HdqC39zRu+AjxIJrqln9/gKdLBMpdHqwiLeP4rzua6P\n10AYRP1aa6slFctiue6dYm9jEzPwK1mQyrsE3RBinbH5sHPWlGp42+LIivD9CQCl\ndjK9yeO8aqTVzoHUaaoofrpbRJtaE/jCGEyz4r51yJvUayQWo5i6idGKMB7WYFlI\n69M0MqkYd/Y8vQb5p53q7+UzJd8BICvpfgMWpNiiyhnZwRKKnh+GV36TJg0feJ+8\nG5dRTmAtAgMBAAECggEAAKlz7wPNqPWbGzYit+HRFKqOOUoGHPKMfgpUdhyLGMPe\nyXJPNLtFUabVbrA+oZpHJWsRTHAs8niV918Ur7SSjR11MzYT6giTaSZ58MDLNwjj\n6ootEHCgZpYKU4MWEVTsYOyxiOqajhDTNjFUIZABJYraJg00s9tAh4yYKTtVJQIz\nrY1ifJ5yCYagghbWz0YdKh86bIMiJnCzcNBpv75F2JVyBpRu5x2Gg10nSMKCjDEZ\nc1DGlEG0UzEF6dft4L+7sd10n23txSY4Q4WQvpOZM6UJp88MrVdgfVohsu+yjx0L\noR9cB/3mKO808DCLuUE6IGpfGNUYiNaDsTEYvIx64QKBgQDkx/mF5N/bs3ZKWQy4\nco5Z1WB9/wco+aSItPIjzg6HkHtP/wlZQPkiJQ9yXAVPel0TLguoQdMB3Sq9lFnH\nTxPWNRjXiaksA8oYg7uLR6KiapRVlQbkvVfJvwI1OzLQ+2KKgWrm6X8FB3oELhiD\nkXHkYrZ1BfUW/nkwaqGUik7yFwKBgQDh8WloNAQOlnY3P74D9bZPodb2FuQCybNS\n56QYa8S50jgRmMd3p5VX19RurhXG/yaSZgQq87oUJ5a/wZow3gaqX7xqKj+uowE6\nRi/BrRYpbc+ieoYZRfzB9u5nVtr2BNScizJFSljMJ6NNPK3XtRqpo7nu8Dmxt8CQ\njJCUtgF+WwKBgE/O6t6ofucXbbZ15hgZ7kqsQuLxKkBDBgCijq2q3iqwXjQD1fEK\n113v67mLHFcjaoCcWXiyrbdCvfwwWjlK/rKFB0t5PEiccc2ndq8ZqERcRa6tNCBr\nZMp+FXkYU5vPdgq3JuGypprMhuYaZnbPMBnpzZh5IYyJ7SDUsdUtgmi1AoGAccwV\nOtZ39KDziYAhQ/1NhW4NxoRg8saD+w2QKHye6LhoZPR+AlX1cfjSlaw4a7G2y6V2\nE+wNnHkUBCCOeG5bDRSK4S3GRT70L1WKWBHFR7h3C26Ke9A0Lb0g9gtY3PAx5WZ2\nO0/myWxtY0lchXhBpY9A5oc3h7r00dj6OOwydGECgYAOGuIe0Bt/7XwOWK7bq+iG\nQJdl0cEPmz5GKNfFXu/FhyI5AlsQdYrF3EsFlh/Q5bZQ9TwmP3ZRsL+b/cJkzyW2\npse/r8oqPIseBw1m8ZQ2EE+JxbpaKrSFbBAzrkyXLJg+KBaJsIZZNO7jqgT1V6oP\nk6KWo/x29DLJPjUnIopNiA==\n-----END PRIVATE KEY-----\n"'

# Secrets used to sign cookies.
COOKIE_SECRET_CURRENT=xG$WVv6qr574irew
COOKIE_SECRET_PREVIOUS=2x7#msoVe5849ruej

# Cookie options.
NEXT_PUBLIC_COOKIE_SECURE=false # set to true in HTTPS environment
52 changes: 52 additions & 0 deletions components/FirebaseAuth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* globals window */
import React, { useEffect, useState } from 'react'
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'
import firebase from 'firebase/app'
import 'firebase/auth'

// Note that next-firebase-auth inits Firebase for us,
// so we don't need to.

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',
callbacks: {
// https://github.com/firebase/firebaseui-web#signinsuccesswithauthresultauthresult-redirecturl
signInSuccessWithAuthResult: () =>
// Don't automatically redirect. We handle redirects using
// `next-firebase-auth`.
false,
},
}

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
62 changes: 62 additions & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react'
import Link from 'next/link'

const nfaDependencyVersion =
require('../package.json').dependencies['next-firebase-auth']
const nextDependencyVersion = require('../package.json').dependencies.next
const firebaseDependencyVersion =
require('../package.json').dependencies.firebase

const styles = {
container: {
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
padding: 16,
},
versionsContainer: {
marginLeft: 0,
marginRight: 'auto',
},
button: {
marginLeft: 16,
cursor: 'pointer',
},
}

const Header = ({ email, signOut }) => (
<div style={styles.container}>
<div style={styles.versionsContainer}>
<div>v{nfaDependencyVersion}</div>
<div>Next.js v{nextDependencyVersion}</div>
<div>Firebase v{firebaseDependencyVersion}</div>
</div>
{email ? (
<>
<p>Signed in as {email}</p>
<button
type="button"
onClick={() => {
signOut()
}}
style={styles.button}
>
Sign out
</button>
</>
) : (
<>
<p>You are not signed in.</p>
<Link href="/auth">
<a>
<button type="button" style={styles.button}>
Sign in
</button>
</a>
</Link>
</>
)}
</div>
)

export default Header
100 changes: 100 additions & 0 deletions initAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* globals window */
import { init } from 'next-firebase-auth'
import absoluteUrl from 'next-absolute-url'

const TWELVE_DAYS_IN_MS = 12 * 60 * 60 * 24 * 1000

const initAuth = () => {
init({
// debug: true,

// This demonstrates setting a dynamic destination URL when
// redirecting from app pages. Alternatively, you can simply
// specify `authPageURL: '/auth-ssr'`.
authPageURL: ({ ctx }) => {
const isServerSide = typeof window === 'undefined'
const origin = isServerSide
? absoluteUrl(ctx.req).origin
: window.location.origin
const destPath =
typeof window === 'undefined' ? ctx.resolvedUrl : window.location.href
const destURL = new URL(destPath, origin)
return `auth-ssr?destination=${encodeURIComponent(destURL)}`
},

// This demonstrates setting a dynamic destination URL when
// redirecting from auth pages. Alternatively, you can simply
// specify `appPageURL: '/'`.
appPageURL: ({ ctx }) => {
const isServerSide = typeof window === 'undefined'
const origin = isServerSide
? absoluteUrl(ctx.req).origin
: window.location.origin
const params = isServerSide
? new URL(ctx.req.url, origin).searchParams
: new URLSearchParams(window.location.search)
const destinationParamVal = params.get('destination')
? decodeURIComponent(params.get('destination'))
: undefined

// By default, go to the index page if the destination URL
// is invalid or unspecified.
let destURL = '/'
if (destinationParamVal) {
// Verify the redirect URL host is allowed.
// https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/11-Client_Side_Testing/04-Testing_for_Client_Side_URL_Redirect
const allowedHosts = ['localhost:3000']
const allowed =
allowedHosts.indexOf(new URL(destinationParamVal).host) > -1
if (allowed) {
destURL = destinationParamVal
} else {
// eslint-disable-next-line no-console
console.warn(
`Redirect destination host must be one of ${allowedHosts.join(
', ',
)}.`,
)
}
}
return destURL
},
loginAPIEndpoint: '/api/login',
logoutAPIEndpoint: '/api/logout',
firebaseAdminInitConfig: {
credential: {
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
// Using JSON to handle newline problems when storing the
// key as a secret in Vercel. See:
// https://github.com/vercel/vercel/issues/749#issuecomment-707515089
privateKey: process.env.FIREBASE_PRIVATE_KEY
? JSON.parse(process.env.FIREBASE_PRIVATE_KEY)
: undefined,
},
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
},
firebaseClientInitConfig: {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
},
cookies: {
name: 'NextBlog',
keys: [
process.env.COOKIE_SECRET_CURRENT,
process.env.COOKIE_SECRET_PREVIOUS,
],
httpOnly: true,
maxAge: TWELVE_DAYS_IN_MS,
overwrite: true,
path: '/',
sameSite: 'strict',
secure: process.env.NEXT_PUBLIC_COOKIE_SECURE === 'true',
signed: true,
},
})
}

export default initAuth
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"name": "next-blog",
"version": "0.1.0",
"private": true,
"engines" : {
"node" : ">=16.0.0 <17.0.0"
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"scripts": {
"dev": "next dev",
Expand All @@ -29,19 +29,24 @@
"d3-selection": "^3.0.0",
"d3-time-format": "^4.1.0",
"d3-zoom": "^3.0.0",
"firebase": "^8.9.1",
"firebase-admin": "^11.3.0",
"gray-matter": "^4.0.3",
"hast-util-to-string": "^2.0.0",
"lodash": "^4.17.21",
"next": "12.1.0",
"next-absolute-url": "^1.2.2",
"next-firebase-auth": "^0.14.4",
"next-mdx-remote": "^4.0.0",
"node-fetch": "^3.2.3",
"parse-link-header": "^2.0.0",
"postcss": "^8.4.6",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-firebaseui": "^5.0.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-code-titles": "^1.0.3",
"rehype-color-chips": "^0.0.1",
"rehype-color-chips": "^0.1.1",
"rehype-prism-plus": "^1.3.1",
"rehype-slug": "^5.0.1",
"remark-gfm": "^3.0.1",
Expand Down
3 changes: 3 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import '../styles/globals.css'
import 'components/BullsAndCows/styles.css'
import type { AppProps } from 'next/app'
import initAuth from '../initAuth'

initAuth()

function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
Expand Down
41 changes: 41 additions & 0 deletions pages/api/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { verifyIdToken } from 'next-firebase-auth'
import initAuth from '../../initAuth'

console.log('calling init auth from examples.ts')
initAuth()

const handler = async (req, res) => {
if (!(req.headers && req.headers.authorization)) {
return res.status(400).json({ error: 'Missing Authorization header value' })
}
const token = req.headers.authorization

let favoriteColor

// This "unauthenticated" token is just an demo of the
// "SSR with no token" example.
if (token === 'unauthenticated') {
favoriteColor = 'unknown, because you called the API without an ID token'
} else {
try {
await verifyIdToken(token)
} catch (e) {
// eslint-disable-next-line no-console
console.error(e)
return res.status(403).json({ error: 'Not authorized' })
}

const colors = [
'sea foam green',
'light purple',
'teal',
'taupe',
'dark grey',
]
favoriteColor = colors[Math.floor(Math.random() * colors.length)]
}

return res.status(200).json({ favoriteColor })
}

export default handler
13 changes: 0 additions & 13 deletions pages/api/hello.ts

This file was deleted.

22 changes: 22 additions & 0 deletions pages/api/login.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// ./pages/api/login
import type { NextApiRequest, NextApiResponse } from 'next'
import { setAuthCookies } from 'next-firebase-auth'
import initAuth from '../../initAuth' // the module you created above

export type Data = {
success?: boolean
error?: string
}

initAuth()

const handler = async (req: NextApiRequest, res: NextApiResponse<Data>) => {
try {
await setAuthCookies(req, res)
} catch (e) {
return res.status(500).json({ error: 'Unexpected error.' })
}
return res.status(200).json({ success: true })
}

export default handler
Loading