For implementation steps, skip to Step 2.

Update 7/13/12:
Fixed IE Bug by enclosing popup URL access block within try/catch (tested in IE9)

1. Theory

Each and every website that provides some service have a login/registration system to manage their users. Traditionally, users are required to register with the website, create an account and then only can access their services. This means user need to remember a lot of different credentials for each of the sites. But now a days, the scenario is changing with OpenID.
Users can now create a single account with any OpenID provider like Google, Facebook and can use the same account to access services of many of the websites.

Open ID

OpenID is an open standard that describes how users can be authenticated in a decentralized manner, eliminating the need for services to provide their own ad hoc systems and allowing users to consolidate their digital identities. – Wikipedia

Authentication

Authentication is the mechanism whereby systems may securely identify their users. 

Authorization

Authorization, by contrast, is the mechanism by which a system determines what level of access a particular authenticated user should have to secured resources controlled by the system.

2. Google Accounts Authentication and Authorization

Google provides a generic, simple and excellent procedure for authentication and authorization of google API’s in here: https://developers.google.com/accounts/. But in this article we will see how to actually implement the procedure in a real web page using javascript. Also, we will be doing all the authentication and authorization flow in a separate popup window.
While developing a HTML5 Web application, especially games it would be hard to restore you app state or game state if you direct your user to OAuth provider and the provider redirects the user back to you app after successful login. So instead of directing the user to the OAuth provider in the same application, it can be done in a separate pop window, without affecting your app. This technique will be very much useful in many scenarios.

Step 1: Registering the Application

  • Register an application in Google APIS Console @ https://code.google.com/apis/console.
  • Goto API Access page.
  • Create a client ID in that page by selecting Web Application and set Redirect URL to some blank page in your web site. Just an empty page is enough. Lets say the Redirect URL (URL of that empty page) be www.example.com/oauth/
  • Note down your Client ID.
  • Enough theory we will get into coding.

Step 2: Basic Web Page setup

Create an HTML page and with a basic structure as follows:
<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
        var OAUTHURL    =   'https://accounts.google.com/o/oauth2/auth?';
        var VALIDURL    =   'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=';
        var SCOPE       =   'https://www.googleapis.com/auth/userinfo.profile';
        var CLIENTID    =   '433322211111.apps.googleusercontent.com';
        var REDIRECT    =   'http://localhost:8888/MAMP/html5/oauth/'
        var TYPE        =   'token';
        var _url        =   OAUTHURL + 'scope=' + SCOPE + '&client_id=' + CLIENTID + '&redirect_uri=' + REDIRECT + '&response_type=' + TYPE;
        function login() {

        }
    </script>
</head>

<body>
    <a href='#' onClick='login();'> click here to login </a>
    <div id='uName'>Welcome  </div>
    <img src='' id='imgHolder'/>
</body>
</html>
We just created a simple HTML page with a link that says ‘click here to login’ and an empty div to display the user name and an dummy image file to show the user’s profile picture. Please refer to https://developers.google.com/accounts/docs/OAuth2Login for detailed theories on OAuth.  Then in javascript we defined 7 variables,
  • OAUTHURL – This is the google end point to get access token
  • VALIDURL – This URL is to validate the access token.
  • SCOPE – This variables refers to various resources / services(APIs) we would like to access which can include Analytics API, Youtube API, Blogger API. Before we access them here, the corresponding API access should be turned on Google APIS Console’s ‘Services’ page. Since we just want basic user’s info our scope is https://www.googleapis.com/auth/userinfo.profile
  • CLIENTID – This is ID that you noted down in Step1
  • REDIRECT – This is the URL of the empty page that you mentioned in Google APIS Console.
  • TYPE – its always token, i suppose.
All these variables are combined to form the authentication and authorization URL and assigned to _url variable. As of now when the link is pressed an empty function is just invoked. Nothing happens.

Step 3: Authentication and Authorization in popup

Lets show the actual authentication dialog now in a ‘popup’. Redefine the login function as follows:
        function login() {
            var win         =   window.open(_url, "windowname1", 'width=800, height=600'); 

            var pollTimer   =   window.setInterval(function() { 
                try {
                    console.log(win.document.URL);
                    if (win.document.URL.indexOf(REDIRECT) != -1) {
                        window.clearInterval(pollTimer);
                        var url =   win.document.URL;
                        acToken =   gup(url, 'access_token');
                        tokenType = gup(url, 'token_type');
                        expiresIn = gup(url, 'expires_in');
                        win.close();

                        validateToken(acToken);
                    }
                } catch(e) {
                }
            }, 100);
        }
        function validateToken(token) {

        }

        //credits: http://www.netlobo.com/url_query_string_javascript.html
        function gup(url, name) {
            name = name.replace(/[[]/,"\[").replace(/[]]/,"\]");
            var regexS = "[\?&]"+name+"=([^&#]*)";
            var regex = new RegExp( regexS );
            var results = regex.exec( url );
            if( results == null )
                return "";
            else
                return results[1];
        }
Now the login function opens up a pop window with the URL that we just formed and setup a timer that constantly checks if the pop up page URL matches the Redirect URL, which will happen after successful login is made. Once user logged in and authorized the application, he will be redirected to the url REDIRECT. Once this happens, the timer is stopped, access token, expiry time are retrieved from the popup URL and then the popup is closed. Next step is to validate the access token.

What is access token?
Access Token is a string returned by OAuth server after successful authorization. For all further API requests, the access token need to be passed.

Step 4: Validating Access token

        function validateToken(token) {
            $.ajax({
                url: VALIDURL + token,
                data: null,
                success: function(responseText){  
                    getUserInfo();
                },  
                dataType: "jsonp"  
            });
        }

Replace the empty validateToken with the function above. Its a very straight forward ajax call using jquery to the validation URL. We are passing our access token to the Validation URL. On success, we call another function, getUserInfo() which retrieves the user’s information.

Step 5: Retrieving User Info

        function getUserInfo() {
            $.ajax({
                url: 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + acToken,
                data: null,
                success: function(resp) {
                    user    =   resp;
                    console.log(user);
                    $('#uName').append(user.name);
                    $('#imgHolder').attr('src', user.picture);
                },
                dataType: "jsonp"
            });
        }

Its another ajax call to get the user info. Access Token is passed along with the call. On successful response, the placeholder HTML elements are updated with appropriate user’s name and picture.

Step 6: Logging out

Logging out of the session seemed to be a trivial one. Quick googling revealed, just pointing to http://accounts.google.com/Logout does the job. So I wrote an AJAX call that POSTed to the logout URL. But it always returned some error. So I opened up the URL in a popup window and this does the job perfectly, except that there is no way for me to close the pop once the session is logged out ,since I cant access URL of popup window as the domain is google.com (cross-domain issues) and also there is no way I could redirect to my domain after logging out.

I was scratching my head on what to do and this thread on SO came to the rescue. The point is to make an iframe and load the logout url in that frame and then hiding the iframe by setting its display style to none as follows:

    <!-- inside body tag -->
    <a href="#" style="display:none" id="logoutText" target='myIFrame' onclick="myIFrame.location='https://www.google.com/accounts/Logout'; startLogoutPolling();return false;"> Click here to logout </a>
    <iframe name='myIFrame' id="myIFrame" style='display:none'></iframe>

    //inside script tag
    function startLogoutPolling() {
        $('#loginText').show();
        $('#logoutText').hide();
        loggedIn = false;
        $('#uName').text('Welcome ');
        $('#imgHolder').attr('src', 'none.jpg');
    }

But still there is a catch. This code allows the user to login again immediately after the logout procedure is started. So when you click fast enough on login immediately after you clicked on logout, then there is a high chance that you get logged into previous session automatically without asking for user credentials. This could be fixed by adding some delay (say 2 seconds) and allowing user to login only after that delay.

Full Code goes Here:

<!DOCTYPE html>
<html>
<head>
    <script src="jquery.js"></script>
    <script>
        var OAUTHURL    =   'https://accounts.google.com/o/oauth2/auth?';
        var VALIDURL    =   'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=';
        var SCOPE       =   'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email';
        var CLIENTID    =   '716569014051.apps.googleusercontent.com';
        var REDIRECT    =   'http://www.gethugames.in/proto/googleapi/oauth/'
        var LOGOUT      =   'http://accounts.google.com/Logout';
        var TYPE        =   'token';
        var _url        =   OAUTHURL + 'scope=' + SCOPE + '&client_id=' + CLIENTID + '&redirect_uri=' + REDIRECT + '&response_type=' + TYPE;
        var acToken;
        var tokenType;
        var expiresIn;
        var user;
        var loggedIn    =   false;

        function login() {
            var win         =   window.open(_url, "windowname1", 'width=800, height=600'); 

            var pollTimer   =   window.setInterval(function() { 
                try {
                    console.log(win.document.URL);
                    if (win.document.URL.indexOf(REDIRECT) != -1) {
                        window.clearInterval(pollTimer);
                        var url =   win.document.URL;
                        acToken =   gup(url, 'access_token');
                        tokenType = gup(url, 'token_type');
                        expiresIn = gup(url, 'expires_in');
                        win.close();

                        validateToken(acToken);
                    }
                } catch(e) {
                }
            }, 500);
        }

        function validateToken(token) {
            $.ajax({
                url: VALIDURL + token,
                data: null,
                success: function(responseText){  
                    getUserInfo();
                    loggedIn = true;
                    $('#loginText').hide();
                    $('#logoutText').show();
                },  
                dataType: "jsonp"  
            });
        }

        function getUserInfo() {
            $.ajax({
                url: 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + acToken,
                data: null,
                success: function(resp) {
                    user    =   resp;
                    console.log(user);
                    $('#uName').text('Welcome ' + user.name);
                    $('#imgHolder').attr('src', user.picture);
                },
                dataType: "jsonp"
            });
        }

        //credits: http://www.netlobo.com/url_query_string_javascript.html
        function gup(url, name) {
            name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
            var regexS = "[\\#&]"+name+"=([^&#]*)";
            var regex = new RegExp( regexS );
            var results = regex.exec( url );
            if( results == null )
                return "";
            else
                return results[1];
        }

        function startLogoutPolling() {
            $('#loginText').show();
            $('#logoutText').hide();
            loggedIn = false;
            $('#uName').text('Welcome ');
            $('#imgHolder').attr('src', 'none.jpg');
        }

    </script>
</head>

<body>
    <a href='#' onClick='login();' id="loginText"'> Click here to login </a>
    <a href="#" style="display:none" id="logoutText" target='myIFrame' onclick="myIFrame.location='https://www.google.com/accounts/Logout'; startLogoutPolling();return false;"> Click here to logout </a>
    <iframe name='myIFrame' id="myIFrame" style='display:none'></iframe>
    <div id='uName'></div>
    <img src='' id='imgHolder'/>
</body>
</html>

Update Nov 25, 2015:

Deleting all `Thank` comments, leaving out important discussions.