前提・実現したいこと
WEBアプリでfirebaseを利用しており、LINEのLIFFを使いログインできるようにしたいと考えています。
下記のサイトを参考にコードを書いてみたところ、殆どの場合ログインできるのですが、たまに失敗します。
https://qiita.com/Arahabica/items/8f5c7472ded92128535f
複数の友達にテストしてもらっているのですが、失敗する人が同じ人なので実行環境側の原因を疑いました。
しかしLIFFに必要なLINEのバージョンも問題なく、キャッシュをクリアしてみても同じでした。
なので、コード側に問題があると思うのですが、糸口すらつかめずにいます。
原因の探し方でも構いませんので、何かアドバイスをいただけますと幸いです。
発生している問題・エラーメッセージ
サーバー(Functions)のログ
login
Function execution took 78 ms, finished with status code: 500
Functionsの正常性の欄に表示されているエラー
login
Unhandled error TypeError: Cannot destructure property 'accessToken' of 'data' as it is null. at /workspace/index.js:1269:17 at func (/workspace/node_modules/firebase-functions/lib/providers/https.js:336:32) at processTicksAndRejections (internal/process/task_queues.js:95:5)
testSet(テストとして、ログインできたらDBに書き込みを行う処理を追加しているのですが、その関数でもエラー)
Error: Request failed with status code 400 at createError (/workspace/node_modules/axios/lib/core/createError.js:16:15) at settle (/workspace/node_modules/axios/lib/core/settle.js:17:12) at IncomingMessage.handleStreamEnd (/workspace/node_modules/axios/lib/adapters/http.js:260:11) at IncomingMessage.emit (events.js:412:35) at IncomingMessage.emit (domain.js:532:15) at endReadableNT (internal/streams/readable.js:1317:12) at processTicksAndRejections (internal/process/task_queues.js:82:21) {
該当のソースコード
クライアント側
javascript
1window.onload = function() { 2 liff.init({liffId: "*****"}) 3 .then(() => { 4 initializeApp(); 5 }) 6 .catch((err) => { 7 console.log(err) 8 }); 9}; 10 11function initializeApp() { 12 if (liff.isLoggedIn()) { 13 firebaseLoginCheck() 14 } else { 15 liff.login({ redirectUri: "*****" }); 16 } 17} 18 19function firebaseLoginCheck() { 20 auth().onAuthStateChanged(async user => { 21 if (user) { 22 testesSet(); 23 } else { 24 const accessToken = liff.getAccessToken() 25 const login = functions.httpsCallable('login') 26 login({ accessToken }) 27 .then((result) => { 28 auth().signInWithCustomToken(result.data.token) 29 .then((userCredential) => { 30 testesSet(); 31 }) 32 .catch((error) => { 33 console.log(error) 34 }); 35 }) 36 .catch((error) => { 37 console.log(error) 38 }); 39 } 40 }) 41} 42 43async function testesSet() { 44 const accessToken = liff.getAccessToken() 45 const testSet = functions.httpsCallable('testSet') 46 const result = await testSet({ accessToken: accessToken }) 47 console.log(result.data) 48}
サーバー(Functions)側
javascript
1const axios = require('axios') 2const axiosInstance = axios.create({ 3 baseURL: 'https://api.line.me', 4 responseType: 'json' 5}) 6 7// 渡されたLINEトークンが正しいものかを検証 8const verifyToken = async accessToken => { 9 const response = await axiosInstance.get('/oauth2/v2.1/verify', { params: { access_token: accessToken } }) 10 if (response.status !== 200) { 11 console.error(response.data.error_description) 12 throw new Error(response.data.error) 13 } 14 // チャネルIDをチェック 15 if (response.data.client_id !== channelId) { 16 throw new Error('client_id does not match.') 17 } 18 //アクセストークンの有効期限 19 if (response.data.expires_in < 0) { 20 throw new Error('access token is expired.') 21 } 22} 23 24// プロフィールの取得 25const getProfile = async(accessToken) => { 26 const response = await axiosInstance.get('/v2/profile', { 27 headers: { 28 'Authorization': `Bearer ${accessToken}` 29 }, 30 data: {} 31 }) 32 if (response.status !== 200) { 33 console.error(response.data.error_description) 34 throw new Error(response.data.error) 35 } 36 return response.data 37} 38 39// カスタムトークンを活用したログイン処理 40exports.login = functions.region(REGION).https 41 .onCall(async data => { 42 const { accessToken } = data 43 try { 44 // LINEのアクセストークンが正しいか検証 45 await verifyToken(accessToken); 46 // アクセストークンを利用してプロフィール取得 47 const profile = await getProfile(accessToken); 48 // user情報をDBに登録 49 db.collection('user').doc(profile.userId).set({ 50 imgRef: profile.pictureUrl, 51 name: profile.displayName, 52 update: admin.firestore.FieldValue.serverTimestamp() 53 }); 54 // LINEのuserIdを利用してfirebaseのカスタム認証トークンを発行 55 const token = await admin.auth().createCustomToken(profile.userId); 56 return { token } 57 } catch (e) { 58 console.error(JSON.stringify(e, null, ' ')) 59 throw new functions.https.HttpsError('unknown', 'ログインに失敗しました ' + e); 60 } 61 }) 62 63exports.testSet = functions.region(REGION).https 64 .onCall(async(data, context) => { 65 66 const uid = context.auth.uid; 67 const accessToken = data.accessToken 68 69 const testRef = db.collection("test").doc(uid) 70 71 try { 72 await verifyToken(accessToken) 73 const profile = await getProfile(accessToken) 74 75 const value = { 76 imgRef: profile.pictureUrl, 77 name: profile.displayName, 78 update: admin.firestore.FieldValue.serverTimestamp() 79 } 80 await testRef.set(value) 81 return "成功しました" 82 } catch (error) { 83 console.log(error) 84 return "失敗しました" 85 } 86 })
試したこと
LINEのLIFFのログイン機能を使ってfirebaseのカスタム認証を利用する方法について調べているのですが、情報が少なく、何が問題なのか全く分かっていません。
エラーから、アクセストークンがnullの場合があるのかな?と思ってコードを読み直したのですが、アクセストークンは取得して格納しているようですし、殆どの場合は成功しているのでよく分からないでいます。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。