Connect-backend feature has limitation(no way to authenticate) when opening pgweb in iframe
Hi. We want to use pgweb's connect-backend feature. In our web-app we embed pgweb iframe to access the database for a user. We have configured pgweb with these params as mentioned in docs here
--connect-backend=<backend-url>
--connect-token=<some-randomly-token>
--connect-headers=<access_token(token-to-authenticate-user)>
The way we want to use the connect backend feature is using the pgweb url like
https://<pgweb-url>/connect/<resource-id> . The resource id is something that that will uniquely identify a database in our system.
The issue here is in an iframe there is no direct way to pass headers directly, so we have no way to authenticate our user(i.e. passing access_token as header with /connect/
For passing iframe in header, on googling i found some answers but they mainly ask to make the request using some JS library like axios, XMLhttpRequest and then use the info returned, in iframe.
From what i understood, when we use pgweb connect backend in browser(without any headers for auth), it makes a request to backend, get credentials and from https://<pgweb-url>/connect/<resource-id> redirects us to base pgweb url https://<pgweb-url>/ setting the session id in session storage of browser and let's us access the database.
Since the part of setting the session id is done by pgweb's web interface and for iframe usecase we are making call to https://<pgweb-url>/connect/<resource-id> via axios, the session id can't be set(in pgweb domains session storage) and hence the pgweb web-app opened in the iframe remains unauthenticated.
Secondly we saw that we can pass header x-session-id in GET request for /connect/<>, then this session id can be used to connect to the database, but this session id needs to be set in pgweb-domains session storage and browsers don't allow cross domain access to browser storage i.e. from our https://<my-org-web/ i can't set session id of https://<pgweb-url>/.
Any help or suggestion is appreciated. If anything is unclear in my explanation, please feel free to tag and comment. Thanks in advance.
I'm not a Pgweb developer, just a fellow user who has been working with Pgweb's connect backend recently. Since you asked for any suggestion, may I offer my 2 cents?
Browser clients cannot pass custom headers in non-Javascript GET requests (which is what an iframe would make). Cookies were invented to send extra data along with every request, but in order to make that work Pgweb would have to be on the same origin as the site that sets the cookies. That is possible using a proxy and Pgweb's --prefix option, but this is way too complex in my opinion.
As an alternative, let's focus on your core requirement. Ultimately you're just trying to pass the value of the access_token along with the database identifier to your connect backend somehow. You mentioned that resource-id happens to be a database identifier, but you could use almost whatever you want as the resource-id and Pgweb will just blindly forward it on to your backend as the string value of "resource". If it's not too long or secret, it might be simplest to append the value of access_token to the database name in the path with a separator character, like https://<pgweb-url>/connect/<database-name>,<access-token>. Then your backend could split them apart, authenticate the user, and move on. Or, if you have a shared session store (like redis or memcached or DynamoDB or something), you could store the access token and database identifier under a temporary random key, then pass that key in the connect URL. Then your backend server could look it up, verify the user, and then delete the temporary key to avoid a session reply attack.
You already have everything you need to make this work without any change to Pgweb.
@Barnabas Hi, thanks for the suggestion. As of know we have implemented somewhat similiar to what you explained.
- Before making a call to pgweb connect, we call the backend with passing auth token in header and get a unique connect token for the given user that expires in 15sec.
- then we open pgweb in iframe by calling
/connect/<connect_token_from_prev_call>. We didn't wanted to pass user authtoken in request directly as that can be logged at ingress or in ES. - then backend get's the connect_token and retrieves the db info back to understad which db to connect and eastablish a connection to that db.