Skip to content

403 Error when executing 'slack/install' in GCP cloud functions #646

Description

@ggm1207

First of all, I apologize that I may not be able to write smoothly because my English skill is not good. I will write in as much detail as possible, so please ask again if there are any missing parts.

I'm implementing and debugging a slackbot in 2 environments. One is my Macbook and the other is GCP Cloud Functions.

As mentioned in the title, if I try to download(in workspace) the Slack bot using the url below, I will get a 403 Error (see the picture below). However, the problem is fine when I run Server using ngrok + flask on Macbook, but it only occurs when I run it in Cloud Functions.

  • url(example): {SERVER_ENDPOINT}/slack/install

image

I'm not sure if this is an Oauth issue or a GCP permission issue or something.

Reproducible in:

The slack_bolt version

  • slack-bolt==1.11.4
  • slack-sdk==3.13.0

Python runtime version

  • Python 3.9.1

OS info

  • Cloud Functions
    • Can't provide
  • Macbook
    • ProductName: macOS
    • ProductVersion: 11.3.1
    • BuildVersion: 20E241
    • Darwin Kernel Version 20.4.0: Thu Apr 22 21:46:47 PDT 2021; root:xnu-7195.101.2~1/RELEASE_X86_64

Steps to reproduce:

  1. Place the sample file somewhere on your computer.
  2. Deploy to Cloud Functions using the command below.
gcloud functions deploy {FUNCTION_NAME} \
    --region {REGION} \
    --allow-unauthenticated \
    --entry-point handler \
    --runtime python39 \
    --service-account {SERVICE_ACCOUNT} \
    --project {PROJECT_NAME} \
    --trigger-http \
    --env-vars-file ".env.json"
  1. Enter the url({SERVER_ENDPOINT}/slack/install) in the search bar to install Slackbot.

(I think it might be helpful) service-acoount has permisssions below:

  • Cloud Functions Developer
  • Cloud Functions Invoker
  • Cloud SQL Client

An easier reproduction would be to insert the code below into this tutorial.

flask_app = Flask(__name__)

@flask_app.route("/slack/install", methods=["GET"])
def install():
    return handler.handle(request)

@flask_app.route("/slack/oauth_redirect", methods=["GET"])
def oauth_redirect():
    return handler.handle(request)

Expected result:

I want 'slack/install' to be called well on the slack server deployed to Cloud Functions.

My goal is to implement and deploy a Slackbot.

Actual result:

This is an error that occurs before entering slack/install, so the above image is all.

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Activity

  1. mwbrooks commented on May 12, 2022

    @mwbrooks
    Member

    Hi @ggm1207 👋🏻

    Thanks for the question and your English was easy to read and very well written! 👌🏻

    The error message "Error: Forbidden" is returned by GCP. I'm not very experienced with GCP, but it looks like you will need configure the role that can access your Cloud Function. I came across this Stackoverflow article that explains how to configure GCP.

    I know other maintainer's are more experience with GCP and may be able to add more help, but I wanted to first make this suggestion.

  2. seratch commented on May 13, 2022

    @seratch
    Contributor

    Hi @ggm1207, the reason why the example uses the Flask adapter is that Google Cloud Functions runtime provides Flask compatible request and response interface. Unfortunately, the HTTP endpoint routing in the Flask app layer does not work for Cloud Functions.

    To serve the OAuth URLs on Google Cloud Functions , there are two options as below:

    • Implement your own adapter that handles both GET and POST requests like our AWS Lambda adapter does, plus use a single endpoint URL for both the OAuth flow (/slack/install + /slack/oauth_redirect) and event delivery (/slack/events)
    • Deploy three functions for GET /slack/install, GET /slack/oauth_redirect, and POST /slack/events

    I hope this helps.

  3. ggm1207 commented on May 13, 2022

    @ggm1207
    Author

    Hi @mwbrooks 👋

    I've already tried the suggested method in the link you've attached. But I had time to check it again and it was useful to check the issue again. 👍 👍

    Thank you for your answer !!

    Thank you for adding the question label.

  4. ggm1207 commented on May 13, 2022

    @ggm1207
    Author

    Hi @seratch 👋

    I think the first option is the answer that I can try.

    The second option is, I don't know if I can explicitly deploy slack/install and slack/oauth_redirect. (I'll have to look for related examples.)

    Does it mean that there is another way as Flask app layer explicitly specifies Endpoint?

    Thank you for your answer and I let's try the first option.

  5. seratch commented on May 13, 2022

    @seratch
    Contributor

    Does it mean that there is another way as Flask app layer explicitly specifies Endpoint?

    No, it doesn't. You cannot use Flask's routes on Google Cloud Functions.

    I meant that you need to deploy three different functions for the three endpoints - /slack/install, /slack/oauth_redirect, and /slack/events . You may be able to use the same function for these three, but it does not mean you can use Flask routes.

    Also, you may need to adjust oauth_settings.install_path and oauth_settings.redirect_uri_path to make them functional (perhaps, you cannot use /slack/xxx path in Cloud Functions): https://github.com/slackapi/bolt-python/blob/main/slack_bolt/oauth/oauth_settings.py

  6. ggm1207 commented on May 14, 2022

    @ggm1207
    Author

    @seratch

    Oh, I understand.

    While trying the first option, I looked at the SlackRequestHandler class' detailed code and found that the features we wanted were already implemented. (Code below)

    I added some code for Debug.

    class SlackRequestHandler:
        def __init__(self, app: App):  # type: ignore
            self.app = app
    
        def handle(self, req: Request) -> Response:
            if req.method == "GET":
                print("[execute] GET")
                print(f"oauth_flow.install_path: {self.app.oauth_flow.install_path}")
                print(
                    f"oauth_flow.redirect_uri_path: {self.app.oauth_flow.redirect_uri_path}"
                )
    
                bolt_req = to_bolt_request(req)
                print(f"bolt_request.error: {bolt_req.query.get('error', [None])[0]}")
                print(f"bolt_request.state: {bolt_req.query.get('state', [None])[0]}")
                print(f"bolt_request.headers: {bolt_req.headers}")
                print(f"bolt_request.code: {bolt_req.query.get('code', [None])[0]}")
    
                if self.app.oauth_flow is not None:
                    print("[execute] app.oauth_flow")
                    oauth_flow: OAuthFlow = self.app.oauth_flow
                    if req.path == oauth_flow.install_path:
                        print("[execute] oauth_flow.install_path")
                        bolt_resp = oauth_flow.handle_installation(to_bolt_request(req))
                        return to_flask_response(bolt_resp)
                    elif req.path == oauth_flow.redirect_uri_path:
                        print("[execute] oauth_flow.redirect_uri_path")
                        bolt_resp = oauth_flow.handle_callback(to_bolt_request(req))
                        return to_flask_response(bolt_resp)
            elif req.method == "POST":
                bolt_resp: BoltResponse = self.app.dispatch(to_bolt_request(req))
                return to_flask_response(bolt_resp)
    
            return make_response("Not Found", 404)

    So I might not be aware of the issues I'm experiencing, so I'm going to explain the error in detail again.

    Steps to reproduce:

    1. Sample File Deploy to Cloud Functions
    2. Click on the 'Sharable URL' suggested by Slack to download the bot to the workspace.
      • image
    3. When you click on 'Sharable Url', the bot installs in the workspace with the steps below.

    As mentioned above, it will be installed when deployed in a local environment. However, it does not work when deployed as Cloud Functions.

    • Installation Steps
      1. Request permission to access the {YOUR WORKSPACE} from the {MY BOT}.
      2. If you allow permission, the following error page appears:
        • image
      3. And when you click here, it goes to {SERVER_ENDPOINT}/slack/install and the Slackbot is installed in the workspace. (Only when I deployed it on my MacBook)

    Information that may be useful

    This is the result of the print code that I wrote when I proceeded with the installation step as above.

    image

    You can see that elif req.path == oauth_flow.redirect_uri_path has entered, but you can see that if req.path == oauth_flow.install_path has not even entered.

    I may have written the wrong code, and there may be less authorization to the IAM or other causes. The important thing is that I have difficulty identifying the problem. What do you think is the cause?

    Thank you for solving this problem with me. It's really helpful. 👍 👍

  7. seratch commented on May 16, 2022

    @seratch
    Contributor

    @ggm1207 Thanks for your response. I just submitted a pull request that adds a new adapter for Google Cloud Functions. Please check the PR: #649

    You can use the adapter and with it, you can use only a single endpoint like https://us-central1-YOUR_PROJECT_NAME.cloudfunctions.net/YOUR_FUNCTION_NAME for everything. Please don't use the sharable URL in the Slack app admin page. As long as you set the client_id, client_secret, scopes, and signing secret to your app using env.yml file, you can just use the Cloud Functions public endpoint for all.

    In the next release, we will add the adapter. Until then, please copy the handler.py to your project.

    Let me close this issue now but please don't hesitate to write in if you have anything further to share.

  8. added this to the 1.14.0 milestone on May 16, 2022
  9. self-assigned this
    on May 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions