Search code examples
javascriptgoogle-calendar-apigoogle-oauth

Can't signOut of Google OAuth2


I am not able to use the Google signOut() to sign me out of an oAuth session I am signed into.

As an example, I have pasted the below code taken directly from the Google quick start site... https://developers.google.com/google-apps/calendar/quickstart/js

I have saved it to a 1.htm file on a website and am attempting to drive the Google API.

When I use the OAuth 2.0 client ID (not shown in the attached code) from my API console, I am able to login and see my calendar events. However, when the Sign Out button is clicked, I don't get logged out.

enter image description here

Right after clicking Sign Out, the following code is running for sure...

enter image description here

But then, it doesn't sign me out. Running from the Console, I see it still thinks I am logged in... enter image description here

I am listening for any changes to status on signIn our signOut... enter image description here

But then the callback is never hit for signOut()... enter image description here

Finally, if I try the entire thing from the developer console...

  1. Shows gapi.auth2 says I am logged in
  2. Manual sign out command
  3. Shows gapi.auth2 says I am STILL logged in enter image description here

How can this code be modified to make signOut() work?

<!DOCTYPE html>
<html>
<head>
    <title>Google Calendar API Quickstart</title>
    <meta charset='utf-8' />
</head>
<body>

<p>Google Calendar API Quickstart</p>

<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize-button" style="display: none;">Authorize</button>
<button id="signout-button" style="display: none;">Sign Out</button>

<pre id="content"></pre>

<script type="text/javascript">
    // Client ID and API key from the Developer Console
    var CLIENT_ID = 'MY OAUTH CLIENT ID';

    // Array of API discovery doc URLs for APIs used by the quickstart
    var DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];

    // Authorization scopes required by the API; multiple scopes can be
    // included, separated by spaces.
    var SCOPES = "https://www.googleapis.com/auth/calendar";

    var authorizeButton = document.getElementById('authorize-button');
    var signoutButton = document.getElementById('signout-button');

    /**
     *  On load, called to load the auth2 library and API client library.
     */
    function handleClientLoad() {
        gapi.load('client:auth2', initClient);
    }

    /**
     *  Initializes the API client library and sets up sign-in state
     *  listeners.
     */
    function initClient() {
        gapi.client.init({
            discoveryDocs: DISCOVERY_DOCS,
            clientId: CLIENT_ID,
            scope: SCOPES
        }).then(function () {
            // Listen for sign-in state changes.
            gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);

            // Handle the initial sign-in state.
            updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
            authorizeButton.onclick = handleAuthClick;
            signoutButton.onclick = handleSignoutClick;
        });
    }

    /**
     *  Called when the signed in status changes, to update the UI
     *  appropriately. After a sign-in, the API is called.
     */
    function updateSigninStatus(isSignedIn) {
        if (isSignedIn) {
            authorizeButton.style.display = 'none';
            signoutButton.style.display = 'block';
            listUpcomingEvents();
        } else {
            authorizeButton.style.display = 'block';
            signoutButton.style.display = 'none';
        }
    }

    /**
     *  Sign in the user upon button click.
     */
    function handleAuthClick(event) {
        gapi.auth2.getAuthInstance().signIn();
    }

    /**
     *  Sign out the user upon button click.
     */
    function handleSignoutClick(event) {
        gapi.auth2.getAuthInstance().signOut();
    }

    /**
     * Append a pre element to the body containing the given message
     * as its text node. Used to display the results of the API call.
     *
     * @param {string} message Text to be placed in pre element.
     */
    function appendPre(message) {
        var pre = document.getElementById('content');
        var textContent = document.createTextNode(message + '\n');
        pre.appendChild(textContent);
    }

    /**
     * Print the summary and start datetime/date of the next ten events in
     * the authorized user's calendar. If no events are found an
     * appropriate message is printed.
     */
    function listUpcomingEvents() {
        gapi.client.calendar.events.list({
            'calendarId': 'primary',
            'timeMin': (new Date()).toISOString(),
            'showDeleted': false,
            'singleEvents': true,
            'maxResults': 10,
            'orderBy': 'startTime'
        }).then(function (response) {
            var events = response.result.items;
            appendPre('Upcoming events:');

            if (events.length > 0) {
                for (i = 0; i < events.length; i++) {
                    var event = events[i];
                    var when = event.start.dateTime;
                    if (!when) {
                        when = event.start.date;
                    }
                    appendPre(event.summary + ' (' + when + ')')
                }
            } else {
                appendPre('No upcoming events found.');
            }
        });
    }

</script>

<script async defer src="https://apis.google.com/js/api.js"
        onload="this.onload=function(){};handleClientLoad()"
        onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>


Solution

  • Try using the revoking token HTTP/REST method:

    To programmatically revoke a token, your application makes a request to https://accounts.google.com/o/oauth2/revoke and includes the token as a parameter:

    curl -H "Content-type:application/x-www-form-urlencoded" \
            https://accounts.google.com/o/oauth2/revoke?token={token}
    

    The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.