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