4

I've been trying to implement Google OAuth service with the implicit flow for my Dialogflow account linking (Using this documentation)

I've developed the code according to that document. It works fine when a user links his account and shows "you've successfully, linked your account with chatbot".

But when I try to use the access token that I've generated in Step 3 of that document, it says that the access token is not valid. Looks like it doesn't register with the Google service!

Here is my code using which I've implemented it

import os
import flask
from flask import request, redirect
import string
import random
from random import choice, randint
import google_auth_oauthlib.flow
from AES import AESCipher
import json

CLIENT_SECRETS_FILE = "client_secret.json"

SCOPES = ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email']
app = flask.Flask(__name__)
app.secret_key = 'SECRET_KEY'

#
# Actions on google call this function with client_id, redirect_uri, state, and response_type
#
@app.route('/auth')
def authorize():
    flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE, scopes=SCOPES)

    flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

    print ("%%%%STATE", request.args["state"])
    authorization_url, state = flow.authorization_url(
        access_type='offline',
        include_granted_scopes='true')

    flask.session['state'] = request.args["state"]
    print ("IN AUTH STATE ", state)

    return flask.redirect(authorization_url)


def get_user_id():
    min_char = 8
    max_char = 12
    allchar = string.ascii_letters + string.punctuation + string.digits
    password = "".join(choice(allchar) for x in range(randint(min_char, max_char)))
    return password


@app.route('/oauth2callback')
def oauth2callback():
    print ("### IN CALLBACK ###", request)
    state = flask.session['state']

    user_id = get_user_id()
    client_id = ''.join(random.sample("Google", len("Google")))
    key = ''.join(random.sample("JayPatel", len("JayPatel")))
    bytes_obj = {
        'user_id': user_id,
        'client_id': client_id
    }
    access_token = AESCipher("Jay").encrypt(json.dumps(bytes_obj))
    access_token = access_token.replace('+', 'p')
    access_token = access_token.replace('&', '1')

    # My generated access_token looks like 
    # 6u2PeFcUtNmAblw5N3YAKSn7EC46xQVmgv3Rc8XK9sTW4xcgAxw1doRsvmgE/CC2qGUJ1x7XpNpdYvGes6qJHIEQSwvgEJt8hnYEUbm0qEA=
    print ("###### Access Token ", access_token)
    print ("###### State ", state)

    url = "https://oauth-redirect.googleusercontent.com/r/test-happierbot-4c514#access_token=" + access_token + "&token_type=bearer&state=" + state

    return redirect(url)


if __name__ == '__main__':
    os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

    app.run('localhost', 8080, debug=True)

When I try to get the information using access_token by https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=my_generated_token API, it gives

{
    "error": "invalid_token",
    "error_description": "Invalid Value"
}

Is there any other process to activate the access_token or am I missing any step?

Jay Patel
  • 2,341
  • 2
  • 22
  • 43

1 Answers1

3

If I'm following the code correctly, you're generating your own access token to return to the user, and then using google's "tokeninfo" end point to see if the token is valid?

Google has no concept of what your token is or means, so it returns that it is invalid. The Assistant uses your service to get a token, and returns it when it sends a message from the user. Otherwise, it just treats it as a bearer token.

Your service is expected to determine its validity based on whatever means you want (look it up in a table, reverse the process to decrypt and verify it, check for a valid signature, etc). Since Google doesn't know anything about the token, it can't determine if it is actually valid.

The tokeninfo service returns information about tokens that Google has generated.

Google does not let you use their token endpoint for OAuth Account Linking. Instead, you can use the Google Sign In for Assistant and this will give you an id token. You can use this along with regular Google Sign In through a website to get an access token and refresh token that you can use to gain access to a user's Google resources with their permission. (See this Stack Overflow answer for more details.)

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • Okay! Sounds good, Is there any way to generate tokens from google? And one more thing when I try to use Google OAuth to get the generated tokens from Google in Calendar API (https://github.com/gsuitedevs/python-samples/tree/master/calendar/quickstart), I found that it has `expires_in=3600` parameter set. How can I make it to lifetime? – Jay Patel Oct 23 '18 at 11:39
  • @JayPatel access tokens have a life time of one hour you cant change that. – Linda Lawton - DaImTo Oct 23 '18 at 11:42
  • @DaImTo So after every hour, chatbot user's need to link their account again!! It will suck! Is there any way to make them logged in? – Jay Patel Oct 23 '18 at 11:46
  • You only link a users account to your oauth server once. After that it will be up to your server to request a new access token from Googles server. The users should not see this so it wont suck. – Linda Lawton - DaImTo Oct 23 '18 at 11:48
  • Answer updated with more info about other Account Linking options which may help you more and a link to an SO question where I answer this scenario in more depth. – Prisoner Oct 23 '18 at 12:08