9 minute read

A walkthrough of integrating with Webex using OAuth2

Introduction

“I know kung fu.”

This article details how to use OAuth2 to authorize a Webex integration to act on behalf of a user with certain permissions. I’ll show the authorization code grant type in abstract, how this process works with Webex, and how to implement it as a Python integration.

Recently, I built a simple integration for Webex that required permission to act on behalf of users. The integration is a message scheduling service. Using a web app, Webex users can schedule messages to another person. The goal is to provide a way for people to send messages during the recipient’s active hours and avoid their off hours. Personally, I want to be mindful of others’ working and non-working hours. I enjoy this feature in email and thought it’d be nice to preserve others’ time and space when off work on Webex too.

Before writing any code, developers must create a Webex integration at developer.webex.com. An integration allows devs to ask a user to authorize their app to act on their behalf with certain permissions. You’ve likely encountered the same process when logging into an app with Google, Facebook, or Apple. For identifying users an app would typically request minimal permissions to publicly visible information, like the account name. Other apps acting on people’s behalf may ask for more permissions on the platform. For example, the DocuSign add-on for Google Workspace. It requests to read emails, files, email addresses, etc.

The industry standard protocol that allows this cross-app authorization is called OAuth2. It comes to us from the IETF and is pervasively implemented. There are a number of RFCs that define the framework and specifications and there is an excellent website, oauth.com. The website has simplified explanations, examples, and a playground to learn and experiment with a real deployment.

There are multiple types of OAuth2 authorization flows. Webex uses the authorization code grant flow. The flow essentially gives integrations an authorization code that it can use to request an access token for the granting user. This allows apps to authenticate with the token and act on behalf of the user who granted the permission.

This is the Webex authorization code flow:

  1. Webex user clicks on the integration’s authorization link
  2. Webex user is prompted to authenticate to Webex (with SSO and MFA, hopefully)
  3. Webex user is presented with an authorization page and asked if the integration should be authorized to act on their behalf within the scopes listed
  4. Webex user grants authorization (or doesn’t, which ends the flow)
  5. Webex redirects the user back to the integration’s redirect URI with an authorization code, i.e. callback
  6. The integration uses the authorization code value to request an access token
  7. The integration can now use the access token to act on the user’s behalf within the scopes requested (e.g., send a message or schedule a meeting)

Now let’s dive into actually using OAuth2 with the Webex platform. We’re going to create an integration on Webex, construct our authorization link for Webex users to click on, process the callback from Webex, use the authorization code to acquire an access token, and access Webex resources on behalf of the granting user.

Create a Webex Integration

“Never send a human to do a machine’s job.”

First we must create a Webex integration. Integrations are created at developer.webex.com. Click on the avatar in the top right hand corner and click My Webex Apps. Then choose Create a New App. The New App options are Embedded App, Bot, Integration, and Guest Issuer. They all have different characteristics and you can learn more about them in the docs. For our purposes, choose Integration.

Name this integration, write a description, and choose an icon. After this, enter a redirect URI. This is the URI Webex will redirect a user to after they grant this integration permissions. Our integration’s redirect URI will be the web address of our app with the path or route that will receive the authorization code from Webex. For example, https://myapp.example.com/auth. In my case, I built the message scheduling service on AWS Lambda and API Gateway, so I chose an endpoint on API Gateway of /auth to accept the redirect from Webex.

The last bit of configuration to provide are the scopes this integration requires. Scopes are the levels of access required to perform services for users on the Webex platform. Scopes should be limited to the least privileges integrations require. For my message scheduling service, I chose Write Messages and Read Directory. My integration needs to be able to send messages as the authorizing user and read the directory to search for recipients based on name.

After completing the form, click Add Integration. After the Webex platform creates our integration, key pieces of information are presented: Client ID, Client Secret, and OAuth Authorization URL.

The client ID is used to identify integrations when users land on the Webex permission grant page. It is visible to users. The client secret is used by integrations when requesting an access token from Webex. It is not visible to users. The OAuth Authorization URL is the Webex address where users grant integrations permission on the Webex platform. Apps must redirect Webex users to this link somehow.

After creation we can change the name, redirect URI, icon, and scopes. We cannot change the client ID, client secret, or Integration ID. If scopes change, the OAuth Authorization URL will change, so make sure to update any links accordingly.

You just created an integration! Wasn’t that easy? Now let’s do something with it!

“I can only show you the door, you’re the one that has to walk through it.”

For users to correctly land on the Webex authorization page for an integration, a link must contain all of the important information Webex needs. Fortunately, Webex provides an OAuth Authorization URL when integrations are created. The parameters in the URL are complete for our specific integration except the state parameter.

State is an ephemeral parameter used to identify and validate users’ attempts to authorize an integration. State value in the authorization URL is passed through as a parameter in redirect URI callback. This is useful for verifying a valid callback. More on state later.

I’ll walk through an entire authorization URL to help clarify what it actually looks like for Webex. Afterwards, I’ll expand on the state parameter and its use.

Example OAuth Authorization URL:

https://webexapis.com/v1/authorize?client_id=123&response_type=code&redirect_uri=https://myapp.example.com/auth&scope=spark%3Akms%20spark%3Apeople_read&state=STATE_VALUE
  1. Client ID is constant for an integration and provided when created.
  2. Response type is always code because Webex uses the OAuth2 Authorization Code grant flow
  3. Redirect URI is the web location where an integration processes redirects/callbacks from Webex.
  4. Scope is a URL encoded list of access permissions the user may grant to an integration.
  5. State is a value that Webex will pass as a parameter when redirecting to an integration’s redirect URI.

Integrations set the state value in requests and validate it when redirects are received from Webex as a way to verify valid authorization attempts. The primary value of doing this is to mitigate CSRF attacks. If an integration sets and validates the state parameter, an attacker sending an unsolicited callback to the redirect URI would be ignored rather than processed as a valid callback.

To generate a state value for my message scheduling integration, I used the secrets module in Python’s standard library to make a URL safe token. The default length of tokens is 32 bytes. Devs could also encode session information into the state value. For example, the last page the user visited or a user identifier.

import secrets
new_token = secrets.token_urlsafe()

Process the Response or Callback from Webex

“What was said was for you, and you alone.”

The redirect URI for an integration is the location Webex will redirect users once they grant permissions on the platform. At this URI, there must be a server, function, etc. waiting to accept the callback from Webex. This callback is the vehicle for the authorization code.

Here is an example callback:

https://myapp.example.com/auth?code=123&state=123

The code parameter is the authorization code an integration uses to request a Webex access token. The state parameter is whatever value is set in the authorization link. In my example integration above, I used a random 32 byte URL safe token.

After receiving this callback, my example integration validates the state value in the database. If it exists, continue processing the callback. If not, ignore the callback.

Example callback handler:

@app.route('/auth', methods=['GET'])
def auth():
   # Get request dict
   request = app.current_request
   # Get ephemeral state to verify correct OAuth flow
   state = request.query_params.get('state')
   stored_state = db.get_item(key=state)
   If stored_state == state:
      # proceed

Request an Access Token from Webex

“What do all men with power want? More power.”

After validating the state value, request an access token from Webex with the authorization code, client ID, client secret, and redirect URI. The access token request will look similar to this:

POST https://webexapis.com/v1/access_token
HEADERS
Content type: application/x-www-form-urlencoded
BODY
grant_type=authorization_code&cliend_id=123&cliend_secret=123&code=123&redirect_uri=https://myapp.example.com/auth

Webex will respond with the access token:

{
    "access_token": "123",
    "expires_in": 1209600, //seconds
    "refresh_token": "123",
    "refresh_token_expires_in": 7776000 //seconds
}

To handle this, and other Webex API interaction in my example integration, I used the wonderful webexteamssdk. It’s an excellent Python SDK created by Chris Lundsford. If Python is your backend flavor, check it out! Otherwise, there are many great Webex SDKs to choose from for Node.js, Java, Go, Ruby, C#, PHP, Elixir, Perl, and more.

Using webexteamssdk, we can easily request an access token:

from webexteamssdk import WebexTeamsAPI

wbxapi = WebexTeamsAPI(client_id='123',
                        client_secret='123',
                        oauth_code='123',
                        redirect_uri='https://myapp.example.com/auth')
token = wbxapi.access_token

After parsing the response from Webex and storing the tokens, we can now interact with the Webex API on behalf of the person who granted permission.

Access Webex Resources

“With great power comes great responsibility.”

Now our integration has permissions granted by the user on the Webex platform and can exercise them via an access token. Now let’s use our newly acquired access to do something useful. We’re going to send a message to someone. Here we’ll rely on the webexteamsdk to do most of the work. This article is about integrating with Webex via OAuth2 and not the Webex API in general, so I won’t go through the details of making API calls.

Continuing on from our instantiation of the WebexTeamsAPI above, we can get more information about the user with the handy me() function of the person object:

person = wbxapi.people.me()
print(person.nickName)
Sam

This object has been hydrated with lots of information from the Webex API like displayName, nickName, emails, avatar, timezone, and more. Of course, at this point an integration can do anything on Webex within the authorized scopes.

Summary

Good bye, Mr. Anderson.

In this article we…

  1. Created a Webex integration.
  2. Made an authorization link for users to grant our integration access.
  3. Processed the response or callback from Webex once users have granted access.
  4. Requested an access token from Webex with the authorization code we parsed from the callback.
  5. Used the Webex API (via an SDK) to gather user details.

I focused mostly on using OAuth2 authorization code grant flow on the Webex platform but added some context around creating an integration that I hope was helpful. Below you’ll find resources and further reading helpful when building an integration using OAuth2 on Webex. Thank you for reading!

Resources and Further Reading

OAuth2 docs
https://www.oauth.com/

Authorization Code Grant
https://www.oauth.com/oauth2-servers/server-side-apps/authorization-code/

OAuth2 Playground
https://www.oauth.com/playground/

Webex Integration docs
https://developer.webex.com/docs/integrations

Link authorization request with redirect URI with the state parameter
https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.1.8

Webex API SDKs
https://developer.webex.com/docs/sdks/community

Webexteamssdk
https://github.com/CiscoDevNet/webexteamssdk https://webexteamssdk.readthedocs.io/

Example Integration
https://github.com/sambyers/mindful-messages

Quotes taken from Spider-Man and The Matrix.

Updated: