I'm building a dockerized website with docker-compose. A React frontend and Python Connexion/Flask b...
h
I'm building a dockerized website with docker-compose. A React frontend and Python Connexion/Flask backend in separate, networked containers. Their container names 'frontend' and 'backend' resolve to their respective IP-addresses within this Docker network. I'm running into a problem while integrating SuperTokens in my backend. I'm setting the
app_info
values to this:
Copy code
app_info=InputAppInfo(
    app_name="GG-Host",
    api_domain="http://backend:8080",
    website_domain="http://frontend:3000",
    api_base_path="/auth",
    website_base_path="/auth"
)
Running with this configuration causes the following error:
Copy code
supertokens_python.exceptions.GeneralError: Since your API and website domain are different, for sessions to work, please use https on your apiDomain and don't set cookieSecure to false.
I don't want to use https, since this is all local networking within the Docker network. Changing
api_domain
to use https instead resolves the error, but the sign up form still errors.
r
hey!
h
Hello 👋
r
how do you access the frontend app during testing from the browser
using localhost?
h
Yea, I bind my host port 3000 to the container's port 3000 and then use localhost:3000 in my browser
r
so the website_domain should be
http://localhost:3000
and how does the frontend app on the browser query the backend? Using which URL?
h
http://backend:8080
r
hmm. that works? The browser sending a request to backend:8080
h
Ooooh, my bad! I see where it's going wrong now. I'm completely new to frontend stuff. Thank you!
r
happy to help 🙂
h
But then, using localhost for all of them still makes the form error
Although it fails right away whereas everything else I try takes a while to query (probably waiting for timeout) and then fails
r
"still makes the form error" -> what does this mean?
h
The SuperTokens signup web form errors with "Something went wrong. Please try again."
So, if I understand right, the backend still needs to refer to the frontend as
http://frontend:3000
. This circles back to the initial problem with https
r
the backend deosn't need to the frontend as that. It should use localhost
about the something went wrong error, do you have an error handler in your backend? What does that log?
h
These are my backend logs when I connect to the site and attempt to sign in
Copy code
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:08:54] "OPTIONS /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:09:00] "OPTIONS /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:09:00] "OPTIONS /auth/signin HTTP/1.1" 404 -
r
what do you see in the network tab on the browser?
and the response from that API?
which gives a 500
h
And here's logs for attempted sign up
Copy code
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:10:27] "OPTIONS /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:10:30] "OPTIONS /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:10:31] "OPTIONS /auth/signup/email/exists?email=elneff%40pm.me HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:10:33] "OPTIONS /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 14:10:33] "OPTIONS /auth/signup HTTP/1.1" 404 -
I will check the network tab now
r
oh right. You may not have setup CORS
since OPTIONS is giving a 404
h
r
yea.. you need to setup CORS
please see our quick setup guide on the backend
it shows you how to set CORS
h
I have this function
Copy code
def add_supertokens_apis(app):
    """"Add SuperTokens API to Flask app"""
    Middleware(app)

    # TODO: Add APIs

    CORS(
        app=app,
        origins=[
            "https://localhost:3000"
        ],
        supports_credentials=True,
        allow_headers=["Content-Type"] + get_all_cors_headers(),
    )
Which I pass my Flask app from Connexion like so
Copy code
if __name__ == '__main__':
    logging.basicConfig(
        # filename='gamehost.log'
        encoding='utf-8',
        level=logging.INFO
    )

    # Start Connexion
    app = connexion.App(__name__, options={'swagger_ui': True})
    supertokens.add_supertokens_apis(app.app)
    app.add_api('openapi.yml')
    app.run(server='flask', host='0.0.0.0', port=8080)
see the above example app
this might help
sorry, im not too familiar with flask myself - so why the CORS middleware is not working, you would need to find out non your own
h
I see, well it helps a lot to have it narrowed down to this. Thanks a lot for your help 🙂
r
happy to help! if after fixing CORS you have other issues, please feel free to ask
h
One other thing. The SuperTokens container repeatedly logs this line:
Copy code
14 Sep 2022 14:24:56:951 +0000 | INFO | pid: 56ca2e82-56c5-4a29-8e3d-b1d9a92c0fb2 | [http-nio-0.0.0.0-3567-exec-1] thread | io.supertokens.webserver.WebserverAPI.service(WebserverAPI.java:184) | API ended: /hello. Method: GET
It seems to just be an info log, but it's printed way too often for my liking, filling up my terminal. Is there any way to disable it?
r
yea
h
Ah, thank you. I haven't ventured outside of the quick setup section yet, mb 😅
The problem seems to stem from the fact that the
Access-Control-Allow-Origin
header is not set in the request. Am I supposed to set this somewhere? Printing the result of
get_all_cors_headers()
shows the following list:
['anti-csrf', 'rid', 'fdi-version']
r
the CORS middleware should take care of that
if that header is not in the response, it means CORS middleware isn't setup correctly
did you see how we add the middleware in the example app?
h
Yea, I'm doing the same
r
hmm.. then it should work
@KShivendu can perhaps help here
h
I am using Connexion, but their docs say you can just use
app.app
to get the Flask app and enable CORS that way, like in the example you sent. I will post my code here, in case it helps KShivendu spot something
Copy code
python
if __name__ == '__main__':
    # Start Connexion
    app = connexion.App(__name__, options={'swagger_ui': True})
    app.add_api('openapi.yml')

    Middleware(app.app)
    CORS(
        app=app.app,
        origins=[
            "https://localhost:3000"
        ],
        supports_credentials=True,
        allow_headers=["Content-Type"] + get_all_cors_headers(),
    )

    print('CORS headers:', str(get_all_cors_headers()))

    app.run(host='0.0.0.0', port=8080)
k
hi @Hansi, I tried sending requests to endpoints not registered in my
openapi.yml
file. They also return CORS error (while the registered ones succeed). So I think the issue is related to connexion. give me some time, I'll try to figure something to fix it.
h
That's great, thank you very much!
I get the same CORS error when using Flask directly without Connexion
And here's what I see when browsing to
http://localhost:8080/auth/session/refresh
This makes the backend log:
Copy code
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 15:36:45] "GET /auth/session/refresh HTTP/1.1" 404 -
INFO:werkzeug:172.26.0.1 - - [14/Sep/2022 15:36:46] "GET /favicon.ico HTTP/1.1" 404 -
I should perhaps also mention that, in order to even get this running, I had to manually to downgrade
Flask==2.2.2
to
Flask==2.1.3
in order to use
Werkzeug==2.0.3
, which
supertokens-python==0.10.4
(latest version) requires. Though I doubt this is related to the issue at hand.
k
/auth/session/refresh
expects POST request
h
Ah, right
k
fetch("http://localhost:8000/auth/session/refresh", {method: "POST"}).then(res => res.text()).then(console.log)
try this
h
k
oops. you're using 8080.
h
k
weird. I don't face this.
my config:
Copy code
python
from flask import Flask, abort
from flask_cors import CORS
from supertokens_python import get_all_cors_headers
from supertokens_python.framework.flask import Middleware

from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session

init(
    supertokens_config=SupertokensConfig(
        connection_uri="XXX",
        api_key="YYY"
    ),
    app_info=InputAppInfo(
        app_name="SuperTokens Demo",
        api_domain="http://api.supertokens.io",
        website_domain="http://supertokens.io",
        api_base_path="/auth",
    ),
    framework="flask",
    recipe_list=[
        session.init(anti_csrf="VIA_TOKEN"),
    ],
)

app = Flask(__name__)
Middleware(app)
CORS(
    app=app,
    supports_credentials=True,
    origins=["http://localhost:3000"],
    allow_headers=["Content-Type"] + get_all_cors_headers(),
)

@app.get("/hello")
def hello_wolrd():
    return "Hello World"

# This is required since if this is not there, then OPTIONS requests for
# the APIs exposed by the supertokens' Middleware will return a 404
@app.route("/", defaults={"u_path": ""})  # type: ignore
@app.route("/<path:u_path>")  # type: ignore
def catch_all(u_path: str):  # pylint: disable=unused-argument
    abort(404)

app.run(host="0.0.0.0", port=8000)
Just to cofirm, your core is up, right?
h
I am using
try.supertokens.com
My core is running tho, I can try switching to it
k
then it should be fine.
please try this config once
h
I put my domains in your config like so:
Copy code
python
from flask import Flask, abort
from flask_cors import CORS
from supertokens_python import get_all_cors_headers
from supertokens_python.framework.flask import Middleware

from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session

init(
    supertokens_config=SupertokensConfig(
        connection_uri="http://localhost:3567",
        api_key="someKey"
    ),
    app_info=InputAppInfo(
        app_name="SuperTokens Demo",
        api_domain="http://localhost:8080",
        website_domain="http://localhost:3000",
        api_base_path="/auth",
    ),
    framework="flask",
    recipe_list=[
        session.init(anti_csrf="VIA_TOKEN"),
    ],
)

app = Flask(__name__)
Middleware(app)
CORS(
    app=app,
    supports_credentials=True,
    origins=["http://localhost:3000"],
    allow_headers=["Content-Type"] + get_all_cors_headers(),
)

@app.get("/hello")
def hello_world():
    return "Hello World"

# This is required since if this is not there, then OPTIONS requests for
# the APIs exposed by the supertokens' Middleware will return a 404
@app.route("/", defaults={"u_path": ""})  # type: ignore
@app.route("/<path:u_path>")  # type: ignore
def catch_all(u_path: str):  # pylint: disable=unused-argument
    abort(404)

app.run(host="0.0.0.0", port=8000)
Still getting the CORS error
Actually, this time it says "CORS Failed"
What versions are you using for Flask, Werkzeug, and supertokens-python?
Since Connexion and supertokens-python conflict over the Werkzeug version without manual intervention
k
Copy code
Werkzeug==2.0.1
Flask==2.1.1
my
supertokens-python
is in edit mode and contains the latest code from the repo (just latest version of st-python should be sufficient for you)
h
Which version of Flask-Cors?
k
Flask-Cors==3.0.10
h
supertokens-python==0.10.4
should be fine, right?
k
yes
h
Hmm, exact same versions still result in
CORS Failed
k
weird
btw, st-python is now working with connexion too.
Copy code
python
import connexion
from flask_cors import CORS
from flask import abort
from supertokens_python import get_all_cors_headers
from supertokens_python.framework.flask import Middleware

from supertokens_python import init, SupertokensConfig, InputAppInfo
from supertokens_python.recipe import session

def post_greeting(name: str) -> str:
    return f"Hello {name}"

init(
    supertokens_config=SupertokensConfig(
        connection_uri="XXX",
        api_key="YYY",
    ),
    app_info=InputAppInfo(
        app_name="SuperTokens Demo",
        api_domain="http://api.supertokens.io",
        website_domain="http://supertokens.io",
        api_base_path="/auth",
    ),
    framework="flask",
    recipe_list=[
        session.init(anti_csrf="VIA_TOKEN"),
    ],
)

app = connexion.FlaskApp(__name__, specification_dir='openapi/')
app.add_api('my_api.yaml')

flask_app = app.app
Middleware(flask_app)
CORS(
    app=flask_app,
    origins=[
        "http://localhost:3000"
    ],
    supports_credentials=True,
    allow_headers=["Content-Type"] + get_all_cors_headers(),
)



# This is required since if this is not there, then OPTIONS requests for
# the APIs exposed by the supertokens' Middleware will return a 404
@flask_app.route("/", defaults={"u_path": ""})  # type: ignore
@flask_app.route("/<path:u_path>")  # type: ignore
def catch_all(u_path: str):  # pylint: disable=unused-argument
    abort(404)


app.run(host="0.0.0.0", port=8000)
h
What does your specification look like?
k
Copy code
yaml
swagger: "2.0"

info:
  title: "{{title}}"
  version: "1.0"

basePath: /v1.0

paths:
  /greeting/{name}:
    post:
      summary: Generate greeting
      description: Generates a greeting message.
      operationId: demo.post_greeting
      produces:
        - text/plain;
      responses:
        200:
          description: greeting response
          schema:
            type: string
          examples:
            "text/plain": "Hello John"
      parameters:
        - name: name
          in: path
          description: Name of the person to greet.
          required: true
          type: string
wait. your config contains https://localhost:3000/
ohh sorry, you have already tried mine so that can't be the issue
h
Shouldn't I be able to browse here and see the greeting with your config?
http://localhost:8080/greeting/test
Neither that or
http://localhost:3000/greeting/test
works
k
h
Oh, right, 8000
This link does not work either
k
curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' 'http://localhost:8000/v1.0/greeting/22'
again a post request
lol. we have created too much complexity here xD
works?
h
I'm on Windows rn, so I don't have curl available. Could you make a fetch version?
Everything works
I changed your config to port 8080 and now I'm not getting any CORS errors
k
so kind of caching in the browser 🤔
that's the only sensible explanation for this behavior.
h
Well, I hadn't tested your config properly yet
k
cool then. let me know if you need help with anything else. 😃
which one.
h
Yea, thanks for the help, man! Now I just need to figure out why mine didn't work 🤔
The connexion one, I didn't realize the port was the problem, since I only expose 8080 in docker-compose. When I did, your version worked
Mine works too now, so it must have been the versions?
k
iirc you mentioned that our versions were already same 🤔
.
h
No, I changed my versions to match yours
k
oo got it.
h
But I still got
CORS Failed
at that point 🤔
Also, what is this option for? It's not in the guide.
session.init(anti_csrf="VIA_TOKEN")
k
try checking the docs. if it isn't clear, it will be an opportunity for us to improve it 😄
any feedback for us in general?
h
Well, the support here is extremely helpful! Thanks to both of you for all your time and patience. This is not exactly my area of expertise 😅 The only thing that has bothered me was the version conflict, but that's a minor thing
Okay, the CORS error is gone, but this also does not seem quite right..
@KShivendu this is with your Connexion version
Nvm, I got it working! :)
k
cool.
h
I think there's a mistake in the guide
Step 2) Backend > Core > Self hosted > With Docker
. I'm told to use
connection_uri='http://localhost:3567'
but that does not resolve. I know that my core is running and everything works when I do this instead:
connection_uri='http://supertokens:3567'
r
well, that's not a mistake. It's just what the default value of the connection_uri is if you run the core without using docker networking
h
But your guide supplies a docker-compose where it is networked:
Copy code
supertokens:
    image: registry.supertokens.io/supertokens/supertokens-postgresql
    depends_on:
      - db
    ports:
      - 3567:3567
    environment:
      POSTGRESQL_CONNECTION_URI: "postgresql://supertokens_user:somePassword@db:5432/supertokens"
      LOG_LEVEL: "WARN"
    networks:
      - app_network
    restart: unless-stopped
    healthcheck:
      test: >
        bash -c 'exec 3<>/dev/tcp/127.0.0.1/3567 && echo -e "GET /hello HTTP/1.1\r\nhost: 127.0.0.1:3567\r\nConnection: close\r\n\r\n" >&3 && cat <&3 | grep "Hello"'
      interval: 10s
      timeout: 5s
      retries: 5

networks:
  app_network:
    driver: bridge
r
ahh right. Fair enough
Thanks for this - will keep a note of it and make the changes