Social Login using Angular and RESTful APIs
Wall Script
Wall Script
Sunday, July 29, 2018

Social Login using Angular and RESTful APIs

There are a number of advantages to implementing social login in your web applications. First of all, it best secures the user credentials, the user can use a single login for accessing multiple sites, this can automatically access the user details for further use, and many such. This article is about how to implement the social login with Facebook and Google using Angular and RESTful API. This social login helps to store social data of the logged in user into the database, so that it provides you valid user data like email, name, and others. Let’s see how this works, follow the live demo and code below.

Social Login using Angular and RESTful APIs


Live Demo


Database Design
To build the user social login system, you have to create an user table.

Users
User table contains all the users social details.
CREATE TABLE users(
user_id int AUTO_INCREMENT PRIMARY KEY,
email varchar(300),
name varchar(200),
provider varchar(50),
provider_id varchar(200),
provider_pic varchar(200),
token varchar(500));

You can check my previous tutorials for creating token-based API system.

Install Angular Command-line
Open your terminal or command prompt and execute the following command, this will install AngularCli globally. Before that you must setup your machine with latest Node.js software.
$ sudo npm install -g @angular/cli 

Create and Launch an Angular Project
Here ng new command will take care the project files. Use ng serve command to launch the application.
ng new angularFirebaseProject

cd angularFirebaseProject

ng serve

Project Launch
Angular Cli project default port is 4200. Open your browser and launch the URL http://localhost:4200


Get Started

This project requires login and home pages. Login page contains social login buttons and Home page for displaying the user details line name, email etc.

Generate Login Component
ng generating command will help you to create login component files, this will update app.module.ts all of the dependencies.
ng generate component login-page

installing component
  create src/app/login-page/login-page.component.css
  create src/app/login-page/login-page.component.html
  create src/app/login-page/login-page.component.spec.ts
  create src/app/login-page/login-page.component.ts
  update src/app/app.module.ts

Generate Home Component
$ ng generate component home-page

You will find all of the generated files in src folder.
Create Angular Cli Components

Create Auth API Service
Injectable component this contains a postData function for connecting the PHP REST APIs
$ng generate service authApi

Create a services folder inside src and move newly created auth-api service files.

auth-api.service.ts
This service helps you to communicate with APIs. This funcation contains a http.post method to connect APIs and returns with promise.
import { Injectable } from '@angular/core';
import {Http, Headers} from '@angular/http';
//const apiUrl = "https://apipaypal.9lessons.info/apipaypal/";
const apiUrl = "http://localhost/socialapi/";

@Injectable()
export class AuthAPIService {

constructor(public http: Http) {
    console.log('Hello AuthService Provider');
}

   postData(credentials, type) {
       return new Promise((resolve, reject) => {
       const headers = new Headers();
       this.http.post(apiUrl + type, JSON.stringify(credentials), {headers: headers})
       .subscribe(res => {
            resolve(res.json());
        }, (err) => {
        reject(err);
    });

});

}
}

Create User Service
User data distribution component. This contains all the user data actions like storing user info on local storage and navigating to pages.
$ng generate service user

Create a services folder inside src and move newly created user service files.

user.service.ts
This service helps you to sharing the data with Pages. Here async and await functions are the new of writing promises. If you want use rxjs observables.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@Injectable()
export class UserService {
constructor(public router: Router) {}

async storeData(data) {
    localStorage.setItem('userData', JSON.stringify(data));
    const newData = await this.getData();
    return this.router.navigate(['home'], newData);
}

getData() {
   return JSON.parse(localStorage.getItem('userData'));
}

sessionIn() {
   let A;
   if (this.getData()) {
       A = this.router.navigate(['home'], this.getData());
   }
   return A;
}

sessionOut() {
   let A;
   if (!this.getData()) {
     A = this.router.navigate(['']);
   }
   return A;
}

logOut() {
   localStorage.setItem('userData', '');
   localStorage.clear();
   return this.router.navigate(['']);
}
}

Social OAuth Login Implementation for Facebook and Google

Install angular-6-social-login
Install an angular social NPM project into project dependencies.
$ npm install angular-6-social-login --save

Create a Facebook Application

Step 1
Go to Facebook developer page and create anapplication.

Step 2
Dashboard scroll page down and choose platform as Web.

Step 3
Enter your website URL.

Step 4
It will turn on app login permissions.

Step 5
App dashboard your will find the client ID.

Step 5
In Dashboard your will

Create a Google Application

Step 1
Go to Google API Console create OAuth client ID

Step 2
Choose OAuth client ID.

Step 3
Select web application and enter your website url.

Step 4
It will generate client ID.


app.moudle.js
Import newly created modules like AuthApiService, AngularSocialLoginModule, etc. Implemented page routing with @angular/router module. For more understanding please visit my previous article Angular 4 TypeScript Google Account Email Login and Routing.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule} from '@angular/http';
import { AppComponent } from './app.component';
import { LoginPageComponent } from './login-page/login-page.component';
import { HomePageComponent } from './home-page/home-page.component';
import {
   SocialLoginModule,
   AuthServiceConfig,
   GoogleLoginProvider,
   FacebookLoginProvider
} from 'angular-6-social-login';

import { RouterModule, Routes } from '@angular/router';
import { AuthAPIService } from './services/auth-api.service';
import { UserService } from './services/user.service';

const routes: Routes = [
{ path: 'home', component: HomePageComponent },
{ path: '', component: LoginPageComponent }
];

export function getAuthServiceConfigs() {
const config = new AuthServiceConfig(
[
{
    id: FacebookLoginProvider.PROVIDER_ID,
    provider: new FacebookLoginProvider('Your_Facebook_Client_ID')
},
{
    id: GoogleLoginProvider.PROVIDER_ID,
    provider: new GoogleLoginProvider('Your_Google_Client_ID')
}
]
);
return config;
}
@NgModule({
declarations: [
   AppComponent,
   LoginPageComponent,
   HomePageComponent
],
imports: [
   BrowserModule,
   SocialLoginModule, HttpModule, RouterModule.forRoot(routes)
],
providers: [ AuthAPIService, UserService, {
provide: AuthServiceConfig,
useFactory: getAuthServiceConfigs
}],
bootstrap: [AppComponent]
})
export class AppModule { }


login-page.component.js
Here socialSignin function get the user social details and post request to apiConnection. Once API call is succesful, it will return user inserted data. Using localStorage storing the user data in app level.
import { Component, OnInit } from '@angular/core';
import {
   AuthService,
   FacebookLoginProvider,
   GoogleLoginProvider
} from 'angular-6-social-login';
import { AuthAPIService } from '../services/auth-api.service';
import { UserService } from '../services/user.service';
@Component({
   selector: 'app-login-page',
   templateUrl: './login-page.component.html',
   styleUrls: ['./login-page.component.css']
})
export class LoginPageComponent implements OnInit {

public responseData: any;
public userPostData = {
email: '',
name: '',
provider: '',
provider_id: '',
provider_pic: '',
token: ''
};

constructor(private socialAuthService: AuthService,
public authAPIService: AuthAPIService,
public user: UserService) {
this.user.sessionIn();
}

ngOnInit() {}

public socialSignIn(socialPlatform: string) {
let socialPlatformProvider;
if (socialPlatform === 'facebook') {
   socialPlatformProvider = FacebookLoginProvider.PROVIDER_ID;
} else if (socialPlatform === 'google') {
   socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID;
}

this.socialAuthService.signIn(socialPlatformProvider).then(userData => {
   this.apiConnection(userData);
});
}

apiConnection(data) {
   this.userPostData.email = data.email;
   this.userPostData.name = data.name;
   this.userPostData.provider = data.provider;
   this.userPostData.provider_id = data.id;
   this.userPostData.provider_pic = data.image;
   this.userPostData.token = data.token;

this.authAPIService.postData(this.userPostData, 'signup').then(
result => {
   this.responseData = result;
   if (this.responseData.userData) {
      this.user.storeData(this.responseData.userData);
   }
},
err => {
   console.log('error');
}
);
}
}


login-page.component.html
Created two buttons with click action to socialSignIn with provider value.
<button (click)="socialSignIn('facebook')" class="btn btn-social btn-facebook">
<span class="fa fa-facebook"></span> Sign in with Facebook</button>
<button (click)="socialSignIn('google')" class="btn btn-social btn-google">
<span class="fa fa-google"></span> Sign in with Google</button>

home-page.component.js
Home page check the local user data and prints, if not available it will redirect to login page. Logout function clears the local data with social data and it navigate to login page.
import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
import { AuthService } from 'angular-6-social-login';
@Component({
   selector: 'app-home-page',
   templateUrl: './home-page.component.html',
   styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
public data: any;
public userData: any;

constructor(
public user: UserService,
public socialAuthService: AuthService,
public route: ActivatedRoute
) {
this.userData = this.user.getData();
}

ngOnInit() {
   this.user.sessionOut();
}

logout() {
   this.socialAuthService.signOut().then(data => {
   this.user.logOut();
});
}
}



home-page.component.html
Printing the user data with logout button. Here the ? operator is an angular safe navigation operator, this will helps to avoid template  exceptions while data binding.
<div class="userData">
<h2>Welcome {{userData?.name}}</h2>
<b>Email</b>: {{userData?.email}}
<br/>
<b>Provider</b>: {{userData?.provider}}
<br/>
<b>Token</b>: {{userData?.token}}
<br/>
<b>ID</b>: {{userData?.id}}
<br/>
<img [src]="userData?.image" *ngIf="userData?.image" />
<br/>
<br/>
<button (click)='logout()' class="btn btn-logout"><span class="fa fa-sign-out"></span> Logout</button>
</div>


Protecting RESTful API from Extrenal Calls
You have to secure your API with source origin to avoid wrong post inputs.


.htacces
You can change the origin value in root level by changing *(here * referes to all sources) to your website URL. But this will apply all of your services.
RewriteEngine On

Header add Access-Control-Allow-Origin "*"
to
Header add Access-Control-Allow-Origin "http://www.yourwebsite.com"


Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

index.html
Included font-awesome CSS for social icons.
<?php
/* ### Srinivas Tamada ### */
/* ### https://www.9lessons.info ### */
require 'config.php';
require 'Slim/Slim.php';

\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
$app->post('/signup','signup'); /* User Signup */
$app->run();


function signup() {
$request = \Slim\Slim::getInstance()->request();
$data = json_decode($request->getBody());
$email=$data->email;
$name=$data->name;
$provider=$data->provider;
$token=$data->token;
$provider_pic=$data->provider_pic;
$provider_id=$data->provider_id;
try {

if($_SERVER['HTTP_ORIGIN'] && 
   $_SERVER['HTTP_ORIGIN'] == "http://www.yourwebsite.com"){
$emain_check = preg_match('~^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.([a-zA-Z]{2,4})$~i', $email);

if (strlen(trim($name))>0 && strlen(trim($email))>0 && $emain_check>0 )
{
   $db = getDB();
   $userData = '';
   $sql = "SELECT uid FROM social_users WHERE email=:email";
   $stmt = $db->prepare($sql);
   $stmt->bindParam("email", $email,PDO::PARAM_STR);
   $stmt->execute();
   $mainCount=$stmt->rowCount();
   $created=time();
   if($mainCount==0)
   {
   /*Inserting user values*/
   $sql1="INSERT INTO social_users(name,email,provider, provider_id, token, provider_pic)VALUES(:name,:email,:provider, :provider_id, :token, :provider_pic)";
   $stmt1 = $db->prepare($sql1);
   $stmt1->bindParam("name", $name,PDO::PARAM_STR);
   $stmt1->bindParam("provider_id", $provider_id,PDO::PARAM_STR);
   $stmt1->bindParam("provider", $provider,PDO::PARAM_STR);
   $stmt1->bindParam("email", $email,PDO::PARAM_STR);
   $stmt1->bindParam("token", $token,PDO::PARAM_STR);
   $stmt1->bindParam("provider_pic", $provider_pic,PDO::PARAM_STR);
   $stmt1->execute();
   $userData=internalUserDetails($email);
}
else{
  $userData=internalUserDetails($email);
}
$db = null;

if($userData){
  $userData = json_encode($userData);
  echo '{"userData": ' .$userData . '}';
} else {
  echo '{"error":{"text":"Enter valid data"}}';
}

}
else{
  echo '{"error":{"text":"Enter valid data"}}';
}
}
else{
  echo '{"error":{"text":"No access"}}';
}
}
catch(PDOException $e) {
  echo '{"error":{"text":'. $e->getMessage() .'}}';
}
}


/* ### internal Username Details ### */
function internalUserDetails($input) {
try {
  $db = getDB();
  $sql = "SELECT uid, name, email, provider , provider_id,  provider_pic FROM social_users WHERE email=:input";
  $stmt = $db->prepare($sql);
  $stmt->bindParam("input", $input,PDO::PARAM_STR);
  $stmt->execute();
  $usernameDetails = $stmt->fetch(PDO::FETCH_OBJ);
  $usernameDetails->token = apiToken($usernameDetails->uid);
  $db = null;
  return $usernameDetails;
  } catch(PDOException $e) {
    echo '{"error":{"text":'. $e->getMessage() .'}}';
   }
}
?>

Run Project
You need live hosting of testing social login.

Build Production Version
$ng build --prod

Firebase Deploy
Read more about firebase angular project deployment
$firebase deploy
web notification

18 comments:

  1. The Content were very helpful .

    ReplyDelete
  2. Just a warning: this code was made using Angular, not AngularJS! They are totally different because AngularJS (up to version 1.5) has been completely refactored and is now called Angular only (without the JS at the end). Other than that, the content is very good, congratulations!

    ReplyDelete
  3. Hi Srinivas, Thank you for the post.
    When I try facebook login from localhost
    I am getting error - POST http://localhost/socialapi/signup 404 (Not Found).
    When I try google login:
    I am getting error - ERROR {error: "popup_closed_by_user"}

    Can you please help?


    Thanks,
    Venkat

    ReplyDelete
    Replies
    1. Hi, it works only in your original domain, wont work in your local host.....

      Delete
  4. if i want to get country name and phone number of the user then how can i get them

    ReplyDelete
  5. wow sir nice post great sir

    aap bahut mast jankari dete hai

    gud sir

    ReplyDelete
  6. Hello sir,

    Thank you for sharing your experience with us.
    I am glad to read your article
    Hope you are enjoying the day

    ReplyDelete
  7. Hi,

    I have applied your code for google login but console getting error "Failed to load resource: net::ERR_BLOCKED_BY_CLIENT". Please help to resolve this error soon.

    Thanks

    ReplyDelete
  8. Could you please explain what does postData method in AuthAPIService does ? And also what does "apiUrl" represent ?

    ReplyDelete
  9. Could you please explain what does postdata method does ? And also what does "apiUrl" in AuthAPIService represent ?

    ReplyDelete
  10. Could you please explain how are you routing from login to home page ?

    ReplyDelete
    Replies
    1. Please follow this https://www.9lessons.info/2018/09/angular-lazyload-routing-route-guards.html

      Delete
  11. I might be missing something but it looks like you don't validate the auth token on the back end at all. It's a bad idea to accept any auth token without first validating it against the provider before accepting it.

    ReplyDelete
    Replies
    1. This case we are not using the social provider token. Yes, token validation is required, if you do something else.

      Delete

mailxengine Youtueb channel
Make in India
X