mirror of
https://github.com/kevin-DL/commander_league_web.git
synced 2026-01-11 18:14:27 +00:00
Initial commit from Create Next App
This commit is contained in:
9
.env.local.example
Normal file
9
.env.local.example
Normal file
@@ -0,0 +1,9 @@
|
||||
# Update these with your Firebase app's values.
|
||||
FIREBASE_CLIENT_EMAIL=my-example-app-email@example.com
|
||||
NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY=MyExampleAppAPIKey123
|
||||
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=my-example-app.firebaseapp.com
|
||||
NEXT_PUBLIC_FIREBASE_DATABASE_URL=https://my-example-app.firebaseio.com
|
||||
NEXT_PUBLIC_FIREBASE_PROJECT_ID=my-example-app-id
|
||||
|
||||
# Your Firebase private key.
|
||||
FIREBASE_PRIVATE_KEY=some-key-here
|
||||
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
37
README.md
Normal file
37
README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Example: Firebase authentication with a serverless API
|
||||
|
||||
This example includes Firebase authentication and serverless [API routes](https://nextjs.org/docs/api-routes/introduction).
|
||||
|
||||
## How to use
|
||||
|
||||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example with-firebase-authentication with-firebase-authentication-app
|
||||
# or
|
||||
yarn create next-app --example with-firebase-authentication with-firebase-authentication-app
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Set up Firebase:
|
||||
|
||||
- Create a project at the [Firebase console](https://console.firebase.google.com/).
|
||||
- Copy the contents of `.env.local.example` into a new file called `.env.local`
|
||||
- Get your account credentials from the Firebase console at _Project settings > Service accounts_, where you can click on _Generate new private key_ and download the credentials as a json file. It will contain keys such as `project_id`, `client_email` and `client_id`. Set them as environment variables in the `.env.local` file at the root of this project.
|
||||
- Get your authentication credentials from the Firebase console under _Project settings > General> Your apps_ Add a new web app if you don't already have one. Under _Firebase SDK snippet_ choose _Config_ to get the configuration as JSON. It will include keys like `apiKey`, `authDomain` and `databaseUrl`. Set the appropriate environment variables in the `.env.local` file at the root of this project.
|
||||
- Go to **Develop**, click on **Authentication** and in the **Sign-in method** tab enable authentication for the app.
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
# or
|
||||
yarn
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [Vercel](https://vercel.com/import?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
||||
|
||||
After deploying, copy the deployment URL and navigate to your Firebase project's Authentication tab. Scroll down in the page to "Authorized domains" and add that URL to the list.
|
||||
54
components/FirebaseAuth.js
Normal file
54
components/FirebaseAuth.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* globals window */
|
||||
import { useEffect, useState } from 'react'
|
||||
import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth'
|
||||
import firebase from 'firebase/app'
|
||||
import 'firebase/auth'
|
||||
import initFirebase from '../utils/auth/initFirebase'
|
||||
import { setUserCookie } from '../utils/auth/userCookies'
|
||||
import { mapUserData } from '../utils/auth/mapUserData'
|
||||
|
||||
// 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',
|
||||
callbacks: {
|
||||
signInSuccessWithAuthResult: async ({ user }, redirectUrl) => {
|
||||
const userData = mapUserData(user)
|
||||
setUserCookie(userData)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
8058
package-lock.json
generated
Normal file
8058
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "with-firebase-authentication",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase": "^7.15.5",
|
||||
"firebase-admin": "^8.12.1",
|
||||
"js-cookie": "2.2.1",
|
||||
"next": "latest",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-firebaseui": "4.1.0",
|
||||
"swr": "0.2.3"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
17
pages/api/getFood.js
Normal file
17
pages/api/getFood.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { verifyIdToken } from '../../utils/auth/firebaseAdmin'
|
||||
const favoriteFoods = ['pizza', 'burger', 'chips', 'tortilla']
|
||||
|
||||
const getFood = async (req, res) => {
|
||||
const token = req.headers.token
|
||||
|
||||
try {
|
||||
await verifyIdToken(token)
|
||||
return res.status(200).json({
|
||||
food: favoriteFoods[Math.floor(Math.random() * favoriteFoods.length)],
|
||||
})
|
||||
} catch (error) {
|
||||
return res.status(401).send('You are unauthorised')
|
||||
}
|
||||
}
|
||||
|
||||
export default getFood
|
||||
14
pages/auth.js
Normal file
14
pages/auth.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import FirebaseAuth from '../components/FirebaseAuth'
|
||||
|
||||
const Auth = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>Sign in</p>
|
||||
<div>
|
||||
<FirebaseAuth />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Auth
|
||||
17
pages/example.js
Normal file
17
pages/example.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
const Example = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
This page is static because it does not fetch any data or include the
|
||||
authed user info.
|
||||
</p>
|
||||
<Link href={'/'}>
|
||||
<a>Home</a>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Example
|
||||
63
pages/index.js
Normal file
63
pages/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import useSWR from 'swr'
|
||||
import Link from 'next/link'
|
||||
import { useUser } from '../utils/auth/useUser'
|
||||
|
||||
const fetcher = (url, token) =>
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
headers: new Headers({ 'Content-Type': 'application/json', token }),
|
||||
credentials: 'same-origin',
|
||||
}).then((res) => res.json())
|
||||
|
||||
const Index = () => {
|
||||
const { user, logout } = useUser()
|
||||
const { data, error } = useSWR(
|
||||
user ? ['/api/getFood', user.token] : null,
|
||||
fetcher
|
||||
)
|
||||
if (!user) {
|
||||
return (
|
||||
<>
|
||||
<p>Hi there!</p>
|
||||
<p>
|
||||
You are not signed in.{' '}
|
||||
<Link href={'/auth'}>
|
||||
<a>Sign in</a>
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<p>You're signed in. Email: {user.email}</p>
|
||||
<p
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
color: 'blue',
|
||||
textDecoration: 'underline',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
Log out
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<Link href={'/example'}>
|
||||
<a>Another example page</a>
|
||||
</Link>
|
||||
</div>
|
||||
{error && <div>Failed to fetch food!</div>}
|
||||
{data && !error ? (
|
||||
<div>Your favorite food is {data.food}.</div>
|
||||
) : (
|
||||
<div>Loading...</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
||||
24
utils/auth/firebaseAdmin.js
Normal file
24
utils/auth/firebaseAdmin.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as admin from 'firebase-admin'
|
||||
|
||||
export const verifyIdToken = (token) => {
|
||||
const firebasePrivateKey = process.env.FIREBASE_PRIVATE_KEY
|
||||
|
||||
if (!admin.apps.length) {
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert({
|
||||
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
|
||||
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
|
||||
// https://stackoverflow.com/a/41044630/1332513
|
||||
privateKey: firebasePrivateKey.replace(/\\n/g, '\n'),
|
||||
}),
|
||||
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
|
||||
})
|
||||
}
|
||||
|
||||
return admin
|
||||
.auth()
|
||||
.verifyIdToken(token)
|
||||
.catch((error) => {
|
||||
throw error
|
||||
})
|
||||
}
|
||||
15
utils/auth/initFirebase.js
Normal file
15
utils/auth/initFirebase.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import firebase from 'firebase/app'
|
||||
import 'firebase/auth'
|
||||
|
||||
const config = {
|
||||
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,
|
||||
}
|
||||
|
||||
export default function initFirebase() {
|
||||
if (!firebase.apps.length) {
|
||||
firebase.initializeApp(config)
|
||||
}
|
||||
}
|
||||
8
utils/auth/mapUserData.js
Normal file
8
utils/auth/mapUserData.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export const mapUserData = (user) => {
|
||||
const { uid, email, xa, ya } = user
|
||||
return {
|
||||
id: uid,
|
||||
email,
|
||||
token: xa || ya,
|
||||
}
|
||||
}
|
||||
63
utils/auth/useUser.js
Normal file
63
utils/auth/useUser.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import firebase from 'firebase/app'
|
||||
import 'firebase/auth'
|
||||
import initFirebase from '../auth/initFirebase'
|
||||
import {
|
||||
removeUserCookie,
|
||||
setUserCookie,
|
||||
getUserFromCookie,
|
||||
} from './userCookies'
|
||||
import { mapUserData } from './mapUserData'
|
||||
|
||||
initFirebase()
|
||||
|
||||
const useUser = () => {
|
||||
const [user, setUser] = useState()
|
||||
const router = useRouter()
|
||||
|
||||
const logout = async () => {
|
||||
return firebase
|
||||
.auth()
|
||||
.signOut()
|
||||
.then(() => {
|
||||
// Sign-out successful.
|
||||
router.push('/auth')
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Firebase updates the id token every hour, this
|
||||
// makes sure the react state and the cookie are
|
||||
// both kept up to date
|
||||
const cancelAuthListener = firebase.auth().onIdTokenChanged((user) => {
|
||||
if (user) {
|
||||
const userData = mapUserData(user)
|
||||
setUserCookie(userData)
|
||||
setUser(userData)
|
||||
} else {
|
||||
removeUserCookie()
|
||||
setUser()
|
||||
}
|
||||
})
|
||||
|
||||
const userFromCookie = getUserFromCookie()
|
||||
if (!userFromCookie) {
|
||||
router.push('/')
|
||||
return
|
||||
}
|
||||
setUser(userFromCookie)
|
||||
|
||||
return () => {
|
||||
cancelAuthListener()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
return { user, logout }
|
||||
}
|
||||
|
||||
export { useUser }
|
||||
19
utils/auth/userCookies.js
Normal file
19
utils/auth/userCookies.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import cookies from 'js-cookie'
|
||||
|
||||
export const getUserFromCookie = () => {
|
||||
const cookie = cookies.get('auth')
|
||||
if (!cookie) {
|
||||
return
|
||||
}
|
||||
return JSON.parse(cookie)
|
||||
}
|
||||
|
||||
export const setUserCookie = (user) => {
|
||||
cookies.set('auth', user, {
|
||||
// firebase id tokens expire in one hour
|
||||
// set cookie expiry to match
|
||||
expires: 1 / 24,
|
||||
})
|
||||
}
|
||||
|
||||
export const removeUserCookie = () => cookies.remove('auth')
|
||||
Reference in New Issue
Block a user