How to store a JWT refreshToken cookie response - vue.js

I'm trying to authenticate a user with JWT using GraphQL. Once I login the user I receive the token as a JSON response and a httponly cookie storing the refresh token. (Server-side is using Saleor-core)
From the documentation of Saleor and some other blog-posts I assume that this response cookie should now be stored in the browser and whenever I need to refresh a token the cookie-refreshToken is used to authenticate my request. However, when I switch tabs to "Application" in my dev tools it's just empty.
What is the normal behaviour of the browser after receiving a cookie response? Do I need some extra code to somehow "save" that response cookie?
Did not really find anyone else having this problem so I think the mistake must be somewhere else.
UPDATE
I read somewhere the issue might be that there is no "secure" flag, which resulted from the server debug mode. I turned it off, but the cookie is still not being set.
Response Headers:
HTTP/1.1 200 OK
Connection: keep-alive
Date: Thu, 23 Sep 2021 13:32:33 GMT
Server: uvicorn
Content-Type: application/json
Access-Control-Allow-Origin: https://rewhite-86006--beta-duoa0dwg.web.app
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, Authorization-Bearer
Access-Control-Allow-Credentials: true
Content-Length: 912
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Set-Cookie: refreshToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MzI0MDM5NTQsIm93bmVyIjoic2FsZW9yIiwiZXhwIjoxNjM0OTk1OTU0LCJ0b2tlbiI6Ijd2b0VmMm1DNlZZSyIsImVtYWlsIjoiSnVsaWFuLkZpbmtlQGdtYWlsLmNvbSIsInR5cGUiOiJyZWZyZXNoIiwidXNlcl9pZCI6IlZYTmxjam8zTmc9PSIsImlzX3N0YWZmIjpmYWxzZSwiY3NyZlRva2VuIjoiWm55ek9xVG9rOU9GYXlDZXY0cjFxMUxnaktnTXRRR0VNUVJEalR1eTJDZ1IyOW1GSVBxQ1B1T1hZcTFQNk92cyJ9.Cl6PmoLkO9Hlh36tDOuyNLQCib4FVBwn32hhnmd7Q4E; expires=Sat, 23 Oct 2021 13:32:34 GMT; HttpOnly; Max-Age=2592000; Path=/; Secure
Via: 1.1 vegur
Request Headers:
POST /graphql/ HTTP/1.1
Host: rewhite-saleor-engine.herokuapp.com
Connection: keep-alive
Content-Length: 318
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
sec-ch-ua-platform: "macOS"
content-type: application/json
Accept: */*
Origin: https://rewhite-86006--beta-duoa0dwg.web.app
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://rewhite-86006--beta-duoa0dwg.web.app/
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Thanks for your help!

The Domain attribute on you cookie seems to be different from the origin of your request. You're making a cross-site request and receiving a Set Cookie response from the server (of a different domain).
Normally we run into this issue when running backend and frontend on different domains (for e.g. localhost:3000 and localhost:8080).
Solution:
Recent Chrome browser versions (from 2020) will only set cookies received from cross-site requests if cookie has SameSite=None and Secure attributes set. With Secure set, a cookie will only be sent to server over HTTPS protocol (you need to implement SSL).
As of now, you don't have set either. SameSite defaults to Lax not None. You need to explicitly set it.
OR
You need implement a proxy such that you will request your webapp on https://rewhite-86006--beta-duoa0dwg.web.app and your webapp will proxy this to your Saleor engine domain rewhite-saleor-engine.herokuapp.com. How you do that depends on what frameworks you're using for serving your webapp. You haven't mentioned your it in your question, but I notice you've tagged it under vue.js, so I'll assume that you're using Vue CLI for serving a Vue app.
Its very simple to set up a proxy with Vue CLI. Just look for vue.config.js file in your root directory. If its not there, create it and paste the code below:
module.exports = {
devServer: {
proxy: {
'^/graphql': {
target: 'https://rewhite-saleor-engine.herokuapp.com',
changeOrigin: true,
logLevel: 'debug',
},
},
},
}
Now instead of fetching the refreshToken from rewhite-saleor-engine.herokuapp.com/graphql, you should send the request to your webapp at https://rewhite-86006--beta-duoa0dwg.web.app/graphql, and your web app local server will forward the request to your Saleor backend on Heroku. To your browser it will appear as though the request's response came form the webapp itself, so it won't be a cross-site request anymore.

Related

Cannot generate an authorization code on API Explorer

I'm trying to collect and download my lifelog user data. The first step into doing this is getting a user-access token. I am encountering problems while requesting authorization.
From the sony developer authenticization page I am told to input the following code into my API explorer:
https://platform.lifelog.sonymobile.com/oauth/2/authorize?client_id=YOUR_CLIENT_ID&scope=lifelog.profile.read+lifelog.activities.read+lifelog.locations.read
I am supposed to receive the authorization code as such:
https://YOUR_CALLBACK_URL?code=abcdef
However, this is what the current situation is actually like:
I have replaced my actual client ID below with MY_CLIENT_ID for security reasons
INPUT:
GET /oauth/2/authorize?client_id=MY_CLIENT_ID&scope=lifelog.profile.read%2Blifelog.activities.read%2Blifelog.locations.read HTTP/1.1
Authorization:
Bearer kN2Kj5BThn5ZvBnAAPM-8JU0TlU
Host:
platform.lifelog.sonymobile.com
X-Target-URI:
https://platform.lifelog.sonymobile.com
Connection:
Keep-Alive
RESPONSE:
HTTP/1.1 302 Found
Content-Length:
196
Location:
https://auth.lifelog.sonymobile.com/oauth/2/authorize?scope=lifelog.profile.read+lifelog.activities.read+lifelog.locations.read&client_id=MY_CLIENT_ID
Access-Control-Max-Age:
3628800
X-Amz-Cf-Id:
HILH9w3eOm-6ebs_74ghegYQyWS4xyqA1l0gXPRJuuubsoZ6eiiS3g==
Access-Control-Allow-Methods:
GET, PUT, POST, DELETE
X-Request-Id:
76caccfc976d40259ef30415d10980e9
Connection:
keep-alive
Server:
Apigee Router
X-Cache:
Miss from cloudfront
X-Powered-By:
Express
Access-Control-Allow-Headers:
origin, x-requested-with, accept
Date:
Sun, 22 Jan 2017 03:00:42 GMT
Access-Control-Allow-Origin:
*
Vary:
Accept
Via:
1.1 dc698cd00b7ec82887573cfaba9ecca6.cloudfront.net (CloudFront)
Content-Type:
text/plain; charset=utf-8
Found. Redirecting to https://auth.lifelog.sonymobile.com/oauth/2/authorize?scope=lifelog.profile.read+lifelog.activities.read+lifelog.locations.read&client_id=MY_CLIENT_ID
Nowhere can I see the authorization code in the above code. I even tried copying and pasting the URL (on the last line) into my browser, it says "localhost.com took too long to respond"
This is where I input my request
I am not sure whether it is an issue with the callback URL. I don't have an actual website or app made, I just used the default localhost
I am a beginner in this and would really appreciate all help.

I want to figure out how cookies/ session variables interact to validate a user session

---I have been experimenting on cookies/ session ids and have a little trouble digesting in the concepts.
I'm working on a Debian distro. Using Burp to capture/alter requests/ responses. What I know is below.
a. Cookies get stored in the client machine database at /root/.mozilla/firefox/pya18ecc.default/cookies.sqlite. In the moz_cookies table. I'm using sqlite3 to access the database.
b. Session variables are getting stored in /var/lib/PHP5 on the server.
The PHP code on the SERVER is below
<?php
require_once 'login.php';
$connection = new mysqli($db_hostname,$db_username,$db_password,$db_database);
if($connection->connect_error) die ($connect->connect_error);
if (isset($_SERVER['PHP_AUTH_USER']) &&
isset($_SERVER['PHP_AUTH_PW']))
{
$username = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_USER']);
$password = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_PW']);
$query = "SELECT * FROM user WHERE username = '$username'";
$result = $connection->query($query);
if(!$result) die ($connection->error);
elseif ($result->num_rows)
{
$row = $result->fetch_array(MYSQLI_NUM);
$result->close();
$salt1="!##$";
$salt2="$##!";
$token = hash('ripemd128',"$salt1$password$salt2");
if($token == $row[3])
{
session_start();
$_SESSION['username'] = $username;
$_SESSION['password'] = $password;
$_SESSION['forename'] = $row[0];
$_SESSION['surname'] = $row[1];
echo "$row[0] $row[1] : Hi '$row[0]' you are logged
in as '$row[2]'";
die("<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>");
}
else {die("Invalid Username/ Password Combination");}
}
else
{
die("Invalid Username/ Password Combination");
}
}
else
{
header('WWW-Authenticate: Basic realm="Restricted Section"');
header('HTTP/1.0 401 Unauthorized');
die("Please enter your username and password to Login");
}
$connection->close();
function mysql_entities_fix($connection,$var)
{
return htmlentities(mysql_entities_string($connection,$var));
}
function mysql_entities_string($connection,$var)
{
if (get_magic_quotes_gpc()) $var = stripslahes($var);
return $connection->real_escape_string($var);
}
?>
When I send in the request(1), it lookes like this.
request(1)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
The response(1) is a login page (Basic HTTP authentication)
response(1)
HTTP/1.0 401 Unauthorized
Date: Sat, 28 Mar 2015 07:27:44 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
WWW-Authenticate: Basic realm="Restricted Section"
Vary: Accept-Encoding
Content-Length: 48
Connection: close
Content-Type: text/html
Please enter your username and password to Login
----Till here there has no exchange of session ids or cookies. (If I'm wrong, prompt me ?)
I responded with the username and password and the request(2) is
request(2)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Authorization: Basic YnNtaXRoOm15c2VjcmV0
---- On forwarding the above request what I observe is that I've received the Session id: cl5mi7tbhdnobpv8kkau7thjo6 in the /var/lib/PHP5 even before forwarding the response(2). That's because Server
has created the same and is ready to fowrward it in the response (2)
The response(2) is
response(2)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:36:13 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Set-Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must- revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
----Now on checking SELECT * FROM moz_cookies, I'm not seeing any cookies which are getting saved on the client m/c. So where are they getting saved ? (This is my first question)
Next I've deleted the session variable "cl5mi7tbhdnobpv8kkau7thjo6" from the server and again have hit the
refresh button on the client's browser. The request(3)/ response(3) are below
request(3)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6
Authorization: Basic YnNtaXRoOm15c2VjcmV0
Connection: keep-alive
Cache-Control: max-age=0
response(3)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:50:01 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
-----Now the Server has again got the same session variable set under the /var/lib/PHP5. Shouldn't the server not recognize the session variable ? Also if this is happening because of the saved Cookies, then why isn't I'm able to see them under moz_cookies table ..... Please explain ?(This is my second question)
A few items to consider:
Cookies can be stored on disk or in memory. How they are stored is dependent on browser settings (.e.g Private Browsing mode in Firefox) and server settings (see PHP docs on session handling).
HTTP connections can be cached using the Keep-Alive header.
It looks like you are set up to store session ids on the server in a file, but the browser cookie is being stored in memory rather than in a file, which is why you are seeing the PHP file but not a Firefox cookie file.
As to why the server is recognizing the session id even though you deleted the file, it looks like what is happening is that PHP knows the session is authenticated, meaning it must be cached in RAM on the server. (A PHP guru might be able to fill in the specifics of the session mechanics).
After receiving request 3, PHP looks for the session id file and when it doesn't see it, it writes the file again because it knows that the session is valid. This is expected behavior, because if you want to delete the session, you as a PHP programmer should be doing it in PHP rather than deleting the session id file behind the scenes.
You'll notice that the client sent the session id with request 3. The client will send the session id cookie with each request, but the server should not respond with the Set-Cookie instruction again during this session. The browser now has the session id cookie and can send it with each request, thus no further authentication is needed.
The weakness to this strategy is that it could leave open the possibility of session hijacking. If an attacker could obtain the session id cookie from your browser, they could execute commands against the server using your credentials.
Obtaining the session id may or may not be a trivial task for an attacker depending on many factors. If for instance the victim's computer is compromised with a virus or other malware controlled by the attacker, the attacker probably pwns the victim's computer at that point and can do whatever they want, including taking the session id. Other attacks use cross-site scripting ( XSS ) attacks to trick the victim's browser into divulging the session id.
There are specific defenses against XSS and session hijacking, again the subject is fairly complex and worth researching in detail. A more secure session, for instance, might use a one-time token that is changed with every response, although that strategy can be complicated by the use of asynchronous HTTP calls.
Till here there has no exchange of session ids or cookies. (If I'm wrong, prompt me ?)
No, that's correct. Until here, session_start hasn't been called, so there are no session cookies to exchange.
Now the Server has again got the same session variable set under the /var/lib/PHP5. Shouldn't the server not recognize the session variable ?
It probably should, but it isn't. What I think is happening: When a client sends a session id, the server is happy to use it, even if it doesn't know it yet. This can (in theory) be used for session fixation, which is why it is recommended to regenerate ids each time something in the session changes (in practice, session.use_trans_sid is set to 0 by default, which makes session fixation less of an issue).
What I find odd is that the data is also still there (I couldn't replicate this behavior). My guess would be that you are actually re-submitting the form?
Also if this is happening because of the saved Cookies, then why isn't I'm able to see them under moz_cookies table
My guess is that the cookies are still in the browser memory. Did you try the same thing, but deleting the cookie via the browser interface?
Misc
I know that this is just a test script, but still:
prepared statements are recommended over simple escaping.
don't store plaintext passwords in sessions).
don't echo user input unsanitized.
hashes should be user specific, not site specific.
htmlentities protects against XSS, not SQL injection, and should thus be applied when echoing data, not when inserting it into the database.

How can I authenticate websocket connection

What is a common approach in authenticating of user session for websocket connection?
As I understand websocket message contains data only with no headers. Thus authorization cookie is not available to server backend. How should application distinguish messages from different clients?
Which websocket server are you using?
if your webserver and websocketserver are the same, you could send the sessionid via websocket and force-disconnect any client that does not send a valid sessionid in his first message.
if your websocketserver parses the HTTP headers sent in the HTTP upgrade request properly, it may also save any cookie. this is what a request of my firefox (version 35) looks like:
GET /whiteboard HTTP/1.1
Host: *:*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Sec-WebSocket-Version: 13
Origin: *
Sec-WebSocket-Protocol: whiteboard
Sec-WebSocket-Key: iGPS0jjbNiGAYrIyC/YCzw==
Cookie: PHPSESSID=9fli75enklqmv1a30hbdmg1461
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
as you see, the phpsessionid is transmitted fine. check the documentation of your websocketserver if it parses the HTTP headers. remember that cookies will not be sent if the websocket's domain differs from the webserver's domain.

NSURLConnection and Authenticating to webservices behind ssl?

I'm currently trying to connect to a webservice placed on https://xxx.xxx.xx/myapp
It has anonymous access and SSL enabled for testing purposes atm.
While trying to connect from the 3G network, i get Status 403: Access denied. You do not have permission to view this directory or page using the credentials that you supplied.
I get these headers while trying to connect to the webservice locally:
Headers
Request URL:https://xxx.xxx.xx/myapp
Request Method:GET
Status Code:200 OK
Request Headers
GET /myapp/ HTTP/1.1
Host: xxx.xxx.xxx
Connection: keep-alive
Authorization: Basic amViZTAyOlE3ZSVNNHNB
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Response Headers
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Server: Microsoft-IIS/7.0
X-Powered-By: ASP.NET
Date: Thu, 16 Feb 2012 12:26:13 GMT
Content-Length: 622
But when accessing outside the local area, we get the big ol 403. Which in turn wants credentials to grant the user access to the webservice.
However, i've tried using the ASIHTTPRequest library without success, and that project has been abandoned. And they suggest going back to NSURLConnection.
And i have no clue where to start, not even which direction to take.
-connection:(connection *)connection didRecieveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
The above delegate method of NSURLConnection doesnt even trigger. So i have no idea what so ever how to authenticate myself.
All i get is the parsed results of the xml elements of the 403-page.
I needs dem seriouz helps! plx.
This was all just a major f-up.
The site had ssl required and enabled, and setting ssl required for the virtual directories does some kind of superduper meta-blocking.
So, by disabling ssl required for the virtual directories, it runs over ssl and is not blocking 3G access..

User authentication with XMLHttpRequest works in IE, not in Chrome?

The following function works in IE but not in Chrome:
function doStuff() {
var request = new XMLHttpRequest();
request.open("POST", "http://twitter.com/statuses/update.json", true, "USERNAME-HERE", "PASSWORD-HERE");
request.send("status=STATUS UPDATE HERE");
}
Chrome generates the following request. Note the Authorization header is missing:
OPTIONS /statuses/update.json HTTP/1.1
Host: twitter.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.78 Safari/532.5
Access-Control-Request-Method: POST
Origin: file://
Access-Control-Request-Headers: Content-Type
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
I get the following response (http 401):
HTTP/1.1 401 Unauthorized
Date: Wed, 03 Feb 2010 00:39:33 GMT
Server: hi
Status: 401 Unauthorized
WWW-Authenticate: Basic realm="Twitter API"
X-Runtime: 0.00107
Content-Type: application/json; charset=utf-8
Cache-Control: no-cache, max-age=300
Set-Cookie: _twitter_sess=BAh7BzoHaWQiJTUxMTc2Nzk4N2U0YzMzZmU0ZTQyNzI4NjQyYjI3ODE2Igpm%250AbGFzaElDOidBY3Rpb25Db250cm9sbGVyOjpGbGFzaDo6Rmxhc2hIYXNoewAG%250AOgpAdXNlZHsA--bb61324c3ba12c3cd1794b3895a906a69c154edd; domain=.twitter.com; path=/
Expires: Wed, 03 Feb 2010 00:44:33 GMT
Vary: Accept-Encoding
Content-Length: 73
Connection: close
{"request":"/statuses/update.json","error":"Could not authenticate you."}
So, how am I supposed to pass a username and password to XHR? Webkit/Safari documentation says the open method should take these parameters, so I'm not sure why it is failing.
The solution was that I needed to add
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
The way I'm doing this is... special... so this may not be of much use to others going forward. But once I added this webkit started adding Authorization.
From the look of it, you're trying to do an X-Domain XMLHTTPRequest, which is why Chrome sends the OPTIONS pre-flight request. Because the Twitter server doesn't respond to the OPTIONS request indicating that X-Domain access is okay, you get a failure here.
Your code would only work in IE in the Local Computer zone, or if you turn off x-domain-checking (very dangerous)
Have you tried:
request.setRequestHeader('Authorization', 'yourvalue');