S
Solara4mo 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
MaartenBreddels4mo ago
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
CyrusOP4mo 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
MaartenBreddels4mo ago
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
MaartenBreddels4mo ago
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
CyrusOP4mo 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
MaartenBreddels4mo ago
What load balancer are you using (that should behave as a reverse proxy)
Cyrus
CyrusOP4mo 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
MaartenBreddels4mo ago
@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 [email protected])
Cyrus
CyrusOP4mo 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
MaartenBreddels4mo ago
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
CyrusOP4mo 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
MaartenBreddels4mo ago
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
MaartenBreddels4mo ago
looking at https://www.uvicorn.org/ We can define UVICORN_PROXY_HEADERS=1
Uvicorn
The lightning-fast ASGI server.
MaartenBreddels
MaartenBreddels4mo ago
could you try that?
MaartenBreddels
MaartenBreddels4mo ago
Deploying and hosting your own Solara app
Solara is compatible with many different hosting solutions, such as Flask, Starlette, FastAPI, and Voila.
MaartenBreddels
MaartenBreddels4mo ago
looking at https://github.com/encode/starlette/issues/692 it seems like only ALLOW_FORWARDED_IPS is needed
Monty Python
Monty Python4mo ago
shangxiao
<:issue_closed_completed:979047130847117343> [encode/starlette] Consider using X-Forwarded-Proto with HTTPSRedirectMiddleware
It would be handy if HTTPSRedirectMiddleware had an option to use the X-Forwarded-Proto header for redirecting on platforms like Heroku.
Created
Cyrus
CyrusOP4mo ago
setting ALLOW_FORWARDED_IPS=1 didn't work. The redirect uri based on request.base_url reverted back to http
Cyrus
CyrusOP4mo ago
Maybe it should be FORWARDED_ALLOW_IPS='*' ?
No description
Cyrus
CyrusOP4mo ago
Cyrus
CyrusOP4mo ago
It worked!! I definitely think a note about FORWARDED_ALLOW_IPS='*' should be documented in the solara auth page (or even set by default to '*' within solara server?)
Monty Python
Monty Python4mo ago
maartenbreddels
<:pull_open:882464248721182842> [widgetti/solara] feat: give warnings if we detect a possible proxy/uvicorn misconfiguration
Based on x-forwarded-host and x-forwarded-proto headers, and asgi scope data we can detect a possible misconfiguration of the proxy/uvicorn We noticed many people struggle to configure this right. The documentation is also made consistent with better insights into the possible misconfigurations. Related to #740
Created
MaartenBreddels
MaartenBreddels4mo ago
Could you maybe check this branch and see if this gives you good error messages/hints?
Cyrus
CyrusOP4mo ago
Sure, will test now and reply in the issue. Looks like it worked (see issue)
Want results from more Discord servers?
Add your server