SpringBoot With Angular 4 Integration and Social Authentication

As a full stack developer, we need to create an application with both frontend-end and backend.

Today we will create a simple full stack application which will have a social login using Google authentication.The area on which we want to focus is how to integrate google social authentication to spring-boot, with logout functionality. And when angular 4 is used in spring-boot application to build the UI, how we will manage the routes, as we know that both angular and have routes, so how our application will resolve which route it has to call.

Tech Stack

  • Angular 4
  • SpringBoot
  • Gradle

Prerequisites

Let’s start creating an application

  • Create a simple spring boot application using Spring.io
    • Project Type with gradle and Java
    • Add necessary description, group, and artifact
    • Add following dependencies to it.

compile 'org.springframework.cloud:spring-cloud-starter-config'
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-starter-tomcat")
compile 'org.springframework.security:spring-security-web'
compile 'org.springframework.security:spring-security-config'
compile 'org.springframework.security.oauth:spring-security-oauth2'
compile 'org.apache.httpcomponents:httpclient'
compile("io.springfox:springfox-swagger2:2.6.1")
compile("io.springfox:springfox-swagger-ui:2.6.1")

testCompile('org.springframework.boot:spring-boot-starter-test')
compile group: 'org.slf4j' , name: 'slf4j-api' , version: '1.7.22'
compile group: 'io.jsonwebtoken' , name: 'jjwt' , version: '0.7.0'
compile group: 'commons-io' , name: 'commons-io', version: '2.5'
compile group: 'org.projectlombok' , name: 'lombok' , version: '1.16.12'

These dependencies will allow us to enable the google authentication on the top of normal spring boot application.

  • Create the property file application-local.yml in the java main resources.

security:
oauth2:
client:
clientId: ******.apps.googleusercontent.com
clientSecret: ********
accessTokenUri: https://www.googleapis.com/oauth2/v4/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/v2/auth
clientAuthenticationScheme: form
scope:
- openid
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v3/userinfo
preferTokenInfo: true

user:
role: test-role

We have created a property file to enable google authentication against the particular client.Replace the * with your actual data.

  • Create bootstrap.yml with following data.
<pre>spring:
  application:
    name: frontend
  profiles:
    active: local
  cloud:
    config:
      uri: http://localhost:8888
      enabled: true</pre>

It will help us to give the name to our application and will activate “LOCAL” profile.

  • Create a Security Config java file “SpringConfig” on the same level as of the main file.

package com.test.practise;

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers( "/login","/public/**", "/resources/**","/resources/public/**")
                    .permitAll()
                .antMatchers("/","/api/**").authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/");
    }
}

It will apply authentication to all the path accept /login,  /public,  /resources/public/.

  • Add controller for logout “SecurityController”
<pre>package com.test.practise.controller;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class SecurityController {


    @RequestMapping(value="/logout", method = RequestMethod.GET)
    public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null){
            new SecurityContextLogoutHandler().logout(request, response, auth);
        }
        return "redirect:/";
    }
}
</pre>

It will enable the logout URL, which will delete the session.

  • We are not pretty much done with the spring side coding, but we have not added any view or frontend related code yet. We will create the angular project now.
  • Go to the root of the project and create the angular project angular-cli.

ng new webui

It will create a new project directory for frontend inside our current project directory.

  • Navigate to a webui directory which you have created, and make 3 testing controller using the following command.

ng g component landingComponent

ng g component homeComponent

ng g component settingComponent

These are the dummy component to show the routing.

  • Now we will mention our routes in the angular project in file app.module.ts

<pre>import {RouterModule, Routes} from "@angular/router";</pre>


const appRoutes: Routes =[

{path:'',component:LandingComponent},
{path:'welcome',component:WelcomeComponent},
{path:'setting',component:SettingComponent}

]

 

 

  • Add routing module in Imports in the same file.
<pre>@NgModule({
  declarations: [
    AppComponent,
    WelcomeComponent,
    SettingComponent,
    LandingComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot(appRoutes,{enableTracing:true})
  ],
  providers: [],
  bootstrap: [AppComponent]
})

</pre>
  • replace the code of app.component.html the main page of ui with following code.

<pre>&lt;!–The content below is only a placeholder and can be replaced.–&gt;
&lt;div style="text-align:center"&gt;
&lt;h1&gt;
Welcome to {{title}}!!
&lt;/h1&gt;
&lt;img width="300" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOS4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAyNTAgMjUwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAyNTAgMjUwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDojREQwMDMxO30NCgkuc3Qxe2ZpbGw6I0MzMDAyRjt9DQoJLnN0MntmaWxsOiNGRkZGRkY7fQ0KPC9zdHlsZT4NCjxnPg0KCTxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iMTI1LDMwIDEyNSwzMCAxMjUsMzAgMzEuOSw2My4yIDQ2LjEsMTg2LjMgMTI1LDIzMCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAJIi8+DQoJPHBvbHlnb24gY2xhc3M9InN0MSIgcG9pbnRzPSIxMjUsMzAgMTI1LDUyLjIgMTI1LDUyLjEgMTI1LDE1My40IDEyNSwxNTMuNCAxMjUsMjMwIDEyNSwyMzAgMjAzLjksMTg2LjMgMjE4LjEsNjMuMiAxMjUsMzAgCSIvPg0KCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMjUsNTIuMUw2Ni44LDE4Mi42aDBoMjEuN2gwbDExLjctMjkuMmg0OS40bDExLjcsMjkuMmgwaDIxLjdoMEwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMUwxMjUsNTIuMQ0KCQlMMTI1LDUyLjF6IE0xNDIsMTM1LjRIMTA4bDE3LTQwLjlMMTQyLDEzNS40eiIvPg0KPC9nPg0KPC9zdmc+DQo="&gt;
&lt;/div&gt;
&lt;a href="/setting"&gt;Direct Setting&lt;/a&gt;
&lt;a href="/welcome"&gt;Direct Welcome&lt;/a&gt;
&lt;nav&gt;
&lt;a routerLink="/" routerLinkActive="active"&gt;Landing Page&lt;/a&gt;
&lt;a routerLink="/welcome" routerLinkActive="active"&gt;Welcome Page&lt;/a&gt;
&lt;a routerLink="/setting" routerLinkActive="active"&gt;Setting Page&lt;/a&gt;
&lt;/nav&gt;
&lt;router-outlet&gt;&lt;/router-outlet&gt;

</pre>

It will create the routes .First two route for setting and welcome is using href. normal HTML routes and then we have refined routes using routerLink.

  • Build the frontend application

ng build

  • Now we will make changes in spring application to consume this frontend code.First of all, we will add the  code in our build.gradle to copy the frontend code and place it in resource so that it can be consume by spring-boot.
  • Add following task to build.gradle

<pre>

task removeWebui(type: Delete) {
delete "${sourceSets.main.resources.srcDirs[0]}/webui"
}

task copyWebui(type: Copy) {
from "webui/dist"

into "${sourceSets.main.resources.srcDirs[0]}/webui"
exclude "*.gz"
//eachFile { println it.name }
}

task downloadRedoc(type: Download) {
src "https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js&quot;
dest "${sourceSets.main.resources.srcDirs[0]}/public/redoc"
overwrite true
}

task var &lt;&lt; {
sourceSets {
main {
println "java.srcDirs = ${java.srcDirs}"
println "resources.srcDirs = ${resources.srcDirs[0]}"
println "output.classesDir = ${output.classesDir}"
println "output.resourcesDir = ${output.resourcesDir}"
}
}
}

compileJava.dependsOn downloadRedoc
copyWebui.dependsOn removeWebui
compileJava.dependsOn copyWebui</pre>

  • Create controller for APIs “WebRestController

package com.test.practise.controller;

import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RefreshScope
@RestController
@RequestMapping("/api")
public class WebRestController {

@RequestMapping("test")
public String test(){
return "test api data";
}


}
  • Create a view controller for enabling angular routes
<pre>package com.test.practise.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import springfox.documentation.annotations.ApiIgnore;


@Slf4j
@ApiIgnore
@Controller
public class ViewController {

    @RequestMapping({ "/","/welcome" })
    public String views() {
        return "forward:/index.html";
    }

}</pre>

This view controller will enable the spring to resolve these URL as angular URL.

 

Now we are good to go.Run the spring boot application using gradle bootRun.it will start the application.Once you will try to react the application from the browser, it will navigate you to google login first.

On Successful login, you will see the angular page.So till now we are pretty much done with authentication part. Now comes the routing part.

Now try to navigate to welcome page and setting page using above router.In this case, the welcome route will be successful but setting URL will fail, as we have not mentioned it in ViewController.

But when we try the routes which are below one, every route will work fine, as it is using angular routes.

For reference, you can check the GIT REPO with sample working code.

 

Advertisements

Add Google Authentication using Firebase in React+Redux Application

Single page applications(SPA) are quite famous these days, they are easy to build thanks to all the available libraries and framework. Angular (by Google) and React(by Facebook) are the most famous options available to explore these days. Because of these, front-end applications are now easy to manage and maintain. But even if we are able to create this SPA using all the technologies, we still need some server side logic to persist our data, and most importantly we need authentication so that each user can perform an action in the scope they are authorized to do.

Below is the diagram which shows the authentication flow.

oauth_implicit

Quick Intro of Firebase

Formerly known as Google Cloud Messaging (GCM), Firebase Cloud Messaging (FCM) is a cross-platform solution for messages and notifications for Android, iOS, and web applications, which currently can be used at no cost.

Today we will try to build a simple SPA using React, Redux, and Firebase(it will provide the google authentication)

Prerequisite-

Installed Software

We will follow the https://github.com/jainamit333/react_google_authetication to walk through the development.

Branch Name:- vanilla

Create a directory name react-authentication

 mkdir google-authentication-react
 cd google-authentication-react

 

Create Directory Structure as follows

Screen Shot 2017-08-05 at 2.00.09 PM

Add following code to package.json 

{
"name": "google-authentication-react",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "npm run build; node server/index.js",
"start-dev": "nodemon server/index.js",
"build": "webpack -p",
"build-dev": "webpack -w",
"build-sass": "node-sass -w ./client/styles/main.scss -o ./client/styles/mainSheet",
"test": "echo \"Error: no test specified\" &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; exit 1",
"stats": "webpack --env production --profile --json &amp;amp;amp;amp;amp;gt; stats.json"
},
"dependencies": {
"axios": "^0.16.1",
"babel": "^6.5.2",
"babel-core": "^6.18.0",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-2": "^6.24.1",
"body-parser": "^1.17.1",
"bootstrap": "^4.0.0-alpha.6",
"css-loader": "^0.28.0",
"express": "^4.15.2",
"firebase": "^4.2.0",
"muicss": "^0.9.20",
"node-sass": "^4.5.2",
"react": "^15.6.1",
"react-addons-css-transition-group": "^15.6.0",
"react-addons-transition-group": "^15.6.0",
"react-dom": "^15.6.1",
"react-redux": "^5.0.4",
"react-router": "^3.0.0",
"react-router-dom": "^4.1.2",
"reactstrap": "^4.8.0",
"redux": "^3.6.0",
"redux-logger": "^3.0.1",
"redux-thunk": "^2.2.0",
"sass-loader": "^6.0.3",
"style-loader": "^0.16.1",
"volleyball": "^1.4.1",
"webpack": "^2.7.0",
"webpack-livereload-plugin": "^0.10.0"
},
"devDependencies": {
"chai": "^3.5.0",
"cross-env": "^3.1.4",
"expose-loader": "^0.7.3",
"mocha": "^3.1.2",
"nodemon": "^1.11.0",
"react-hot-loader": "^1.3.1",
"supertest": "^2.0.1",
"supertest-as-promised": "^4.0.1",
"webpack-dashboard": "^0.4.0",
"webpack-dev-server": "^2.6.1"
}
}

At line number 26 we have added the dependency for firebase.

Note:- This package.json may contain many extra dependencies as I have extracted it from my other project just for tutorial purpose.

Install all the added dependencies

npm install

Add web pack Config code


const path = require('path');
const LiveReloadPlugin = require('webpack-livereload-plugin');
const webpack = require('webpack');

module.exports = {
entry: './client/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'client/dist')
},
context: __dirname,
resolve: {
extensions: ['.js', '.jsx', '.json', '*']
},
devtool:'cheap-module-source-map',
devServer: {
inline: true,
contentBase: './dist',
port: 3001
},
module: {
rules: [{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
options: {
presets: ['react', 'es2015','stage-2']
}
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}]
},
plugins: [
new webpack.DefinePlugin({

'process.env.COSMIC_BUCKET': JSON.stringify(process.env.COSMIC_BUCKET),
'process.env.COSMIC_READ_KEY': JSON.stringify(process.env.COSMIC_READ_KEY),
'process.env.COSMIC_WRITE_KEY': JSON.stringify(process.env.COSMIC_WRITE_KEY)
}),
new LiveReloadPlugin({appendScriptTag: true})

]
};

 

React and Redux related files

  • server/index.js
&amp;amp;amp;amp;lt;pre&amp;amp;amp;amp;gt;const express = require('express');
const app = express();
const path = require('path');
const volleyball = require('volleyball');
app.use(volleyball);
//serve up static files
app.use(express.static(path.resolve(__dirname, '..', 'client')));
app.use(express.static(path.resolve(__dirname, '..', 'node_modules')));
app.use(function (err, req, res, next) {
    console.error(err);
    console.error(err.stack);
    res.status(err.status || 500).send(err.message || 'Internal server error.');
});

// handle every other route with index.html, which will contain
// a script tag to our application's JavaScript file(s).
app.get('*', function (request, response) {
    response.sendFile(path.resolve(__dirname, '..', 'client', 'index.html'))
});
//listen on port 3000
app.listen(process.env.PORT || 3000, function () {
    console.log("Rockin' out on port 3000 homie");
});&amp;amp;amp;amp;lt;/pre&amp;amp;amp;amp;gt;

We are starting the project on port 3000.

  • redux/actions/actions.js
var constants = {
START_AUTHENTICATING:'START_AUTHENTICATING',
AUTHENTICATION_SUCCESSFUL:'AUTHENTICATION_SUCCESSFUL',
AUTHENTICATION_FAILED:'AUTHENTICATION_FAILED',
ALREADY_LOGIN:'ALREADY_LOGIN',
LOGOUT:'LOGOUT',
LOGOUT_SUCCESSFUL:'LOGOUT_SUCCESSFUL',
LOGOUT_ERROR:'LOGOUT_ERROR',
}

export default constants;

We have created constants for actions which will be supported for out dummy application

  • redux/actions/auth.js
import constant from './actions'

export const startAuth = keyWord =&amp;amp;amp;gt; {

return {
type: constant.START_AUTHENTICATING,
authenticated:false,
authenticating:true
}
}

export const alreadyLogin = response =&amp;amp;amp;gt; {

return{
type:constant.ALREADY_LOGIN,
authenticated:true,
authenticating:false,
user:response

}
}

export const authError = error =&amp;amp;amp;gt; {

return {
type: constant.AUTHENTICATION_FAILED,
authenticated:false,
authenticating:false,
error
}
}

export const authSuccess = response =&amp;amp;amp;gt; {
return {
type: constant.AUTHENTICATION_SUCCESSFUL,
user:response,
authenticated:true,
authenticating:false
}
}

These are the various activities that will be spawn during our login lifecycle.
We usually create a different actions file for a different piece of flow.

  • redux/reducers/auth.js
import constants from '../actions/actions'

const auth = (state = [], action) => {
switch (action.type) {

case constants.START_AUTHENTICATING:
return Object.assign({}, state, {
authenticated: false,
authenticating: true,
user: action.user
}, ...state)
break;
case constants.AUTHENTICATION_FAILED:
return Object.assign({}, state, {
authenticated: false,
authenticating: false,
user: {}
}, ...state)
break;
case constants.AUTHENTICATION_SUCCESSFUL:
return Object.assign({}, state, {
authenticated: true,
authenticating: false,
user: action.user
}, ...state)
break;

case constants.ALREADY_LOGIN:
return Object.assign({}, state, {
authenticated: true,
authenticating: false,
user: action.user
}, ...state)
break;
default:
return state
}

}
export default auth

As you can see, on the success we are setting user and setting param authenticated and authenticating accordingly in every case.
Other two param are mostly if we want to add loading bar during the login process.

 

  • redux/reducer.js
import {combineReducers} from "redux";
import auth from "./reducers/auth";

const googleBooks = combineReducers({
auth
})

export default googleBooks

This is the place where we combine all our reducer.
For now, we have only one main reducer which we are adding in line 5.

redux/store.js

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';

const initialState = {
auth:{
authenticated:false,
authenticating:false,
user:{},
}
}

const store = createStore(
reducer,
initialState,
applyMiddleware(
createLogger(),
thunk
)

);
export default store;

We are creating a redux store and adding our combined reducer in line 15.

  • client/index.html
</pre>
<pre><!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Books Around You</title>
    			<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"
            integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
            crossorigin="anonymous"></script>

    <script src="/dist/bundle.js" defer></script>
</head>
<body>


<div id="root"></div>


</body>
</html></pre>
<pre>

Creating a placeholder for all our react component

  • client/index.js
</pre>
<pre>import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import store from '../redux/store';
import Dashboard from "./components/Dashboard";

ReactDOM.render(
    <Provider store={store}>
        <Dashboard />
    </Provider>,
    document.getElementById('root')
);</pre>
<pre>

we are adding dashboard component as out main component which will be added to root div

  • client/component/Dashboard.js
</pre>
<pre>import React, {Component} from "react";
import {connect} from "react-redux";
import Navigation from "./Navigation";
import {login} from "../../services/firebase/auth";
import UserInfoPanel from "./UserInfoPanel";

class Dashboard extends Component {

    componentDidMount() {
        login()
    }

    render() {
        return (


<div>
                <Navigation />


<div className="mui-row">
</div>



<div className="mui-row">
                    <UserInfoPanel/>
</div>


</div>


        )
    }
}

function mapStateToProp(state) {
    return state;
}

export default connect(mapStateToProp)(Dashboard)</pre>
<pre>

We are calling login method on component did mount.
So it will check if we are already login or not before mounting this component

  • client/component/Navigation.js
</pre>
<pre>import React from "react";
import {connect} from "react-redux";
import {login, logout} from "../../services/firebase/auth";

class Navigation extends React.Component {

    render() {

        var styles = {

            marginTop:{
                marginTop:'10px'
            },
            baseColor:{
                color:'#a83808'
            }

        }
        return (
            <nav className="navbar navbar-default">


<div className="container-fluid">


<div className="navbar-header">
                        <a className="navbar-brand" href="#" >
                            <span  className="glyphicon glyphicon-bishop " aria-hidden="true" style={styles.baseColor}></span>
                        </a>
</div>



<ul className="nav navbar-nav navbar-right ">


	<li>
                            {this.props.authenticated && <span style={styles.marginTop} onClick={logout} className="btn btn-sm btn-danger">Logout</span>}
                            {!this.props.authenticated && <span style={styles.marginTop} onClick={login(this.props.dispatch)} className="btn btn-sm btn-danger">Login</span>}
</li>


</ul>


</div>


            </nav>
        );
    }
}

function mapStateToProps(state) {
    return {
        authenticated: state.auth.authenticated,
    }
}

export default connect(mapStateToProps)(Navigation)</pre>
<pre>

If the user is login LOGOUT button will render another wise LOGIN button.

  • client/component/UserInfoPanel.js
</pre>
<pre>import React from 'react';
import {connect} from "react-redux";

class UserInfoPanel extends React.Component {

    render() {
        const styles = {
            card:{
                width: '20em',
                position: 'relative',
                display: 'flex',
                flexDirection:'column',
                backgroundColor: '#fff',
                border: '1px solid rgba(0,0,0,.125)',
                borderRadius: '.25rem',
                padding:'3px',
                margin:'2px'
            }
        }
        return (


<div className="card" style={styles.card}>
                { this.props.authenticated && <img className="card-img-top img-thumbnail" src = {this.props.user.photoURL} alt="Card image cap"/> }
                { this.props.authenticated &&


<div className="card-block">


<h4 className="card-title">{this.props.user.displayName}</h4>


{this.props.user.email}

</div>


                }
</div>


        );
    }
}

function mapStateToProps(state) {
    return {

        authenticated: state.auth.authenticated,
        authenticating: state.auth.authenticating,
        user: state.auth.user
    }
}

export default connect(mapStateToProps)(UserInfoPanel)</pre>
<pre>

This component is to show the information of the login user.
In highlighted part, you can check we are mapping state param to props of the component.
This is a connected component.

Firebase related Files

  • services/firebase/config.js
import firebase from 'firebase'

const config = {

apiKey: "<your api key from google developer console.>",
authDomain: "<auth domain from firebase project eg >",
databaseURL: "<databse url from firebase>",
storageBucket: "<storage bucket from firebase>",

}

firebase.initializeApp(config);
export const provider = new firebase.auth.GoogleAuthProvider();
provider.addScope('https://www.googleapis.com/auth/plus.login')
export const firebaseAuth = firebase.auth

Replace the config part from your configs.
On line 12 we are initializing firebase.
On line, we are creating a google authentication provider.
Line no 14 states the scope of google api we are using, for now, we only need plus.login,
as we are using only from authentication.
When you need some other access also add other scopes to the same provider in next line.

  • services/firebase/auth.js
import { firebaseAuth, provider} from './config'
import {alreadyLogin, authError, authSuccess, startAuth} from "../../redux/actions/auth";
import store from '../../redux/store'

export function logout () {

return firebaseAuth().signOut()
}

function doLogin() {

firebaseAuth().signInWithPopup(provider).then(function(result) {
store.dispatch(authSuccess(result.user))

}).catch(function(error) {
store.dispatch(authError(error))
});
}

export function login () {

firebaseAuth().onAuthStateChanged((response) => {
if(response){
store.dispatch(alreadyLogin(response))
}else{
store.dispatch(startAuth())
doLogin()
}
});
return
}

In login method we are first checking of user is already login or not.
If not it will called do login method otherwise, it will called the action login already.

In doLogin method we are login usingn google provider.
If it is successful we are calling authSuccess action otherwise
authError action.

NOTE:- Since we are using Google authentication from firebase, it has to be enabled in firebase.Inside your firebase project go to authentication, go to sign-in-method and enable google provied.