Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slack API revokes an access token after some time when running the code in Lambda #1075

Open
jota12x opened this issue May 7, 2024 · 4 comments
Assignees
Labels
question Further information is requested

Comments

@jota12x
Copy link

jota12x commented May 7, 2024

Hi, I'm implementing a single-organization Slack app that receives data from a workflow form and creates some resources in AWS. To accomplish this, my app has a custom function that the code listens to, similar to this:

# imports ...
app = App(
    # Get it from Settings > Basic Information > App Credentials > Signing Secret
    signing_secret=os.environ["SLACK_SIGNING_SECRET"],
    # Get it from Settings > Install App
    token=os.environ["SLACK_BOT_TOKEN"], 
    # This setting is a must for AWS Lambda
    process_before_response=True,
)


@app.function("run_create_sso_entra_request")
def handle_create_sso_entra_request(ack: Ack, inputs: dict, say: Say, complete: Complete, fail: Fail):
    ack()
    logger.info(inputs)
    channel_id = inputs['channel_id']
    thread_ts = None
     try:
        entra_request = EntraRequest(inputs)
        logger.info(entra_request)
        blocks = build_blocks_entra(entra_request)
       # send message to channel
        thread_ts = say(channel=channel_id, blocks = blocks)['ts']
      # create resources using entra request
      # aws stuff ....
      ....
      # this is not sent :(
       say(channel=channel_id, text=f"Request completed", thread_ts=thread_ts) 
       complete()
     except Exception as e:
       .....



# Redirect logins to AWS Lambda
SlackRequestHandler.clear_all_log_handlers()
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG)
logger = logging.getLogger()

def handler(event, context):
    # Adapter for Slack to run on AWS Lambda
    slack_event = SlackEvent(event)
    if slack_event.body and slack_event.body.type == 'url_verification':
            return {
                "challenge": slack_event.body.challenge
            }
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

My app works locally, however when deploying it to Lambda, the Slack API revokes the access token after the AWS resource creation, resulting in the second message being ignored

024-05-07T14:08:24.829+09:00	File "/tmp/sls-py-req/slack_sdk/web/client.py", line 2564, in chat_postMessage
2024-05-07T14:08:24.829+09:00
return self.api_call("chat.postMessage", json=kwargs)

return self.api_call("chat.postMessage", json=kwargs)
2024-05-07T14:08:24.829+09:00	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-05-07T14:08:24.829+09:00	File "/tmp/sls-py-req/slack_sdk/web/base_client.py", line 155, in api_call
2024-05-07T14:08:24.829+09:00	return self._sync_send(api_url=api_url, req_args=req_args)
2024-05-07T14:08:24.829+09:00	^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-05-07T14:08:24.829+09:00	File "/tmp/sls-py-req/slack_sdk/web/base_client.py", line 186, in _sync_send
2024-05-07T14:08:24.829+09:00	return self._urllib_api_call(
2024-05-07T14:08:24.829+09:00	^^^^^^^^^^^^^^^^^^^^^^
2024-05-07T14:08:24.829+09:00	File "/tmp/sls-py-req/slack_sdk/web/base_client.py", line 317, in _urllib_api_call
2024-05-07T14:08:24.829+09:00	).validate()
2024-05-07T14:08:24.829+09:00	^^^^^^^^^^
2024-05-07T14:08:24.829+09:00	File "/tmp/sls-py-req/slack_sdk/web/slack_response.py", line 199, in validate
2024-05-07T14:08:24.829+09:00	raise e.SlackApiError(message=msg, response=self)
2024-05-07T14:08:24.829+09:00	slack_sdk.errors.SlackApiError: The request to the Slack API failed. (url: https://www.slack.com/api/chat.postMessage)
2024-05-07T14:08:24.829+09:00	The server responded with: {'ok': False, 'error': 'token_revoked'}

Furthermore, if I remove the AWS processing, both messages are sent to the channel, so I suspect the problem lies on the expiration time for the access token. My questions are:

  • Why would this happen?
  • How can I control the expiration time of the access tokens?
  • Would implementing token rotation solve the problem? If so, why?

Reproducible in:

The slack_bolt version

slack-bolt 1.19.0rc1

Python runtime version

3.11.0
(Paste the output of python --version)

OS info

arm64 (lambda)

@hello-ashleyintech hello-ashleyintech self-assigned this May 7, 2024
@hello-ashleyintech hello-ashleyintech added the question Further information is requested label May 7, 2024
@hello-ashleyintech
Copy link

hello-ashleyintech commented May 7, 2024

Hi, @jota12x! Thank you for submitting this question 😄

We consulted internally about how long tokens delivered in the function_executed event payload last for and this is what they said re: Why would this happen?:

The tokens expire 15 minutes after being issued and are revoked immediately when the step is completed, so whichever of those comes first. In most cases the step only takes a few seconds and then we immediately revoke the token.

To answer the other questions:

How can I control the expiration time of the access tokens?

As far as I know, there is no way to manipulate this with the APIs or SDKs - this is preset based on internal code.

Would implementing token rotation solve the problem? If so, why?

This scenario seems edge case-y, so I'm unsure if token rotation would solve this at all. Based on the token expiration criteria mentioned above where the tokens either expire 15 mins after being issued OR are revoked when the step is completed, I would first suggest looking into if complete() is being called anywhere prior to the say() function - it seems like something is signaling function completion within the resource certain that ends up skipping the remaining body of the function.

I hope this helps - let me know if you have further questions! 🙌

@jota12x
Copy link
Author

jota12x commented May 8, 2024

@hello-ashleyintech Thank you for your prompt and throughout answer.

I would first suggest looking into if complete() is being called anywhere prior to the say() function - it seems like something is signaling function completion within the resource certain that ends up skipping the remaining body of the function.

Sorry, I forgot to add complete() when pasting my code. I checked and only used it once after the second say() function. Leaving aside complete() and fail(), I only call the API twice, one per each say().

The tokens expire 15 minutes after being issued and are revoked immediately when the step is completed, so whichever of those comes first. In most cases the step only takes a few seconds and then we immediately revoke the token.

My lambda function runs for around two minutes. Is that too much? For instance, if I comment enough lines, the second message certainly reaches the API.

By the way, I ran my app in Socket mode, and it works as expected. Is there any difference for the token's handling in Socket mode? 🤔

@hello-ashleyintech
Copy link

@jota12x It does seems like something in the lambda function is running too long - could it be that it takes a while to actually initialize and start the Lambda function with its dependencies?

I recommend checking your code with these examples to make sure that it aligns with how you're calling your function.

By the way, I ran my app in Socket mode, and it works as expected. Is there any difference for the token's handling in Socket mode? 🤔

It could have something to do with Socket Mode requiring an app token. I'm not 100% sure on this though, but you can learn more about the app token vs. bot token here.

@jota12x
Copy link
Author

jota12x commented May 13, 2024

@hello-ashleyintech Thank you for the resources. I continued testing and realized that the token is revoked if the Lambda runs for more than one second. Maybe is related to this doc .

When building workflows using functions, there is a 60 second timeout for a deployed function and a 15 second timeout for a locally-run function.

So, is it possible to run lazy code inside a custom function or workflow step? 🤔 From what I checked, the documentation only refers to commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants