The console
Managing users
React and Redux
The App
Getting firebase running
import firebase from 'firebase';
and firebase auth is initialized like this, where ca.config contains the credentials that were produced by the Firebase console.
const firebaseApp = firebase.initializeApp(ca.config); const firebaseAuth = firebaseApp.auth();
Signing in
render() { let item; const props = this.props; if ( props.auth.status === cs.status.AUTH_LOGGED_IN ) { item = <span><Chip onRequestDelete={this.signout.bind(this)} > <Avatar src={props.auth.photoURL} size={40} /> {props.auth.displayName} </Chip> </span>; } else if (props.auth.status === cs.status.AUTH_AWAITING_RESPONSE || props.auth.status === cs.status.AUTH_AWAITING_USER) { item = <span></span>; } else { item = <ListItem primaryText={<FlatButton style={this.props.itemContainer} onClick={this.signin.bind(this)} label="Sign in" />} style={this.props.itemContainer} />; } return ( <List style={this.props.listContainer}>{item}</List> ); }
The onClick events dispatch action creators to the Redux store which look like this.
signin() { this.props.dispatch (acSignin()); } signout() { this.props.dispatch (acSignout()); }
Here I’m only using the Google sign-in method, which is asynchronous. For more on dealing with promises in redux action creators, see Creating promise actions in redux
/** * sign in to firebase */ export function acSignin() { const provider = new firebase.auth.GoogleAuthProvider(); return acPromise ( cs.actions.AUTH_SIGNIN,'google', () => { return firebaseAuth.signInWithPopup(provider); } ); }
And the signout is also asynchronous
/** * sign out of firebase */ export function acSignout() { return acPromise ( cs.actions.AUTH_SIGNOUT, getCurrentUid(), () => firebase.signOut ); }
The current uid is used for error logging, so it can be retrieved from the store like this.
function getCurrentUid() { const state = Process.store.getState(); return state.auth ? state.auth.uid : ""; }
The reducers
const initialState = { status:cs.status.AUTH_UNKNOWN, error:null, displayName:'', uid:'', photoURL:'', email:'' };
Since this is asynchronous we have 3 sub actions for each action to handle. Note that a successful sign in gives you user information plus the credentials property. If you need an access token to get to other APIS (you can specify additional scopes when you make an authentication request), then you’ll find it in credentials. Although the user information is also available here, firebase recommends that you pick that up by watching for user changes (which I’ll get to in a moment).
case cs.actions.AUTH_SIGNOUT+'_PENDING': return {...initialState, status: cs.status.AUTH_AWAITING_RESPONSE }; case cs.actions.AUTH_SIGNOUT+'_REJECTED': return {...initialState, status: cs.status.AUTH_UNKNOWN,error:action.payload }; case cs.actions.AUTH_SIGNOUT+'_FULFILLED': return {...initialState}; case cs.actions.AUTH_SIGNIN+'_PENDING': return {...initialState, status: cs.status.AUTH_AWAITING_RESPONSE }; case cs.actions.AUTH_SIGNIN+'_REJECTED': return {...initialState, status: cs.status.AUTH_UNKNOWN,error:action.payload }; case cs.actions.AUTH_SIGNIN+'_FULFILLED': { // its possible that firebase will have fired its auth_user action first // so only set to waiting if its not logged in if (state.status === cs.status.AUTH_LOGGED_IN) { return {...state, credential:action.payload.credential}; } else { return { ...initialState, credential:action.payload.credential, status: cs.status.AUTH_AWAITING_USER }; } }
Waiting for changes in auth state
// this is about signing in and out firebaseAuth.onAuthStateChanged( user => { const state = Process.store.getState(); if (user && state.auth.uid && state.auth.uid === user.uid) { // do nothing as this can get fired on a refresh token, // but the user hasn't actually changed } else { // we've received a new user so dispatch an action to send the new user info to the store. Process.store.dispatch(acAuthUser(user)); } }
The action creator
/** * change in user fired */ export function acAuthUser(user) { return { type: cs.actions.AUTH_USER, payload: user }; }
The reducer
case cs.actions.AUTH_USER: return action.payload ? {...state, status: cs.status.AUTH_LOGGED_IN, displayName:action.payload.displayName, uid:action.payload.uid, photoURL:action.payload.photoURL, email:action.payload.email } : {...initialState};