I’ve been trying to allow users to login to my single-page web app written in Angular. I have a PHP and mySQL backend to store the data. I want to end up with an access token that can be validated by my backend. The problem I’ve been having is using the Google Quick Start for PHP (https://developers.google.com/+/quickstart/php). It has not been a quick start for me on this. While I was able to “relatively” quickly get this working as a standalone, my problem has been integrating with my Angular App.
While I’m not done, I think I’m on the right track. The biggest problem was if I had a simple link to GooglePlus’s Signin.php file, when I returned to my app, all my state data re-initialized. It is as if you press the refresh button in the browser. Also, I was unclear on how to put the access token into my application state.
My app uses a single complete HTML file with a div holding the ng-view directive, then using Angular’s $routeProvider to change parts of the page with snippets of html and different controllers. I want to integrate the Google Quick Start for PHP into this so that this app can communicate nicely and securely with the mySQL backend.
While watching codeschool.com‘s Angular course, I discovered the ng-include directive and with some testing, determined that I can pass it a PHP file. I was able to get decent results by creating a snippet of html
NOTE the double and single quotes. This tells Angular to take this expression literally so not to try to evaluate it. The id of gplusController I will use below.
This kept the user in my app and no loss of state data.
The signin.php file sends the index.html file (it’s in the same directory as signin.php) to the user plus adding the Google client-id and a anti-forgery state token using a template engine so it is incorporated into the HTML code.
My first task was to remove the extra HTML code like the body and html tags in the index.html file since we already have those tags from Angular. This file then only contained some divs and script tags.
I did have to change the url the $.ajax function in connectServer in the index.html file. Originally it was set to equal: window.location.href + ‘/connect?state={{ STATE }}’ Angularjs must change this window.location.href value, so I added the correct URL in a string. In my test environment, this became: ‘http://localhost/storycreate/sql/auth/signin.php/connect?state={{ STATE }}’
Once the user logs in, a function called onSignInCallback is called with the authorization results as a parameter. It is during this function the ajax call to the signin.php file is made with google’s one time authorization code and the anti-forgery state token created in the signin.php (created the first time we called signin.php). The authorization code allows our PHP server to obtain Google’s access-token. The anti-forgery code helps prevent malicious scripts from inundating our server. I want my Angular code to make this ajax call so that I can obtain the access-token and any results I want my server to give me. Specifically, I want this in a function in my GPlusController.
My first quandary was how to have the javascript in the index.html call my function in my Angular controller. I researched and found that one can access the $scope of a controller using the following code:
myControllersScope = angular.element($('#gplusController')).scope(); //calling my controller's function myControllersScope.onSignInCallback();
I used the id of an element that is in the part of the DOM the controller is active.
Google Plus’s code will look in JavaScript’s global namespace for a function named onSignInCallback to call when it returns from signing a user in. If you look in the index.html file, you will find the global function onSignInCallback that calls the helper.onSignInCallback function. I renamed the global function and created my own. In it, I obtained the anti-forgery token from the DOM. I forgot to mention, I altered the index.html template file so it will be in a easier place to obtain. I added the following to the index.html file right before the div with id=”gConnect”:
The {{ STATE }} is replaced for you automatically by the signin.php file using the templating engine twig
then in the same file toward the end, I renamed the existing onSignInCallback function and put my own.
//eventually we can delete this function onSignInCallbackOLD(authResult) { helper.onSignInCallback(authResult); } //This function returns the scope of my controller function gplusController() { return angular.element($("#gplusController")).scope(); } //The google plus javascript will call this function with the authResult. I get the anti-forgery token, afToken, from the DOM and pass authResult and afToken to my function in my Angular controller. function onSignInCallback(authResult){ var afToken = document.getElementById('afToken').getAttribute('data-afToken'); gplusController().onSignInCallback(authResult, afToken); }
This is as far as I’ve gotten so far. The next step will be using my onSignInCallback to call the signin.php file with the one-time authorization code so that the server can obtain the google access-token and confirm it’s valid thus proving the user’s identity. I’ll post another post as I get further and if I hit any other snags. If anyone knows an easier way, I’d love to hear about it.
This documentation was the most helpful for understanding the google logon process: https://developers.google.com/+/web/signin/server-side-flow