Skip to content

Commit 3a61815

Browse files
authored
Merge pull request #59 from yukia3/features/issue-58
#58 Add Firebase Auth Modules
2 parents 3905f4a + c175107 commit 3a61815

File tree

7 files changed

+534
-11
lines changed

7 files changed

+534
-11
lines changed

nuxt.config.ts

+12
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ const config: NuxtConfig = {
115115
// https://baianat.github.io/vee-validate/examples/nuxt.html
116116
transpile: ['vee-validate/dist/rules'],
117117
},
118+
serverMiddleware: ['~/api/auth'],
119+
pwa: {
120+
workbox: {
121+
offline: false,
122+
importScripts: [
123+
'https://www.gstatic.com/firebasejs/7.2.1/firebase-app.js',
124+
'https://www.gstatic.com/firebasejs/7.2.1/firebase-auth.js',
125+
],
126+
workboxExtensions: ['~/sw/firebase'],
127+
dev: true,
128+
},
129+
},
118130
}
119131

120132
export default config

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@nuxtjs/pwa": "^3.0.0-beta.20",
3535
"date-fns": "^2.16.1",
3636
"firebase": "^7.20.0",
37+
"firebase-admin": "^9.2.0",
3738
"nuxt": "^2.14.0",
3839
"nuxt-buefy": "^0.3.31",
3940
"vee-validate": "^3.3.11"

src/api/auth.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const admin = require('firebase-admin')
2+
3+
// The Firebase Admin SDK is used here to verify the ID token.
4+
if (!admin.apps.length) {
5+
admin.initializeApp({
6+
credential: admin.credential.applicationDefault(),
7+
databaseURL: process.env.DATABASEURL,
8+
})
9+
}
10+
11+
function getIdToken(req) {
12+
// Parse the injected ID token from the request header.
13+
const authorizationHeader = req.headers.authorization || ''
14+
const components = authorizationHeader.split(' ')
15+
return components.length > 1 ? components[1] : ''
16+
}
17+
18+
export default async function (req, _res, next) {
19+
// /_nuxt/以下ファイルで実行しない
20+
if (req.url.includes('_nuxt')) {
21+
return next()
22+
}
23+
24+
const idToken = getIdToken(req)
25+
26+
if (idToken) {
27+
try {
28+
const decodedClaims = await admin.auth().verifyIdToken(idToken)
29+
30+
// 最新のUserData取得
31+
const userData = await admin.auth().getUser(decodedClaims.uid)
32+
if (userData) {
33+
const data = userData.toJSON()
34+
delete data.passwordHash
35+
delete data.passwordSalt
36+
delete data.tokensValidAfterTime
37+
req.authUser = data
38+
}
39+
} catch (e) {
40+
delete req.authUser
41+
}
42+
}
43+
44+
next()
45+
}

src/store/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export const actions = {
2+
async nuxtServerInit({ dispatch }: any, { req }: any) {
3+
const user = req.authUser || null
4+
5+
if (user) {
6+
await dispatch('serverAuth/saveUID', user.uid)
7+
}
8+
},
9+
}

src/store/serverAuth/index.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const state = () => ({
2+
uid: '',
3+
})
4+
5+
export const mutations = {
6+
saveUID(state: any, uid: string) {
7+
state.uid = uid
8+
},
9+
clearUID(state: any) {
10+
state.uid = ''
11+
},
12+
}

src/sw/firebase.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import firebase from '../plugins/firebase'
2+
3+
/**
4+
* Returns a promise that resolves with an ID token if available.
5+
* @return {!Promise<?string>} The promise that resolves with an ID token if
6+
* available. Otherwise, the promise resolves with null.
7+
*/
8+
const getIdToken = () => {
9+
return new Promise((resolve) => {
10+
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
11+
unsubscribe()
12+
if (user) {
13+
user.getIdToken().then(
14+
(idToken) => {
15+
resolve(idToken)
16+
},
17+
() => {
18+
resolve(null)
19+
}
20+
)
21+
} else {
22+
resolve(null)
23+
}
24+
})
25+
})
26+
}
27+
28+
const getOriginFromUrl = (url) => {
29+
// https://stackoverflow.com/questions/1420881/how-to-extract-base-url-from-a-string-in-javascript
30+
const pathArray = url.split('/')
31+
const protocol = pathArray[0]
32+
const host = pathArray[2]
33+
return protocol + '//' + host
34+
}
35+
36+
self.addEventListener('fetch', (event) => {
37+
if (event.request.url.includes('firebasestorage.googleapis.com')) {
38+
return
39+
}
40+
41+
const requestProcessor = (idToken) => {
42+
let req = event.request
43+
// For same origin https requests, append idToken to header.
44+
if (
45+
self.location.origin === getOriginFromUrl(event.request.url) &&
46+
(self.location.protocol === 'https:' ||
47+
self.location.hostname === 'localhost') &&
48+
idToken
49+
) {
50+
// Clone headers as request headers are immutable.
51+
const headers = new Headers()
52+
for (const entry of req.headers.entries()) {
53+
headers.append(entry[0], entry[1])
54+
}
55+
// Add ID token to header.
56+
headers.append('authorization', 'Bearer ' + idToken)
57+
try {
58+
req = new Request(req.url, {
59+
method: req.method,
60+
headers,
61+
mode: 'same-origin',
62+
credentials: req.credentials,
63+
cache: req.cache,
64+
redirect: req.redirect,
65+
referrer: req.referrer,
66+
body: req.body,
67+
bodyUsed: req.bodyUsed,
68+
context: req.context,
69+
})
70+
} catch (e) {
71+
// This will fail for CORS requests. We just continue with the
72+
// fetch caching logic below and do not pass the ID token.
73+
}
74+
}
75+
return fetch(req)
76+
}
77+
// Fetch the resource after checking for the ID token.
78+
// This can also be integrated with existing logic to serve cached files
79+
// in offline mode.
80+
event.respondWith(getIdToken().then(requestProcessor, requestProcessor))
81+
})
82+
83+
self.addEventListener('activate', (event) => {
84+
event.waitUntil(clients.claim())
85+
})

0 commit comments

Comments
 (0)