Authorizing access to 3P services

This guide explains how Hangouts Chat bots can set up authorized access to third-party (3P) resources for users.

Bots sometimes act as intermediaries for access to protected third-party resources: these are resources that the user can access on a remote system, but which the bot cannot directly access because they require authorization.

Your bot needs to establish authorization allowing it to access the third-party system on the user's behalf.

Introduction

In order to understand how your bot can authorize for third-party access, there are several concepts you should be familiar with.

Why bots need 3P auth

Consider the following scenario:

  • Suppose there is a Hangouts Chat user named Harilton.
  • Harilton is also a user of a third-party project-management app called PManager, used to create project tasks.
  • PManager also provides a Hangouts Chat bot: this bot lets users add PManager tasks from within Hangouts Chat.

In this scenario, the bot needs to authenticate as Harilton so it can add tasks for them on PManager. To do this, the bot must access PManager as if it were Harilton, but neither the bot nor Hangouts Chat knows Harilton's credentials. To gain access, the bot needs to have Harilton provide the credentials.

Read the rest of this guide to learn more about how to set up this form of access.

Mapping user IDs

Bots need to maintain a mapping between users' Google IDs and their IDs on the third-party system.

Bots normally will maintain an internal data structure that associates the Google ID, the third-party ID, and an access token for the third-party system.

User experience

From the user's point of view, authorization works as follows:

  • If the user starts a direct message with the bot, or they add the bot to the room explicitly using the Add people & bots menu:
    1. The bot sends a welcome message telling the user what it can do and telling them to type something like "sign in" to get started.
    2. The user types @bot sign in.
    3. The bot starts the standard auth & config flow below.
  • If the user @mentions the bot in a room, the bot immediately starts the standard auth & config flow below.

Authentication flow

This section describes in generic terms the authorization flow that bots should use to set up access to third-party resources. See also Standard auth & config flow for the recommended way to to implement this.

The following sequence diagram shows how a user might trigger the standard auth & config flow:

As described above, your bot will provide an authorization URL for users to log in. When the bot constructs this URL, the bot should encode the user's Google ID in the URL.

Once the user logs in, the flow should redirect to the bot, where the bot can then create a mapping from the user's Google ID to the third-party user ID and token. The bot can then use the mapping in all future messages to obtain the user's third-party ID and token. So the bot uses the verification token to validate that the message is actually from Hangouts Chat, and then it uses the cached user credentials to authenticate with the 3P service.

Configuration (optional): Some bots may require users to configure the bot when it is added. After the bot maps the user's Google ID to the third party ID, the bot may want to show a configuration screen, hosted by the third-party, that allows them to set up their app preferences. You can also use the redirect in the OAuth flow to take the user directly to the configuration screen.

Standard auth & config flow

Hangouts Chat provides a special response type to help you authenticate users and perform initial configuration. We encourage you to use this response type instead of creating your own custom flow, because it provides the following features:

  • The auth & config flow is kept private to the user in most cases. This means that when the bot returns sign in or configuration URLs, the URLs aren't visible to other room members, and other room members aren't unnecessarily disrupted.
  • The auth & config flow provides an optimized and uniform user experience for all bots. This lets users of your bot complete the process with familiarity and minimal cognitive load.

Overview of standard flow

The auth & config flow requires tight collaboration between Hangouts Chat and bots for the optimal user experience. The following diagram shows this flow:

The following paragraphs describe these steps in detail.

Step 1: User mentions a bot

The flow begins when a user posts a message that at-mentions a bot.

The bot may or may not be an existing member of the room; in either case it requires a message from the user to start the flow. Therefore, only MESSAGE events and ADDED_TO_SPACE events with a message are supported:

  • Supported: MESSAGE events
  • Supported: ADDED_TO_SPACE events that include a message
  • Don't trigger on: ADDED_TO_SPACE events without messages
  • Don't trigger on: All other event types

When a user's message at-mentions a bot, it's initially visible only to that user; other members of the room don't see the message.

Step 2: Hangouts Chat dispatches message to bot

Hangouts Chat dispatches the message event to the at-mentioned bot. The event contains the configCompleteRedirectUrl field. The URL in this field contains an encrypted payload and is unique for each message. This URL will be used in subsequent steps.

Step 3: Bot responds

When a bot receives a MESSAGE event or an ADDED_TO_SPACE event with a message, it has the opportunity to start an auth & config flow. Whether it actually starts the flow can depend on the specific message received.

Postponing the flow

In response to a message like "@bot help", a bot should be able to respond with a message without requiring auth. In such cases, the bot can respond with a Message resource as explained in the Creating new bots guide.

Initiating the flow

At some point the bot determines that the incoming message requires auth & config flow. To start the flow, the bot should return a response of the following form:

{
  "actionResponse": {
    "type": "REQUEST_CONFIG",
    "url": "<your-config-URL>"
  }
}

This tells Hangouts Chat to present the user with a private prompt, with a link to visit the provided config URL for authentication, authorization, or configuration.

Config URL for OAuth

One use case for the Auth & Config flow is for acquiring OAuth credentials. In such cases, a bot typically returns a config URL object that includes the following components:

  • OAuth service provider URL: The config URL starts with the sign-in URL of an OAuth service provider. For example, to acquire OAuth credentials for a Google account, the config URL starts with "https://accounts.google.com/".
  • OAuth callback URL: After signing in to the OAuth service provider and granting access, the flow redirects the user to a callback URL, which should be handled by the bot. In this handler, the bot extracts the OAuth credentials from query parameters and stores them for future use. It then issues an HTTP redirect that forwards the user to the configCompleteRedirectUrl that was provided in the original user message.
  • Custom State: The OAuth callback URL handler typically needs some information from the original user message, such as the configCompleteRedirectUrl to redirect to. For Google OAuth, the state parameter can be used to pass data to the OAuth callback URL. To prevent forgery by a malicious party, the state should be encrypted.

Example for generating and returning a config URL for Google OAuth in Python:

oauth2_callback_args = OAuth2CallbackCipher.encrypt({
    'user_name': event['user']['name'],
    'space_name': event['space']['name'],
    'thread_name': event['message']['thread']['name'],
    'redirect_url': event['configCompleteRedirectUrl'],
})
oauth2_flow = flow.Flow.from_client_secrets_file(
    OAUTH2_CLIENT_SECRET_FILE,
    scopes=API_SCOPES,
    redirect_uri=flask.url_for('on_oauth2_callback', _external=True))
oauth2_url, _ = oauth2_flow.authorization_url(
    access_type='offline',
    include_granted_scopes='true',
    state=oauth2_callback_args)
return flask.jsonify({
    'actionResponse': {
        'type': 'REQUEST_CONFIG',
        'url': oauth2_url,
    },
})

Step 4: Hangouts Chat handles response

The way that Hangouts Chat handles the response depends on whether it is initiating the auth & config flow or not.

Regular message

If the bot responds in step 3 with a regular message, Hangouts Chat converts the user's message to public (visible to other room members) and posts the bot's response message below.

Starting the flow

If the bot responds in step 3 with REQUEST_CONFIG, the user's message remains private (only visible to the initiating user). A prompt (also only visible to that user) appears below the user's message, and contains a button linked to the config URL in the bot's response. All of this interaction is hidden from other users in the room.

Step 5: User performs auth / config

When the user clicks the button in the prompt, Hangouts Chat opens a new browser tab and invokes the associated config URL.

Step 6: Bot handles successful auth / config

In its OAuth callback URL handler, a typical bot performs two tasks:

  1. Persist OAuth credentials: The bot extracts OAuth tokens (an access token and a refresh token) from query parameters, associates them with the sender of the original message, and stores all this information for future use.
  2. Redirect: The bot returns an HTTP response that redirects the user's browser tab to the configCompleteRedirectUrl provided in the original message.

Example OAuth callback URL handler in Python:

@app.route('/oauth2callback')
def on_oauth2_callback():
  oauth2_callback_args = OAuth2CallbackCipher.decrypt(
      flask.request.args['state'])
  user_name, space_name, thread_name, redirect_url = (
      oauth2_callback_args['user_name'],
      oauth2_callback_args['space_name'],
      oauth2_callback_args['thread_name'],
      oauth2_callback_args['redirect_url'])
  oauth2_flow = flow.Flow.from_client_secrets_file(
      OAUTH2_CLIENT_SECRET_FILE,
      scopes=PEOPLE_API_SCOPES,
      redirect_uri=flask.url_for('on_oauth2_callback', _external=True),
      state=flask.request.args['state'])
  oauth2_flow.fetch_token(authorization_response=flask.request.url)
  store = Store()
  store.put_user_credentials(user_name, oauth2_flow.credentials)
  logging.info(
      'Storing credentials for user %s and redirecting to %s',
      user_name,
      redirect_url)
  return flask.redirect(redirect_url)

Step 7: Hangouts Chat handles successful auth / config

When a user is successfully redirected to the configCompleteRedirectUrl provided in the original message, Hangouts Chat performs the following steps:

  1. Erase the prompt that was displayed to the initiating user
  2. Convert the original message to public, making it visible to other members of the room
  3. Dispatch the original message to the same bot a second time

When an event is "re-dispatched" in this way, it normally should be identical to the original event; however, there are some situations where the events may differ. For example, when a message mentions both bot A and bot B, the user will be able to edit the message text if bot A responds with a regular message before authenticating with bot B. In this case, bot B will receive the edited message text after the user completes authentication.

Step 8: Bot responds again

Now that the user has been authenticated and the bot has stored their credentials, the bot can perform the originally requested action and respond with an ordinary message. This step usually doesn't require special handling because it's just the normal processing path for requests from an authenticated user.

Step 9: Hangouts Chat handles response

When Hangouts Chat receives the bot's response from step 8, it is handled in the normal way.

Other users in the room see the message from the original user (converted to public in step 7) and its response, and don't see any traces of the auth & configuration flow.

Send feedback about...

Hangouts Chat API
Hangouts Chat API
Need help? Visit our support page.