Backend Voting App Social Login Question


#1

Hey people,

I have been attempting the backend voting app and I am kinda stuck. I am doing this using the mean stack approach and I want to use facebook and twitter to authenticate for me instead of creating my own db and using local/jwt to do it.

What I have so far are the apis which display the information gotten from facebook/twitter at localhost:3000/users/profile,(using passport.authenticate()) but which are only accessible if I am able to login in the first place, as well as the angular side being able to retrieve another collection in the same mongo db that stores the polls and votes which allows for display and voting.

What I am stuck at is how do I go about redirecting from angular such that it navigates to the facebook/twitter login pages, is authenticated(thus allowing localhost:3000/users/profile to retrieve user profile information), and then redirecting it back to the angular side(which is then able to display the profile information gotten from facebook/twitter through ngOnInit() )? Would be glad if anybody is able to help out!


#2

I’m not sure you will find many Angular experts here. FCC focuses mainly on React.
I presume you have read:
http://passportjs.org/docs


#3

Hey thanks for your prompt reply! Yep I did read that. I am able to .render/.sendFile/.json pages in node/express using passport and the information gotten from facebook/twitter. But how am I able to do that in angular?
My plan is as follows:

  1. click a button e.g. “Login w/ FB” on the angular component .html page
  2. be redirected to fb/twitter account login page(like what happens if I do not use angular)
  3. enter login credentials into fb/twitter site
  4. be redirected back to another angular component which is then able to display the information gotten from localhost:3000/profile api through ngOnInit, that is now available because a user has logged in.

Maybe I am overlooking something here that is right under my nose???


#4

Are you using token based authentication? If so store the token in a cookie and use that.


#5

Check out this repo
It’s angular 1 but I think it’s still self explanatory


#6

Basically what you need to do it make an ajax request to express route that returns req.isAuthenticated() or req.user as soon as your application starts to know whether user if logged in or not. So this request should be placed in AppComponent ngOnInit lifecycle hook.

Also here is my project with angular 2 and passport google auth


#7

@JohnnyBizzel I don’t think I am using token based authentication.
@prohorova I get the part where after the user is authenticated, data is provided in an api e.g. localhost:3000/user in your nightlife app case and then you use it to check if user is logged in, because if not, localhost:3000/user will not give any information and hence the user name will not be displayed on the navbar.
What I don’t get is the part where clicking a button in angular2/4 leads to the login page of google/fb/twitter is authenticated and then back to angular. In your nightlife app case, your “Sign Up” button on your navbar is the one that links to google login and after authenticating, redirects back to the app.
Your “Sign Up” button is a link to /auth/google?path=${this.router.url} from getLoginLink() in your auth.service.ts. How does this lead to authentication???

Isn’t ‘localhost:3000/auth/google’ your link to the google authentication page? From what I understand, after authentication, the url should then go to localhost:3000/auth/google/callback right? From here how do I go back to angular???

*Btw your nightlife app looks awesome.


#8

I have been struggling with social authentication as well.

Yes, but you can use res.redirect(url) to link back to your angular app. I assume you are using the angular cli, so that would be http://localhost:4200/.

Then in your app.component.ts’s ngOnInit() do something like this:

this.http.get("/user").subscribe(data => { 
  // check if user is authenticated
}

I only quickly tested this, but I believe it should work.


#9

Thanks
Well, passport-google (or whatever module you’re using for social auth) handles all redirects for you. When you hit
passport.authenticate('google', {scope: ['https://www.googleapis.com/auth/plus.login']})
it automatically redirects to google authentication page and later back to localhost:3000/auth/google/callback (or whatever you configured). Then app will make a redirect based on what you specified in successRedirect and failRedirect
passport.authenticate('google', {successRedirect: '/', failureRedirect: '/'})
or you could do
res.redirect('/')


#10

Works on all points my friend!
Been struggling with social login too! What with local, social media, jwt, session storage, local storage… very confusing…

Thanks a ton!


#11

Hmmmm one quick question though. Your angular and node are both on the same port 3000? Since you are redirecting back to ‘/’?


#12

For this particular app yes


#13

Nice! Thank you guys for all your help!


#14

Hey people!

Reopening this thread again, because of another related problem I encountered. localhost:3000/profile which is my api end point of the information gotten from fb/twitter, shows up in the browser as JSON with no problems. However when I try to use it in angular after http.get in services as well as subscribing to it I obtain this error

SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data  

Whats even weirder is when I try it in postman, despite the information showing up in the browser at localhost:3000/profile, postman returns a blank response. Any ideas/insights as to why this is happening?


#15

Can you post your app.get("/profile") and the angular request part code?


#16

This part belongs to index.js in the ‘routes’ folder.

router.get('/login/facebook', passport.authenticate('facebook', {scope: ['email', 'public_profile', 'user_friends', 'user_photos']}));
router.get('/login/twitter', passport.authenticate('twitter'));
router.get('/login/facebook/return', passport.authenticate('facebook', { failureRedirect: '/login' }), function(req, res) {
    res.redirect('http://localhost:4200/allcurrentpolls');
  });
router.get('/login/twitter/return', passport.authenticate('twitter', { failureRedirect: '/login' }), function(req, res) {
    res.redirect('http://localhost:4200/allcurrentpolls');
  });  

router.get('/profile', function(req, res){
    res.json(req.user);
  });

This belongs to the service

@Injectable()
export class ServerService {
    constructor(private http: Http){}

    getPollsData(){
    return this.http.get('http://localhost:3000/allcurrentpolls')
        .map(
            (response: Response) => {
                return response.json();
            }
        );    
    
    }

    postPollVote(form: object){
    return this.http.post('http://localhost:3000/saveoption',form);
    }

    getUserData(){
    return this.http.get('http://localhost:3000/profile')
        
            
    
    }

This belongs to pollsmain.component.ts

testing(){
    this.serverService.getUserData()
    .subscribe(
      (response) => console.log(response),
      (error) => console.log(error)
    );
  }

This belongs to pollsmain.component.html

<div class="row">
  <div class="col-xs-12 list-group">
    <app-polltitles *ngFor = "let poll of polldata; let i = index" [polldat]="poll" [index]="i"></app-polltitles>
  </div>
</div>
<button class="btn btn-primary" (click)="testing()">console.log</button>

After stripping off .map and just trying to console.log I got this from the browser console where instead I should expect the api as a string in _body.

Object { _body: "", status: 200, ok: true, statusText: "OK", headers: Object, type: 2, url: "http://localhost:3000/profile" }

#17

In your /profile express route you are not checking if a user is logged in. Does it also return an empty string when you first hit the login route?

To check if everything is working, you can do something like this:

router.get('/profile', function(req, res){
  if(req.user){
    res.json(req.user);
  }else{
    res.json({error: "No user logged in"});
  }
});

Note that you will have to login every time you restart your server.


#18

Try
getUserData(){ return this.http.get('http://localhost:3000/profile') .map(res => res.json()) }


#19

@BenGitter /profile works, nothings wrong. Console.log returns profile json as well. But when I use angular I get this:

Object { _body: "", status: 200, ok: true, statusText: "OK", headers: Object, type: 2, url: "http://localhost:3000/profile" }

@prohorova If I use add back .map to the service I get:

SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data

#20

So did you login from http://localhost:4200?