Skip to content

Unable to run OAuth flow URLs in Google Cloud Run #251

Description

@gifflarn

Hey,

so I started to develop a slack bot using slack_bolt and everything went smoothly locally.
However, when trying to host it using either Google Cloud Run (using your examples for flask + gunicorn) or Kubernetes, all endpoints, except /slack/events/ returns a 503 Service Unavailable. (I also tried to run it in dev mode without flask to see if there was any difference, there wasn't).

Running the docker locally works fine.
Hosting the code locally using ngrok works fine.

At one point I tried this:

@flask_app.route("/install", methods=["GET"])
def install(): 
	rsp = requests.get('http://localhost:3000/slack/install')
	return f"{rsp.text}"

instead of the default

@flask_app.route("/slack/install", methods=["GET"])
def slack_install():
	# handler runs App's dispatch method
	return handler.handle(request)

which surprisingly worked.

So my question is, is there something I have missed which makes Google not appreciate the Slack_bolt responses or should I just continue with the dirty solution of redirecting the call on localhost (which I prefer not to)?

The slack_bolt version

slack-bolt==1.1.5
slack-bot==0.0.7
slack-sdk==3.1.1
slackbot==1.0.0
slackclient==1.3.1

Python runtime version

python:3.6.8

OS info

Running in an Alpine Docker

Expected result:

Going to /slack/install renders the "Add to Slack" button.

Actual result:

Going to /slack/install returns Service Unavailable.

image

Activity

  1. changed the title [-](Set a clear title describing your question)[/-] [+]Unable to run OAuth flow URLs in Google Cloud Run[/+] on Mar 4, 2021
  2. seratch commented on Mar 4, 2021

    @seratch
    Contributor

    Hey @gifflarn, thanks for asking the question! At first glance, I'm still unsure what the cause of your issue is. Our example in this repository does not provide OAuth flow parts. If possible, can you share your code and configuration files like Dockerfile and requirements.txt? With those information, I may be able to check how your app works on my end too.

  3. gifflarn commented on Mar 5, 2021

    @gifflarn
    Author

    Hey, thanks for a quick reply.

    Yeah sure,

    Dockerfile:

    FROM python:3.6.8-alpine3.8
    
    RUN apk update
    RUN apk upgrade
    RUN apk add --no-cache --virtual=build-dependencies unzip
    RUN apk add --no-cache curl pkgconfig python3-dev openssl-dev libffi-dev musl-dev make gcc g++ build-base linux-headers
    RUN apk add subversion
    RUN apk add bash
    RUN apk add nano
    RUN apk add cython-dev
    RUN apk add cython
    
    RUN pip3 install --upgrade pip
    RUN pip3 install setuptools
    ENV PYTHONUNBUFFERED True
    ENV PORT 3000
    COPY . ./app
    ENV APP_HOME /app
    EXPOSE 3000
    WORKDIR "/app"
    RUN pip3 install -r requirements.txt
    ENTRYPOINT gunicorn --bind :$PORT --workers 1 --threads 2 --timeout 0 main:flask_app
    

    (this takes a little while to build due to something about grpc in an alpine build)

    requirements.txt

    slack_bolt
    Flask>=1.1
    gunicorn>=20
    requests
    google-cloud-datastore
    google-cloud-secret-manager
    

    main.py

    
    from slack_bolt import App
    from slack_bolt.adapter.flask import SlackRequestHandler
    from flask import Flask, request
    import os
    import requests
    import socket, json
    from datetime import datetime
    import traceback
    from google.cloud import secretmanager
    from google.cloud import datastore
    
    from slack_bolt.oauth.oauth_settings import OAuthSettings
    from slack_sdk.oauth.installation_store import FileInstallationStore
    from slack_sdk.oauth.state_store import FileOAuthStateStore
    
    signing_secret_name = "<secret path>"
    oauth_name = "<secret path>"
    clientid_name = "<secret path>"
    clientsecret_name = "<secret path>"
    #os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'secret.json'
    #os.environ['PORT'] = '3000'
    
    
    client = secretmanager.SecretManagerServiceClient()
    
    response = client.access_secret_version(request={"name": signing_secret_name})
    os.environ['SLACK_SIGNING_SECRET'] = response.payload.data.decode("UTF-8")
    
    response = client.access_secret_version(request={"name": oauth_name})
    os.environ['O_AUTH']=response.payload.data.decode("UTF-8")
    
    response = client.access_secret_version(request={"name": clientid_name})
    os.environ['SLACK_CLIENT_ID']=response.payload.data.decode("UTF-8")
    
    response = client.access_secret_version(request={"name": clientsecret_name})
    os.environ['SLACK_CLIENT_SECRET']=response.payload.data.decode("UTF-8")
    
    
    
    
    oauth_settings = OAuthSettings(
    	client_id=os.environ["SLACK_CLIENT_ID"],
    	client_secret=os.environ["SLACK_CLIENT_SECRET"],
    	scopes=["channels:read", "groups:read", "chat:write", "im:history", "channels:history"],
    	installation_store=FileInstallationStore(base_dir="./app/data"),
    	state_store=FileOAuthStateStore(expiration_seconds=600, base_dir="./app/data"),
    	redirect_uri=None,
    	install_path="/slack/install",
    	redirect_uri_path="/slack/oauth_redirect",
    )
    
    
    app = App(
    	signing_secret=os.environ["SLACK_SIGNING_SECRET"],
    	oauth_settings=oauth_settings
    )
    
    flask_app = Flask(__name__)
    
    handler = SlackRequestHandler(app)
    
    @flask_app.route("/slack/events", methods=["POST"])
    def slack_events():
    	# handler runs App's dispatch method
    	return handler.handle(request)
    	
    @flask_app.route("/slack/install", methods=["GET"])
    def slack_install():
    	# handler runs App's dispatch method
    	return handler.handle(request)
    	
    @flask_app.route("/install", methods=["GET"])
    def install():
    	rsp = requests.get('http://localhost:3000/slack/install')
    	return f"{rsp.text}"
    	# handler runs App's dispatch method
    	#return handler.handle(request)
    	
    @flask_app.route("/slack/oauth", methods=["GET"])
    def slack_oauth():
    	# handler runs App's dispatch method
    	return handler.handle(request)
    	
    @flask_app.route("/oauth_redirect", methods=["GET"])
    def oauth_redirect():
    	rsp = requests.get(f'http://localhost:3000/slack/oauth_redirect?code={request.args.get("code")}&state={request.args.get("state")}')
    	return f"{rsp.text}"
    	
    @flask_app.route("/oauth", methods=["GET"])
    def oauth():
    	rsp = requests.get('http://localhost:3000/slack/oauth')
    	return f"{rsp.text}"
    	# handler runs App's dispatch method
    	#return handler.handle(request)
    	
    
    
    if __name__ == "__main__":
    	#app.run(int(os.environ['PORT']))
    	flask_app.run(host='0.0.0.0', port=int(os.environ['PORT']),use_debugger=True, debug=True,use_reloader=True)  # POST http://localhost:3000/slack/events	
    

    I have omitted most of the code from main.py, but the problem of Service Unavailable still remains

  4. seratch commented on Mar 6, 2021

    @seratch
    Contributor

    @gifflarn Thanks again for reporting this issue. I've figured the cause out and made a pull request resolving this issue #253. I will release a new patch version shortly.

  5. modified the milestones: , 1.4.3 on Mar 6, 2021
  6. added
    bugSomething isn't working
    and removed
    questionFurther information is requested
    on Mar 6, 2021
  7. added a commit that references this issue on Mar 6, 2021
    f0080b2
  8. seratch commented on Mar 6, 2021

    @seratch
    Contributor

    version 1.4.3, which includes the fix, is now available.

  9. seratch commented on Mar 6, 2021

    @seratch
    Contributor

    Just for your information, I've created an example app: https://gist.github.com/seratch/d81a445ef4467b16f047156bf859cda8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions