S
Solara5w ago
Cyrus

Force solara to use https in redirect uris

Using Chrome's Dev Tools, I observed the following network requests after clicking the login button in my application: Initial Login Request: https://example.com/_solara/auth/login?redirect_uri=https%3A//example.com/return_to_path This request looks correct as it uses https. Authorization Request to Okta: https://my-okta-domain.oktapreview.com/oauth2/v1/authorize?response_type=code&client_id=MY_CLIENT_ID&redirect_uri=http%3A%2F%2Fexample.com%2F_solara%2Fauth%2Fauthorize&scope=openid+profile+email&state=MY_STATE&nonce=MY_NONCE The issue here is that the redirect_uri parameter is using http instead of https. Is there a way to force a solara application to use https scheme/protocol for redirect URIs (for auth). I tried setting SOLARA_SESSION_HTTPS_ONLY=True and SOLARA_BASE_URL=https://example.com/ but I still see http. Maybe I could mount my solara app to a Starlette app with an added HTTPSRedirectMiddleware middleware?
26 Replies
MaartenBreddels
Strange, because everywhere at https://github.com/widgetti/solara/blob/master/packages/solara-enterprise/solara_enterprise/auth/starlette.py it uses SOLARA_BASE_URL (via settings.main.base_url), so if that env var is set, I don't understand why it still takes http. A few ideas: make sure you remove cookies and cache (i've once had an old session cookie act weird). print out settings.main.base_url in the solara source code as a sanity check
GitHub
solara/packages/solara-enterprise/solara_enterprise/auth/starlette....
A Pure Python, React-style Framework for Scaling Your Jupyter and Web Apps - widgetti/solara
Cyrus
Cyrus4w ago
I tried incognito mode with fresh cookies and also verified that settings.main.base_url prints the correct base url. Deploying locally I can modify solara source code to also print request.scope in the /login endpoint and it prints something like this
{
'headers': [(b'host', b'localhost:8765'),
(b'accept',
b'text/html,application/xhtml+xml,application/xml;q=0.9,image/'
b'avif,image/webp,image/png,image/svg+xml,*/*;q=0.8'),
(b'referer', b'http://localhost:8765/'),
...
(b'upgrade-insecure-requests', b'1'),
(b'sec-fetch-dest', b'document'),
(b'sec-fetch-mode', b'navigate'),
(b'sec-fetch-site', b'same-origin'),
(b'sec-fetch-user', b'?1'),
(b'priority', b'u=0, i')],
'http_version': '1.1',
'method': 'GET',
'path': '/_solara/auth/login',
'path_params': {},
'query_string': b'redirect_uri=<correct, but HTTP encoded URL here>', # <-----------
'raw_path': b'/_solara/auth/login',
'root_path': '',
'router': '<starlette.routing.Router object at 0x1278eab30>',
'scheme': 'http',
'server': ('127.0.0.1', 8765),
'session': {'client_id': '...',
'redirect_uri': '<correct URL here>'}, # <--------------------
{
'headers': [(b'host', b'localhost:8765'),
(b'accept',
b'text/html,application/xhtml+xml,application/xml;q=0.9,image/'
b'avif,image/webp,image/png,image/svg+xml,*/*;q=0.8'),
(b'referer', b'http://localhost:8765/'),
...
(b'upgrade-insecure-requests', b'1'),
(b'sec-fetch-dest', b'document'),
(b'sec-fetch-mode', b'navigate'),
(b'sec-fetch-site', b'same-origin'),
(b'sec-fetch-user', b'?1'),
(b'priority', b'u=0, i')],
'http_version': '1.1',
'method': 'GET',
'path': '/_solara/auth/login',
'path_params': {},
'query_string': b'redirect_uri=<correct, but HTTP encoded URL here>', # <-----------
'raw_path': b'/_solara/auth/login',
'root_path': '',
'router': '<starlette.routing.Router object at 0x1278eab30>',
'scheme': 'http',
'server': ('127.0.0.1', 8765),
'session': {'client_id': '...',
'redirect_uri': '<correct URL here>'}, # <--------------------
Given this scope (with 'scheme': 'http') I think then here my url gets defaulted to have 'http' sheme? Shouldn't the login endpoint here be using session.main.base_url instead of request.base_url? When I set SOLARA_BASE_URL then settings.main.base_url correctly updates, while request.base_url doesn't
MaartenBreddels
Yes, you may be right! I am pretty sure that is a bug, and I'll open an issue (if you don't) However, if the proxy server is configured correctly, this should not be a problem, I think X-Forwarded-Proto is not set in your reverse proxy
MaartenBreddels
Using OAuth in your Solara app
Open Authorization can be readily integrated into your Solara applications via the Solara-Enterprise package.
Deploying and hosting your own Solara app
Solara is compatible with many different hosting solutions, such as Flask, Starlette, FastAPI, and Voila.
Cyrus
Cyrus4w ago
In my case the app should not be going through a proxy, because it's hosted within a VPC but maybe the load balancer can affect headers and what not. I am happy to open an issue about the bug! @MaartenBreddels I created an issue
MaartenBreddels
What load balancer are you using (that should behave as a reverse proxy)
Cyrus
Cyrus4w ago
It is a AWS Application Load Balancer (ALB) configured to listen for https requests (the security group only accepts https). I am not too knowledgeable about infra but I think the X-Forwarded-Proto header should automatically be forwarded in our setup to indicate the protocol used by the client.
MaartenBreddels
@mariobuikhuizen maybe you have an idea how this can happen (i can update you tomorrow) @Cyrus could you print out the headers you get from any request? I'd like to see what headers ALB sends similar to the print out you did above (if it contains sensitive data, you can send it to maartenbreddels@widgetti.io)
Cyrus
Cyrus4w ago
I was able to grab the OAuth2 authorization request/response headers from chrome dev tool. The request chain starts when I click on my app's Login button (which href's to auth.get_login_url). The first request is (using placeholders to hide some stuff):
**Request URL:**
https://MY_HOST/_solara/auth/login?redirect_uri=https://MY_HOST/RETURN_TO_PATH

**Request Method:** GET

**Status Code:** 302 Found

**Referrer Policy:** strict-origin-when-cross-origin

**Request Headers:**
{
":authority": "MY_HOST",
":method": "GET",
":path": "/_solara/auth/login?redirect_uri=https://MY_HOST/RETURN_TO_PATH",
":scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "en-US,en;q=0.9",
"cookie": "solara-session-id=<session_id>; solara-session=<session_data>",
"priority": "u=0, i",
"referer": "https://MY_HOST/",
"sec-ch-ua": "\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1"
}

** Response Headers **
{
"content-length": "0",
"location": "https://<API_BASE_URL>.oktapreview.com/oauth2/v1/authorize?response_type=code&client_id=<client_id>&redirect_uri=http://MY_HOST/_solara/auth/authorize&scope=openid+profile+email&state=<state>&nonce=<nonce>",
"server": "uvicorn",
"set-cookie": "solara-session=<session_data>; path=/; Max-Age=1209600; httponly; samesite=lax; secure"
}
**Request URL:**
https://MY_HOST/_solara/auth/login?redirect_uri=https://MY_HOST/RETURN_TO_PATH

**Request Method:** GET

**Status Code:** 302 Found

**Referrer Policy:** strict-origin-when-cross-origin

**Request Headers:**
{
":authority": "MY_HOST",
":method": "GET",
":path": "/_solara/auth/login?redirect_uri=https://MY_HOST/RETURN_TO_PATH",
":scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "en-US,en;q=0.9",
"cookie": "solara-session-id=<session_id>; solara-session=<session_data>",
"priority": "u=0, i",
"referer": "https://MY_HOST/",
"sec-ch-ua": "\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"macOS\"",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1"
}

** Response Headers **
{
"content-length": "0",
"location": "https://<API_BASE_URL>.oktapreview.com/oauth2/v1/authorize?response_type=code&client_id=<client_id>&redirect_uri=http://MY_HOST/_solara/auth/authorize&scope=openid+profile+email&state=<state>&nonce=<nonce>",
"server": "uvicorn",
"set-cookie": "solara-session=<session_data>; path=/; Max-Age=1209600; httponly; samesite=lax; secure"
}
you can see the ":scheme": "https", in the request header but then http in the response location redirect_uri, which doesn't align with the redirect uris allowed in the app, thus breaking the auth flow
MaartenBreddels
I mean the request headers solara sees, because the load balancer will add extra headers that you don’t see in the dev console, you need to modify the Python code with a print statement for that like you did above
Cyrus
Cyrus4w ago
Ah, to print request before I just modified solara source code locally. For the remote version connected to the ALB I can try embedding my solara app in Starlette and then overwriting the _solara/auth/login route to point to a custom endpoint in the app with logging statements. Will let you know how it goes. OK, so with my own custom login function I was able to fix the problem by implementing the bug fix suggested in the issue. I was also able to add print statements and verify that indeed request.base_url is using http compared to settings.main.base_url. I also printed headers confirming that the ALB is forwarding things correctly (with 'x-forwarded-proto': 'https'):
Headers({'x-forwarded-for': 'XX.XXX.XXX.XX', 'x-forwarded-proto': 'https', 'x-forwarded-port': '443', 'host': 'MY_HOST', 'x-amzn-trace-id': 'Root=1-66c58ba1-54acf49f04355573178fa972', 'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'referer': 'https://MY_HOST/?iss=https%3A%2F%2FAPI_BASE_URL.oktapreview.com', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=0, i', 'cookie': 'solara-session-id=111e11ec-2e91-4709-8abc-ea8ff2c1111b'})
Headers({'x-forwarded-for': 'XX.XXX.XXX.XX', 'x-forwarded-proto': 'https', 'x-forwarded-port': '443', 'host': 'MY_HOST', 'x-amzn-trace-id': 'Root=1-66c58ba1-54acf49f04355573178fa972', 'sec-ch-ua': '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"macOS"', 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'navigate', 'sec-fetch-user': '?1', 'sec-fetch-dest': 'document', 'referer': 'https://MY_HOST/?iss=https%3A%2F%2FAPI_BASE_URL.oktapreview.com', 'accept-encoding': 'gzip, deflate, br, zstd', 'accept-language': 'en-US,en;q=0.9', 'priority': 'u=0, i', 'cookie': 'solara-session-id=111e11ec-2e91-4709-8abc-ea8ff2c1111b'})
So it looks like it was the bug that we discussed above @MaartenBreddels Would you like me to create a (tiny) PR for this?
MaartenBreddels
Hi Cyrus, no thanks, not yet, I need to dig into starlette a bit to see why this does not work, because everything is setup correctly I think this is something that uvicorn should be told to do..
MaartenBreddels
looking at https://www.uvicorn.org/ We can define UVICORN_PROXY_HEADERS=1
Uvicorn
The lightning-fast ASGI server.
MaartenBreddels
could you try that?
Want results from more Discord servers?
Add your server