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

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

Closed
ggm1207 opened this issue May 12, 2022 · 7 comments
Closed

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

ggm1207 opened this issue May 12, 2022 · 7 comments
Assignees
Labels
area:adapter enhancement New feature or request question Further information is requested
Milestone

Comments

@ggm1207
Copy link

ggm1207 commented May 12, 2022

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.

@mwbrooks mwbrooks added the question Further information is requested label May 12, 2022
@mwbrooks
Copy link
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.

@seratch
Copy link
Member

seratch commented May 13, 2022

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.

@ggm1207
Copy link
Author

ggm1207 commented May 13, 2022

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.

@ggm1207
Copy link
Author

ggm1207 commented May 13, 2022

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.

@seratch
Copy link
Member

seratch commented May 13, 2022

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

@ggm1207
Copy link
Author

ggm1207 commented May 14, 2022

@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. 👍 👍

seratch added a commit to seratch/bolt-python that referenced this issue May 16, 2022
seratch added a commit to seratch/bolt-python that referenced this issue May 16, 2022
@seratch
Copy link
Member

seratch commented May 16, 2022

@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.

@seratch seratch closed this as completed May 16, 2022
@seratch seratch added this to the 1.14.0 milestone May 16, 2022
@seratch seratch self-assigned this May 16, 2022
@seratch seratch added enhancement New feature or request area:adapter labels May 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:adapter enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants