Delete local data from all subdomains - ruby-on-rails

Is it possible to remove all local data including sessions, cookies, localStorage and sessionStorage of all *.domain.com from a Rails app at rails.domain.com?
I want like to clear users' session for all of *.domain.com when they logout from one of my app so they need to login again just like Google does it.

You can do so by sending the following HTTP header in the response after looging out: Clear-Site-Data: *
please check https://w3c.github.io/webappsec/specs/clear-site-data/published/2015-07-FPWD.html#clear_site_data for more details.

Here's the right syntax (tested against Chrome v.67.0.3396.79)
Clear-Site-Data: "cache", "cookies", "storage"
So in Rails that would be something like:
response.headers['Clear-Site-Data'] = '"cache", "cookies", "storage"'
Couldn't get the * wildcard, nor the executionContexts, nor the includeSubdomains bit to work though but it seems to be doing the job.
Figured out thanks to this :
https://github.com/yzyz/chromium/commit/cc19a8388814caf28b15e348a2e1941b6b66e370

Related

FastAPI RedirectResponse gets {"message": "Forbidden"} when redirecting to a different route

Please bare with me for a question for which it's nearly impossible to create a reproducible example.
I have an API setup with FastAPI using Docker, Serverless and deployed on AWS API Gateway. All routes discussed are protected with an api-key that is passed into the header (x-api-key).
I'm trying to accomplish a simple redirect from one route to another using fastapi.responses.RedirectResponse. The redirect works perfectly fine locally (though, this is without api-key), and both routes work perfectly fine when deployed on AWS and connected to directly, but something is blocking the redirect from route one (abc/item) to route two (xyz/item) when I deploy to AWS. I'm not sure what could be the issue, because the logs in CloudWatch aren't giving me much to work with.
To illustrate my issue let's say we have route abc/item that looks like this:
#router.get("/abc/item")
async def get_item(item_id: int, request: Request, db: Session = Depends(get_db)):
if False:
redirect_url = f"/xyz/item?item_id={item_id}"
logging.info(f"Redirecting to {redirect_url}")
return RedirectResponse(redirect_url, headers=request.headers)
else:
execution = db.execute(text(items_query))
return convert_to_json(execution)
So, we check if some value is True/False and if it's False we redirect from abc/item to xyz/item using RedirectResponse(). We pass the redirect_url, which is just the xyz/item route including query parameters and we pass request.headers (as suggested here and here), because I figured we need to pass along the x-api-key to the new route. In the second route we again try a query in a different table (other_items) and return some value.
I have also tried passing status_code=status.HTTP_303_SEE_OTHER and status_code=status.HTTP_307_TEMPORARY_REDIRECT to RedirectResponse() as suggested by some tangentially related questions I found on StackOverflow and the FastAPI discussions, but that didn't help either.
#router.get("/xyz/item")
async def get_item(item_id: int, db: Session = Depends(get_db)):
execution = db.execute(text(other_items_query))
return convert_to_json(execution)
Like I said, when deployed I can successfully connect directly to both abc/item and get a return value if True and I can also connect to xyz/item directly and get a correct value from that, but when I pass a value to abc/item that is False (and thus it should redirect) I get {"message": "Forbidden"}.
In case it can be of any help, I try debugging this using a "curl" tool, and the headers I get returned give the following info:
Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Date: Wed, 27 Jul 2022 08:43:06 GMT
x-amzn-RequestId: XXXXXXXXXXXXXXXXXXXX
x-amzn-ErrorType: ForbiddenException
x-amz-apigw-id: XXXXXXXXXXXXXXXX
X-Cache: Error from cloudfront
Via: 1.1 XXXXXXXXXXXXXXXXXXXXXXXXX.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: XXXXX
X-Amz-Cf-Id: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
So, this is hinting at a CloudFront error. Unfortunately I don't see anything slightly hinting at this API when I look into my CloudFront dashboard on AWS, there literally is nothing there (I do have permissions to view the contents though...)
The API logs in CloudWatch look like this:
2022-07-27T03:43:06.495-05:00 Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.495-05:00 [INFO] 2022-07-27T08:43:06.495Z Redirecting to /xyz/item?item_id=1234...
2022-07-27T03:43:06.496-05:00 2022-07-27 08:43:06,496 INFO sqlalchemy.engine.Engine ROLLBACK
2022-07-27T03:43:06.496-05:00 [INFO] 2022-07-27T08:43:06.496Z ROLLBACK
2022-07-27T03:43:06.499-05:00 END RequestId: 6f449762-6a60189e4314
2022-07-27T03:43:06.499-05:00 REPORT RequestId: 6f449762-6a60189e4314 Duration: 85.62 ms Billed Duration: 86 ms Memory Size: 256 MB Max Memory Used: 204 MB
I have been wondering if my issue could be related to something I need to add to somewhere in my serverless.yml, perhaps in the functions: part. That currently looks like this for these two routes:
events:
- http:
path: abc/item
method: get
cors: true
private: true
request:
parameters:
querystrings:
item_id: true
- http:
path: xyz/item
method: get
cors: true
private: true
request:
parameters:
querystrings:
item_id: true
Finally, it's probably good to note that I have added custom middleware to FastAPI to handle the two different database connections I need for connecting to other_items and items tables, though I'm not sure how relevant this is, considering this functions fine when redirecting locally. For this I implemented the solution found here. This custom middleware is the reason for the redirect in the first place (we change connection URI based on route with that middleware), so I figured it's good to share this bit of info as well.
Thanks!
As noted here and here, it is mpossible to redirect to a page with custom headers set. A redirection in the HTTP protocol doesn't support adding any headers to the target location. It is basically just a header in itself and only allows for a URL (a redirect response though could also include body content, if needed—see this answer). When you add the authorization header to the RedirectResponse, you only send that header back to the client.
A suggested here, you could use the set-cookie HTTP response header:
The Set-Cookie HTTP response header is used to send a cookie from the
server to the user agent (client), so that the user agent can send it back to
the server later.
In FastAPI—documentation can be found here and here—this can be done as follows:
from fastapi import Request
from fastapi.responses import RedirectResponse
#app.get("/abc/item")
def get_item(request: Request):
redirect_url = request.url_for('your_endpoints_function_name') #e.g., 'get_item'
response = RedirectResponse(redirect_url)
response.set_cookie(key="fakesession", value="fake-cookie-session-value", httponly=True)
return response
Inside the other endpoint, where you are redirecting the user to, you can extract that cookie to authenticate the user. The cookie can be found in request.cookies—which should return, for example, {'fakesession': 'fake-cookie-session-value-MANUAL'}—and you retrieve it using request.cookies.get('fakesession').
On a different note, request.url_for() function accepts only path parameters, not query parameters (such as item_id in your /abc/item and /xyz/item endpoints). Thus, you can either create the URL in the way you already do, or use the CustomURLProcessor suggested here, here and here, which allows you to pass both path and query parameters.
If the redirection takes place from one domain to another (e.g., from abc.com to xyz.com), please have a look at this answer.

Angular Universal / Node: Backend not accessing session. Creating new on each reload

I am using session based authentication in my Angular Universal app. Problem is when http request is made from Angular app, backend (node.js) doesn't access the ongoing session, but creates new. You might think this is because cors, but the thing is, the first initial load only doesn't access session. So when I open up my app on page that has resolver or guard, that is making http request. That http request is going to create new session. Then navigating to other pages in app, it all works. http requests made after initial load will access the session. If I start from page that has no resolver/guards and then navigate to page that has and makes http request, this request will access to session.
Here is how my session is setup in index.js:
var sessionStore = new MySQLStore(options);
app.use(
session({
key: 'sessionStorage',
store: sessionStore,
secret: config.get('demoSess'),
saveUninitialized: false,
resave: false,
name: 'demo',
cookie: {
maxAge: 60000,
secure: false
},
})
)
const cors = require('cors');
app.use(cors({
origin: [
'http://localhost:4200'
],
credentials: true
})
);
And this how http request is made from frontend:
this.http.get(environment.apiUrl+'/server/page/auth', {withCredentials: true});
Is this how it should be? Backend runs on port 8080 and frontend 4200.
In app.module.ts, I have written TransferHttpCacheModule. If I remove it, I can see from backend, when I console log something, that first http request is made twice- first one doesn't access session and then second one does. So if I was to console.log(req.session.userId) in /server/page/auth, I would get undefined and 1 on next line. As I read, something like this was normal and to get around it, transferstate comes to into play, but as I understand TransferHttpCacheModule is basically easy way to do the transferstate. I tried also with writting the transferstate into resolver and outcomes was same- one request is only made, but that request wont access session.
I am hoping I am missing something when I am making http request from frontend or my session/cors is missing something. At this point I am running out of idea what to check or test, any hint what to check out is welcoming.
So I started to build around my authentication in Angular to use localStorage. I ran there into problem and while searching for solution I ran into tutorial talking something about isPlatformBrowser. So I started thinking, maybe Angular Universal in some way is making two request, but these two request are different and I need to eliminate one of them. So I ended up wrapping my http request with if(isPlatformBrowser(this.platformId)) { } and so far it seems I got my problem fixed.

angular2 & lite server dot in url leads to 404 not found

I'm using Angular2 Beta 14 and calling a URL with a "dot" in it leads to a 404 not found error from the lite server which is 2.2.0.
This is the URL I'm calling:
http://localhost:3000/confirmuser/token/eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjYsInVzZXJOYW1lIjoiYXNkZmFzQGNlZC5saSJ9.PMzNWp8mbUKbSAiOqhOqjhZUYNejXY3pIQueBkc8_2E
The router path in app.component.ts looks like this:
{path: '/confirmuser/token/:token', name: 'ConfirmUser', component: ConfirmUserComponent}
The Chrome console shows this:
Failed to load resource: the server responded with a status of 404 (Not Found)
Ant the Lite Server:
[1] 16.04.13 15:57:13 404 GET /confirmuser/token/eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjYsInVzZXJOYW1lIjoiYXNkZmFzQGNlZC5saSJ9.PMzNWp8mbUKbSAiOqhOqjhZUYNejXY3pIQueBkc8_2E
When ever I call the url without a "dot", the page gets loaded correctly.
My aim here is, to confirm a user sign up. He receives an email with an URL he has to confirm. Using a JWT in this (and other cases) is habit I've been using.
Now I doubt this is an Angular issue, I believe this is a lite server issue.
Anyone experience with this?
Thanks
I found a suitable workaround for this issue.
Basically I'm getting rid of the path parameter ":token" and replacing it by a query parameter
In the app.component.ts the new path now looks like this:
{path: '/confirmuser', name: 'ConfirmUser', component: ConfirmUserComponent}
An the URL like this:
http://localhost:3000/confirmuser?token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjYsInVzZXJOYW1lIjoiYXNkZmFzQGNlZC5saSJ9.PMzNWp8mbUKbSAiOqhOqjhZUYNejXY3pIQueBkc8_2E
In the component that handles this request I can continue to call route params as I was used to. So nothing to change there:
constructor(params: RouteParams){
this.token = params.get('token')
...
This question has been answered in https://stackoverflow.com/a/36283859/1465640#
But it can be summarized with dots doesn't work in urls unless you do some work on the lite-server config.
If you are using webpack then you need to change the config to make it working.
Please make the change in webpack dev server config file
historyApiFallback: {
disableDotRule: true
},

Connect to a password protected FTP through PROXY in Ruby

I'm trying to upload to my server (on Heroku) a file stored in a password protected FTP.
The problem is that this FTP also dont contain my production IP address on his whitelist (and i cant add it..) so i should use a proxy to connect my rails app this FTP.
I tried this code :
proxy_uri = URI(ENV['QUOTAGUARDSTATIC_URL'] || 'http://login:password#myproxy.com:9293')
Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port,"login","password").start('ftp://login:password#ftp.website.com') do |http|
http.get('/path/to/myfile.gz').body
end
But my http.get returns me lookup ftp: no such host.
I also got this code for FTP download, but i dont know how to make it works with a proxy :
ftp = Net::FTP.new('ftp.myftp.com', 'login', 'password')
ftp.chdir('path/to')
ftp.getbinaryfile('myfile.gz', 'public/myfile.gz', 1024)
ftp.close
Thanks in advance.
I realise that you asked this question over 6 months ago, but I recently had a similar issue and found that this (unanswered) question is the top Google result, so I thought I would share my findings.
mudasobwa's comment below your original post has a link to the net/ftp documentation which explains how to use a SOCKS proxy...
Although you don't mention a specific requirement for a HTTP proxy in your original post, it seems obvious to me that is what you were trying to use. As I'm sure you're aware, this makes the SOCKS documentation totally irrelevant.
The following code has been tested on ruby-1.8.7-p357 using an HTTP proxy that does not require authentication:
file = File.open('myfile.gz', 'w')
http = Net::HTTP.start('myproxy.com', '9293')
resp, data = http.get('ftp://login:password#ftp.website.com')
file.write(data) if resp.code == "200"
file.close unless file.nil?
Source
This should give you a good starting point to figure the rest out for yourself.
To get you going, I would guess that you could use user:pass#myproxy.com for basic auth, or perhaps sending a Proxy-Authorization header in your GET request.

(#803) Some of the aliases you requested do not exist: access_token","type":"OAuthException"

I am trying to get access token using from facebook graph API in my rails 2.3 based web application. The request I am sending for that is :
https://graph.facebook.com/oauth/access_token?client_id=<client_id>
&redirect_uri=http://localhost:3001/facebook_callback
&client_secret=<client_secret>
&code=AQBgog2NvoUYQCXsa2bGpj--s9RD71F3zTKX344cUZ-
AWX4CNhdx3Yerl_wmzQkQ4zIUFVS_CRoN0zXaEW63dHcC9sH6_
vl7ljSxwA6TLSrkWVcfdfdrmwBTlMNIzyJr0h6irGW1LCdTw8
Racgd8MQ9RgVn1gFL26epWA
And it is redirecting me to
http://localhost/facebook_callback?code=AQBgog2NvoUYQCXsa2bGpj--
s9RD71F3zTKX344cUZ AWX4CNhdx3Yerl_wmzQkQ4zIUFVS_CRoN0mAB_Sr1H4K
dXIlzXaEW63dHcC9sH6_vl7ljSxwA6TLSrkWVcfdfdrmwBTlMNIzyJr0h6irG
SxsrRAXtdviNsBTMW1LCdTw8Racgd8MQ9RgVn1gFL26epWA
I am getting error in both development and production environment . I am not able to get the access token. Has anyone else face the problem ??
This looks correct - Facebook redirects to your redirect url with the code= parameter. You then need to exchange the code for an access token. See here: http://developers.facebook.com/docs/authentication/
edit: my bad, I misread the first section. You can sometimes have problems using localhost as a redirect. Are you using a live domain without port in your non-test environment?
Well, I found solution of my problem :
The problem was with the path which I was using for request of access_token . I placed a slash in front of the path and bingo. It worked like a charm.
So instead of
oauth/access_token?client_id=#{ #client_id }&redirect_uri=#{ #redirect_uri }&client_secret=#{ #client_secret }&code=#{ code }"
we just need to use
/oauth/access_token?client_id=#{ #client_id }&redirect_uri=#{ #redirect_uri }&client_secret=#{ #client_secret }&code=#{ code }".
Thanks to all people for your efforts.

Resources