Skip to content

Commit

Permalink
Set up basic auth provider
Browse files Browse the repository at this point in the history
  • Loading branch information
kmjennison committed Sep 9, 2023
1 parent 2502fd7 commit 45b0154
Show file tree
Hide file tree
Showing 9 changed files with 2,886 additions and 90 deletions.
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
}
5 changes: 5 additions & 0 deletions example-app-router/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copy this file as ".env.local".
# Update these with your Firebase app's values.
NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY=MyExampleAppAPIKey123
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=my-example-app.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=my-example-app-id
109 changes: 20 additions & 89 deletions example-app-router/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,26 @@
import Image from 'next/image'
// TODO: don't make whole page a client component
'use client'

import { useAuth } from '@/auth/context'
import styles from './page.module.css'
import { AuthProvider } from '@/auth/AuthProvider'

export default function Home() {
function Content() {
const { user } = useAuth()
console.log('user!', user)
return (
<main className={styles.main}>
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>app/page.tsx</code>
</p>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{' '}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>

<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>

<div className={styles.grid}>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Docs <span>-&gt;</span>
</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>

<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Learn <span>-&gt;</span>
</h2>
<p>Learn about Next.js in an interactive course with&nbsp;quizzes!</p>
</a>

<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Templates <span>-&gt;</span>
</h2>
<p>Explore the Next.js 13 playground.</p>
</a>
<p>
This is an example.
</p>
)
}

<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Deploy <span>-&gt;</span>
</h2>
<p>
Instantly deploy your Next.js site to a shareable URL with Vercel.
</p>
</a>
</div>
</main>
export default function Home() {
return (
<AuthProvider>
<main className={styles.main}>
<Content />
</main>
</AuthProvider>
)
}
51 changes: 51 additions & 0 deletions example-app-router/auth/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Provide the Firebase user to the rest of the app.
'use client'

import { FunctionComponent, ReactNode, useEffect, useState } from "react"
import {
onIdTokenChanged,
User as FirebaseUser,
} from "firebase/auth"
import useFirebaseAuth from "./useFirebaseAuth"
import { AuthContext } from "./context"

export interface AuthProviderProps {
children: ReactNode
}

export const AuthProvider: FunctionComponent<AuthProviderProps> = ({
children,
}) => {
const { getFirebaseAuth } = useFirebaseAuth()
const [user, setUser] = useState<FirebaseUser | null>(null)

const handleIdTokenChanged = async (firebaseUser: FirebaseUser | null) => {
if (!firebaseUser) {
setUser(null)
return
}
setUser(firebaseUser)
}

const registerChangeListener = async () => {
const auth = getFirebaseAuth()
return onIdTokenChanged(auth, handleIdTokenChanged)
}

useEffect(() => {
const unsubscribePromise = registerChangeListener()
return () => {
unsubscribePromise.then((unsubscribe) => unsubscribe())
}
}, [])

return (
<AuthContext.Provider
value={{
user,
}}
>
{children}
</AuthContext.Provider>
)
}
15 changes: 15 additions & 0 deletions example-app-router/auth/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use client"

import { createContext, useContext } from "react"
import type { UserInfo } from "firebase/auth"


export interface AuthContextValue {
user: UserInfo | null
}

export const AuthContext = createContext<AuthContextValue>({
user: null,
})

export const useAuth = () => useContext(AuthContext)
24 changes: 24 additions & 0 deletions example-app-router/auth/initFirebase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'

import { getApp, getApps, initializeApp } from 'firebase/app'
import { getAuth, connectAuthEmulator } from 'firebase/auth'

export default function initFirebase() {
// Only initialize on the client side.
if (typeof window === 'undefined') {
return
}
if (!getApps().length) {
initializeApp({
apiKey: process.env.NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID
})
console.log('Initialized the Firebase JS SDK.')
} else {
console.log(
'Did not initialize the Firebase JS SDK because an app already exists.'
)
}
}

16 changes: 16 additions & 0 deletions example-app-router/auth/useFirebaseAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getApp } from "firebase/app"
import { getAuth } from "firebase/auth"
import initFirebase from "./initFirebase"
import { useCallback } from "react"

const useFirebaseAuth = () => {
const getFirebaseAuth = useCallback(() => {
initFirebase()
const auth = getAuth(getApp())
return auth
}, [])

return { getFirebaseAuth }
}

export default useFirebaseAuth
1 change: 1 addition & 0 deletions example-app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@types/react-dom": "18.2.7",
"eslint": "8.49.0",
"eslint-config-next": "13.4.19",
"firebase": "^10.3.1",
"next": "13.4.19",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
Loading

0 comments on commit 45b0154

Please sign in to comment.