2016-05-22 9 views
4

私のSPA React/Firebaseアプリケーションのハードリフレッシュは、機能の即時実行時にauth状態を維持しません。私は回避策がありますが、それはスケッチです。firebase authがリフレッシュ時に遅れます

私の反応ルートはonEnter機能を利用して、ユーザーが認証されているかどうかを判断します。例えば

<Route path="/secure" component={Dashboard} onEnter={requireAuth}/> 

はさらに、私のrequireAuth関数は次のようになります。

function (nextState, replace) { 
     console.log('requireAuth', firebase.auth().currentUser); 
     if (!firebase.auth().currentUser) { 
      console.log('attempting to access a secure route. please login first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
}; 

しかし、ハードリフレッシュにfirebase.auth().currentUserに若干の遅延があります。これは最初はヌルです。次に、認証状態を判断するためにfirebaseサーバにPOSTを実行します。それが返されるときcurrentUserオブジェクトが設定されます。この遅延は問題を引き起こします。

私のハックソリューションは以下の通りです:更新:これは実際には動作しません...

function (nextState, replace) { 
    setTimeout(function() { 
     console.log('requireAuth', firebase.auth().currentUser); 
     if (!firebase.auth().currentUser) { 
      console.log('attempting to access a secure route. please login first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
    }, 50); 
}; 

は単にタイムアウトでそれをラップします。しかし、私は本当にこれが好きではない...任意の考えですか?

更新:

また、私は決定的な時間遅れとsetTimeoutよりも正確でなければなりませんonAuthStateChangedリスナー、中でそれをラップしようとしています。

function (nextState, replace) { 
    var unsubscribe = firebase.auth().onAuthStateChanged(function (user) { 
     if (!user) { 
      console.log('attempting to access a secure route'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }) 
      console.log('should have called replace'); 
     } 
     unsubscribe(); 
    }); 
    // setTimeout(function() { 
    //  console.log('requireAuth', firebase.auth().currentUser); 
    //  if (!firebase.auth().currentUser) { 
    //   console.log('attempting to access a secure route. please login first.'); 
    //   replace({ 
    //    pathname: '/login', 
    //    state: { nextPathname: nextState.location.pathname } 
    //   }); 
    //  } 
    // }, 50); 
}; 

2つのログ・ステートメントが実行されているが、反応-ルータreplaceを正しく実行されていないようです。コードは次のように。おそらく、それは反応ルータの専門家のための別の質問です。

アップデート2:私はこれに取り組んでいたとき

それは夜遅くでした。明らかにsetTimeoutは実際には機能しません。

+0

私はちょうどここで似たような質問に答えたと思う:http://stackoverflow.com/questions/37370462/refs-not-rerunning-after-onauthstatechanged-changes-firebase-3-0-0 –

+0

@FrankvanPuffelen私は実際に試したそんな感じ。 requireAuth関数のwrappの中からfirebase.auth()の内容をonAuthStateChanged(function(user){...});しかし、反応ルータの置換機能は、そのコンテキストでは何もしていないようです。私は質問を更新します。 –

答えて

6

大丈夫です。そこで、firebaseが提供する変数localStorageを利用して、ユーザー情報を格納することでこれを解決することができました。

function (nextState, replace) { 
    if (!firebase.auth().currentUser) { 
     let hasLocalStorageUser = false; 
     for (let key in localStorage) { 
      if (key.startsWith("firebase:authUser:")) { 
       hasLocalStorageUser = true; 
      } 
     } 
     if (!hasLocalStorageUser) { 
      console.log('Attempting to access a secure route. Please authenticate first.'); 
      replace({ 
       pathname: '/login', 
       state: { nextPathname: nextState.location.pathname } 
      }); 
     } 
    } 
}; 
2

これはReactJSに関する投稿ですが、最近AngularJSの認証/認可サービスを作成する際に同じ問題が発生しました。ページリフレッシュでは、nullというユーザーがfirebaseにまだ初期化されているため(非同期で)、onAuthStateChangedがユーザーに渡されます。

ユーザーがログアウトしてユーザーがログアウトした後にユーザーがログインして削除した後に、ユーザーのユーザーIDをlocalStorageに保存する唯一の解決策がありました。

私はauthServiceuserServiceを別々に使用していますので、ユーザがログイン/ログアウトすると解雇されるauthServiceにリスナーを登録しました。

コードサンプルのAuthService(ないフルのAuthService):

var loginListeners = []; 
var logoutListeners = []; 

function addLoginListener(func) { 
    loginListeners.push(func); 
} 

function addLogoutListener(func) { 
    logoutListeners.push(func); 
} 

function login(email, password) { 
    return firebase.auth().signInWithEmailAndPassword(email, password).then(function(user) { 
     for(var i = 0; i < loginListeners.length; i++) { 
      loginListeners[i](user); // call registered listeners for login 
     } 
    }); 
} 

function logout() { 
    return firebase.auth().signOut().then(function() { 
     for(var i = 0; i < logoutListeners.length; i++) { 
      logoutListeners[i](); // call registered listeners for logout 
     } 
    }); 
} 

コードサンプルUserServiceの(ないフルUserServiceの):

.provider('userService', ['authServiceProvider', 
function UserService(authServiceProvider) { 

var usersRefUrl = '/users'; 
var userInfo = null; 
var userDetails = null; 

// refreshHack auto-executed when this provider creates the service 
var storageId = 'firebase:uid'; // storing uid local because onAuthStateChanged gives null (when async initializing firebase) 
(function addRefreshHackListeners() { 
    authServiceProvider.addLoginListener(function(user) { 
     userInfo = user; 
     localStorage.setItem(storageId, user.uid); // store the users uid after login so on refresh we have uid to retreive userDetails 
    }); 
    authServiceProvider.addLogoutListener(function() { 
     userInfo = null; 
     localStorage.removeItem(storageId); 
    }); 
    firebase.auth().onAuthStateChanged(function(user) { 
     if(user) { // when not using refreshHack user is null until async initializing is done (and no uid is available). 
      localStorage.setItem(storageId, user.uid); 
      userInfo = user; 
      resolveUserDetails(); 
     } else { 
      localStorage.removeItem(storageId); 
      userInfo = null; 
      userDetails = null; 
     } 
    }); 
})(); 

function isLoggedIn() { 
    return userInfo ? userInfo.uid : localStorage.getItem(storageId); // check localStorage for refreshHack 
} 

function resolveUserDetails() { 
    var p = null; 
    var uid = isLoggedIn(); 
    if(uid) 
     p = firebase.database().ref(usersRefUrl + '/' + uid).once('value').then(function(snapshot) { 
      userDetails = snapshot.val(); 
      return userDetails; 
     }).catch(function(error) { 
      userDetails = null; 
     }); 

    return p; // resolve by returning a promise or null 
} 
}]); 

そして、あなたはユーザーを登録するグローバルことができ、実行ブロックでルート変更ごとにユーザー情報/詳細を解決します(より安全にします)。

多分これは、同じ問題を抱えている人を助けることができます。それ以外にも、コードサンプルを自由に最適化して議論することができます。

+0

なぜ別のローカルストレージ変数にユーザーが格納される必要がありますか?それはすでにそこにある。そうでなければ角度の良い解決策。 –

+0

これは単なる設計上の選択です。 Offcourseでは、ユーザー全体をlocalStorageに保存することができますが、私はlocalStorageへの参照が嫌いです。私の解決策では、 '/ users'のようなfirebaseリファレンスの特定のユーザ情報(詳細)を持っており、firebaseによってプッシュされる 'child_added'イベントや他のイベントを聴くことができます。あなたの状況でこれがオーバーヘッドのように感じられるなら、あなたは別の方法を選択するかもしれません。 –

関連する問題