Skip to content

Commit 0d3677a

Browse files
RoachRodneyU215
authored andcommitted
Add automatic access token rotation for workspace apps (#347)
1 parent 05dacba commit 0d3677a

30 files changed

+625
-269
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ cov_*
2121
# due to using tox and pytest
2222
.tox
2323
.cache
24+
.pytest_cache/
25+
.python-version
26+
pip

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// Place your settings in this file to overwrite default and user settings.
22
{
33
"python.linting.pylintEnabled": false,
4-
"python.linting.flake8Enabled": true
4+
"python.linting.flake8Enabled": true,
5+
"python.venvPath": "./venv",
6+
"python.pythonPath": "${workspaceFolder}/venv/bin/python",
7+
"python.formatting.provider": "black",
8+
"editor.formatOnSave": true,
59
}

README.rst

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ Whether you're building a custom app for your team, or integrating a third party
2525
service into your Slack workflows, Slack Developer Kit for Python allows you to leverage the flexibility
2626
of Python to get your project up and running as quickly as possible.
2727

28+
Documentation
29+
***************
30+
31+
For comprehensive method information and usage examples, see the `full documentation <http://slackapi.github.io/python-slackclient>`_.
32+
33+
If you're building a project to receive content and events from Slack, check out the `Python Slack Events API Adapter <https://github.com/slackapi/python-slack-events-api/>`_ library.
34+
35+
You may also review our `Development Roadmap <https://github.com/slackapi/python-slackclient/wiki/Slack-Python-SDK-Roadmap>`_ in the project wiki.
36+
2837

2938
Requirements and Installation
3039
******************************
@@ -43,16 +52,8 @@ by pulling down the source code directly into your project:
4352
git clone https://github.com/slackapi/python-slackclient.git
4453
pip install -r requirements.txt
4554
46-
Documentation
47-
--------------
48-
49-
For comprehensive method information and usage examples, see the `full documentation <http://slackapi.github.io/python-slackclient>`_.
50-
51-
52-
You may also review our `Development Roadmap <https://github.com/slackapi/python-slackclient/wiki/Slack-Python-SDK-Roadmap>`_ in the project wiki.
53-
5455
Getting Help
55-
-------------
56+
*************
5657

5758
If you get stuck, we’re here to help. The following are the best ways to get assistance working through your issue:
5859

@@ -68,7 +69,6 @@ This package is a modular wrapper designed to make Slack `Web API <https://api.s
6869
app. Provided below are examples of how to interact with commonly used API endpoints, but this is by no means
6970
a complete list. Review the full list of available methods `here <https://api.slack.com/methods>`_.
7071

71-
See `Tokens & Authentication <http://slackapi.github.io/python-slackclient/auth.html#handling-tokens>`_ for API token handling best practices.
7272

7373
Sending a message
7474
********************
@@ -79,6 +79,7 @@ To send a message to a channel, use the channel's ID. For IMs, use the user's ID
7979

8080
.. code-block:: python
8181
82+
import os
8283
from slackclient import SlackClient
8384
8485
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -99,6 +100,7 @@ as sending a regular message, but with an additional ``user`` parameter.
99100

100101
.. code-block:: python
101102
103+
import os
102104
from slackclient import SlackClient
103105
104106
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -127,6 +129,7 @@ appear directly in the channel, instead relegated to a kind of forked timeline d
127129

128130
.. code-block:: python
129131
132+
import os
130133
from slackclient import SlackClient
131134
132135
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -145,6 +148,7 @@ set the ``reply_broadcast`` boolean parameter to ``True``.
145148

146149
.. code-block:: python
147150
151+
import os
148152
from slackclient import SlackClient
149153
150154
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -174,6 +178,7 @@ Sometimes you need to delete things.
174178

175179
.. code-block:: python
176180
181+
import os
177182
from slackclient import SlackClient
178183
179184
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -196,6 +201,7 @@ This method adds a reaction (emoji) to an item (``file``, ``file comment``, ``ch
196201

197202
.. code-block:: python
198203
204+
import os
199205
from slackclient import SlackClient
200206
201207
slack_token = os.environ["SLACK_API_TOKEN"]
@@ -230,22 +236,12 @@ At some point, you'll want to find out what channels are available to your app.
230236

231237
.. code-block:: python
232238
233-
from slackclient import SlackClient
234-
235-
slack_token = os.environ["SLACK_API_TOKEN"]
236-
sc = SlackClient(slack_token)
237-
238239
sc.api_call("channels.list")
239240
240241
Archived channels are included by default. You can exclude them by passing ``exclude_archived=1`` to your request.
241242

242243
.. code-block:: python
243244
244-
from slackclient import SlackClient
245-
246-
slack_token = os.environ["SLACK_API_TOKEN"]
247-
sc = SlackClient(slack_token)
248-
249245
sc.api_call(
250246
"channels.list",
251247
exclude_archived=1
@@ -259,11 +255,6 @@ Once you have the ID for a specific channel, you can fetch information about tha
259255

260256
.. code-block:: python
261257
262-
from slackclient import SlackClient
263-
264-
slack_token = os.environ["SLACK_API_TOKEN"]
265-
sc = SlackClient(slack_token)
266-
267258
sc.api_call(
268259
"channels.info",
269260
channel="C0XXXXXXX"
@@ -277,11 +268,6 @@ Channels are the social hub of most Slack teams. Here's how you hop into one:
277268

278269
.. code-block:: python
279270
280-
from slackclient import SlackClient
281-
282-
slack_token = os.environ["SLACK_API_TOKEN"]
283-
sc = SlackClient(slack_token)
284-
285271
sc.api_call(
286272
"channels.join",
287273
channel="C0XXXXXXY"
@@ -299,18 +285,71 @@ joined one by accident. This is how you leave a channel.
299285

300286
.. code-block:: python
301287
302-
from slackclient import SlackClient
303-
304-
slack_token = os.environ["SLACK_API_TOKEN"]
305-
sc = SlackClient(slack_token)
306-
307288
sc.api_call(
308289
"channels.leave",
309290
channel="C0XXXXXXX"
310291
)
311292
312293
See `channels.leave <https://api.slack.com/methods/channels.leave>`_ for more info.
313294

295+
296+
Tokens and Authentication
297+
**************************
298+
299+
The simplest way to create an instance of the client, as shown in the samples above, is to use a bot (xoxb) access token:
300+
301+
.. code-block:: python
302+
303+
# Get the access token from environmental variable
304+
slack_token = os.environ["SLACK_API_TOKEN"]
305+
sc = SlackClient(slack_token)
306+
307+
308+
The SlackClient library allows you to use a variety of Slack authentication tokens.
309+
310+
To take advantage of automatic token refresh, you'll need to instantiate the client a little differently than when using
311+
a bot access token. With a bot token, you have the access (xoxb) token when you create the client, when using refresh tokens,
312+
you won't know the access token when the client is created.
313+
314+
Upon the first request, the SlackClient will request a new access (xoxa) token on behalf of your application, using your app's
315+
refresh token, client ID, and client secret.
316+
317+
.. code-block:: python
318+
319+
# Get the access token from environmental variable
320+
slack_refresh_token = os.environ["SLACK_REFRESH_TOKEN"]
321+
slack_client_id = os.environ["SLACK_CLIENT_ID"]
322+
slack_client_secret = os.environ["SLACK_CLIENT_SECRET"]
323+
324+
325+
Since your app's access tokens will be expiring and refreshed, the client requires a callback method to be passed in on creation of the client.
326+
Once Slack returns an access token for your app, the SlackClient will call your provided callback to update the access token in your datastore.
327+
328+
.. code-block:: python
329+
330+
# This is where you'll add your data store update logic
331+
def token_update_callback(update_data):
332+
print("Enterprise ID: {}".format(update_data["enterprise_id"]))
333+
print("Workspace ID: {}".format(update_data["team_id"]))
334+
print("Access Token: {}".format(update_data["access_token"]))
335+
print("Access Token expires in (ms): {}".format(update_data["expires_in"]))
336+
337+
# When creating an instance of the client, pass the client details and token update callback
338+
sc = SlackClient(
339+
refresh_token=slack_refresh_token,
340+
client_id=slack_client_id,
341+
client_secret=slack_client_secret,
342+
token_update_callback=token_update_callback
343+
)
344+
345+
346+
Slack will send your callback function the **app's access token**, **token expiration TTL**, **team ID**, and **enterprise ID** (for enterprise workspaces)
347+
348+
349+
See `Tokens & Authentication <http://slackapi.github.io/python-slackclient/auth.html#handling-tokens>`_ for API token handling best practices.
350+
351+
352+
314353
Additional Information
315354
********************************************************************************************
316355
For comprehensive method information and usage examples, see the `full documentation`_.

docs-src/auth.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@ Tokens & Authentication
55

66
Handling tokens and other sensitive data
77
----------------------------------------
8-
Slack tokens are the keys to your—or your customers’—teams. Keep them secret. Keep them safe. One way to do that is to never explicitly hardcode them.
8+
⚠️ **Slack tokens are the keys to your—or your customers’—data.Keep them secret. Keep them safe.**
9+
10+
One way to do that is to never explicitly hardcode them.
911

1012
Try to avoid this when possible:
1113

1214
.. code-block:: python
1315
1416
token = 'xoxb-abc-1232'
1517
16-
If you commit this code to GitHub, the world gains access to this token’s team. Rather, we recommend you pass tokens in as environment variables, or persist them in a database that is accessed at runtime. You can add a token to the environment by starting your app as:
18+
⚠️ **Never share test tokens with other users or applications. Do not publish test tokens in public code repositories.**
19+
20+
We recommend you pass tokens in as environment variables, or persist them in a database that is accessed at runtime. You can add a token to the environment by starting your app as:
1721

1822
.. code-block:: python
1923
2024
SLACK_BOT_TOKEN="xoxb-abc-1232" python myapp.py
2125
22-
Then in your code retrieve the key with:
26+
Then retrieve the key with:
2327

2428
.. code-block:: python
2529
@@ -34,13 +38,15 @@ You can use the same technique for other kinds of sensitive data that ne’er-do
3438

3539
For additional information, please see our `Safely Storing Credentials <https://api.slack.com/docs/oauth-safety>`_ page.
3640

37-
Test Tokens
41+
Single-Workspace Apps
3842
-----------------------
39-
During development (prior to implementing OAuth) you can use a test token provided by the `Test Token Generator <https://api.slack.com/docs/oauth-test-tokens>`_. These tokens provide access to your private data and that of your team.
43+
If you're building an application for a single Slack workspace, there's no need to build out the entire OAuth flow.
4044

41-
**Tester tokens are not intended to replace OAuth 2.0 tokens.** Once your app is ready for users, replace this token with a proper OAuth token implementation.
45+
Once you've setup your features, click on the **Install App to Team** button found on the **Install App** page.
46+
If you add new permission scopes or Slack app features after an app has been installed, you must reinstall the app to
47+
your workspace for changes to take effect.
4248

43-
**Never share test tokens with other users or applications. Do not publish test tokens in public code repositories.**
49+
For additional information, see the `Installing Apps <https://api.slack.com/slack-apps#installing_apps>`_ of our `Building Slack apps <https://api.slack.com/slack-apps#installing_apps>`_ page.
4450

4551
The OAuth flow
4652
-------------------------

docs-src/metadata.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
.. _Contributing: https://github.com/slackapi/python-slackclient/blob/master/.github/contributing.md
1515
.. _contributing guidelines: https://github.com/slackapi/python-slackclient/blob/master/.github/contributing.md
1616
.. _Contributor License Agreement: https://docs.google.com/a/slack-corp.com/forms/d/e/1FAIpQLSfzjVoCM7ohBnjWf7eDYQxzti1EPpinsIJQA5RAUBwJKRUQHg/viewform
17-
.. _Real Time Messaging API: https://api.slack.com/rtm
17+
.. _Real Time Messaging (RTM) API: https://api.slack.com/rtm
1818
.. _Web API: https://api.slack.com/web
1919

docs-src/real_time_messaging.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
.. _real-time-messaging:
22

33
==============================================
4-
Real Time Messaging
4+
Real Time Messaging (RTM)
55
==============================================
6-
The `Real Time Messaging API`_ is a WebSocket-based API that allows you to
6+
The `Real Time Messaging (RTM) API`_ is a WebSocket-based API that allows you to
77
receive events from Slack in real time and send messages as users.
88

99
If you prefer events to be pushed to you instead, we recommend using the
@@ -13,7 +13,7 @@ in `the Events API <https://api.slack.com/events/api>`_.
1313

1414
See :ref:`Tokens & Authentication <handling-tokens>` for API token handling best practices.
1515

16-
Connecting to the Real Time Messaging API
16+
Connecting to the RTM API
1717
------------------------------------------
1818
::
1919

docs/_static/doctools.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ jQuery.fn.highlightText = function(text, className) {
7070
if (node.nodeType === 3) {
7171
var val = node.nodeValue;
7272
var pos = val.toLowerCase().indexOf(text);
73-
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
73+
if (pos >= 0 &&
74+
!jQuery(node.parentNode).hasClass(className) &&
75+
!jQuery(node.parentNode).hasClass("nohighlight")) {
7476
var span;
7577
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
7678
if (isInSVG) {

docs/_static/documentation_options.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var DOCUMENTATION_OPTIONS = {
2-
URL_ROOT: '',
2+
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
33
VERSION: '1.0.1',
44
LANGUAGE: 'None',
55
COLLAPSE_INDEX: false,

docs/about.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
<li class="toctree-l1"><a class="reference internal" href="index.html">Slack Developer Kit for Python</a></li>
4848
<li class="toctree-l1"><a class="reference internal" href="auth.html">Tokens &amp; Authentication</a><ul>
4949
<li class="toctree-l2"><a class="reference internal" href="auth.html#handling-tokens-and-other-sensitive-data">Handling tokens and other sensitive data</a></li>
50-
<li class="toctree-l2"><a class="reference internal" href="auth.html#test-tokens">Test Tokens</a></li>
50+
<li class="toctree-l2"><a class="reference internal" href="auth.html#single-workspace-apps">Single-Workspace Apps</a></li>
5151
<li class="toctree-l2"><a class="reference internal" href="auth.html#the-oauth-flow">The OAuth flow</a></li>
5252
</ul>
5353
</li>
@@ -75,8 +75,8 @@
7575
<li class="toctree-l2"><a class="reference internal" href="conversations.html#get-conversation-members">Get conversation members</a></li>
7676
</ul>
7777
</li>
78-
<li class="toctree-l1"><a class="reference internal" href="real_time_messaging.html">Real Time Messaging</a><ul>
79-
<li class="toctree-l2"><a class="reference internal" href="real_time_messaging.html#connecting-to-the-real-time-messaging-api">Connecting to the Real Time Messaging API</a></li>
78+
<li class="toctree-l1"><a class="reference internal" href="real_time_messaging.html">Real Time Messaging (RTM)</a><ul>
79+
<li class="toctree-l2"><a class="reference internal" href="real_time_messaging.html#connecting-to-the-rtm-api">Connecting to the RTM API</a></li>
8080
<li class="toctree-l2"><a class="reference internal" href="real_time_messaging.html#rtm-start-vs-rtm-connect">rtm.start vs rtm.connect</a></li>
8181
<li class="toctree-l2"><a class="reference internal" href="real_time_messaging.html#rtm-events">RTM Events</a></li>
8282
<li class="toctree-l2"><a class="reference internal" href="real_time_messaging.html#sending-messages-via-the-rtm-api">Sending messages via the RTM API</a></li>

0 commit comments

Comments
 (0)