How to manage JWT properly using React? - reactjs

I am builing a member system with a lot of function like memo or post system...etc, so I think it's more safe to use JWT token, so I let my api return jwt token every time I sign in like below
{
"status": 200,
"message": "",
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MGViYzI0MDRhNmVkNDU2NzUwOTk4YjEiLCJ1c2VybmFtZSI6ImpvaG4iLCJleHAiOjE2MjY5NTgwMzEuMjA3LCJpYXQiOjE2MjYzNTMyMzF9.3t_YzKPq4jk6UuIkzTgFaLoXD0Pq5ktmRFp7xg6dFYU"
}
And it contains userID and userName , but here's the problem , every time I have to use something else like userProfilePicture ,userFriend...etc , I have to make an API request, it's really meaningless to do
eventually I manage it like I used axio.interceptor to verify and before I assign my data to context state I fetch user's all data using this token and assign the data to state , Is'nt it the same way to just return all user's data without JWT?.
It seems to me that JWT Token is kind of useless, can anyone tell me that what's the real ,effiecent way to use JWT Token and what's the common managment?

Use can save your token JWT in localstorage... and call it in header of request (or fetch) when you call api...
But dont forget to set expired token of JWT to one week (or one month, or one year... its up to you)...
Or you can set configuration if your JWT, if your token is expired, it will still send the response to your app....

Related

React-Native: Can I store JWT in AsyncStorage because the tokens expire anyway?

In my server I can set the JWT tokens to expire after a given period of time.
Can I simply have this in my app?
async function getUserDetails() {
const token = await AsyncStorage.getItem('token') //got this token from successful login
const data = await axios.get(apiurl+'&JWT='+token)
console.log(data)
}
By the time the attacker physically gets a hold of the phone, jailbreak it and connect it to her computer, the JWT must have expired? Or am I not understanding something here?
AsyncStorage can not expire data. you have to use a trick. when you are setting your token. just set another key as date equal to new Date(). then in the root of your project setup a function which get date from AsyncStorage and checks the time passed from now to the time you saved token. if its bigger than your desired time just remve token from AsyncStorage
By the way AsyncStorage is not secure. for saving your sensetive information you can use react-native-keychain

How to get a cookie outside getServerSideProps?

I have this code inside getServerSideProps which gives me the token value from a cookie named token:
const token = context.req.cookies?.token || null;
const auth = true;
//Then I sent the token to server
const settings = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': "Bearer " + token,
},
body: JSON.stringify({ "limit": "10" })
};
The cookie is a httpOnly cookie I receive from a post request sent with Set-Cookie header.
The thing is, I want to use the token not only in page components (getServerSideProps is only in page components). In other components I'd like to sometimes use functions that give me more data, let's say all the messages of the client - based on his token (I limit it to 10 in logs.js and I want to increase it in my inner component functions) . Is it safe to pass the token via props and then use it in my functions? I have logs.js component, which has another component named Messages, and inside the Messages component I want to call a function to get more messages but I am not sure whether it is safe or not because the idea of getting the token in getServerSideProps is that nobody can see it, or am I wrong?
If I am not wrong, what is the best way to get the token from the client-side in order to send requests inside inner components?
the idea of getting the token in getServerSideProps is that nobody can see it
Not really when it comes to cookies. Cookies will be sent to the browser so anyone can see it anyways. What you do with it in getServerSideProps is hidden, but the cookie itself is visible.
Because it's an httpOnly cookie, you can't access it with javascript on the client. So if you need the cookie value in javascript, you have a few options:
Read the cookie in getServersideProps and pass that value to your page and through to your components. This will work if you only need your components to read the cookie value.
Change to a { httpOnly: false } cookie which will allow it to be read (and written to) by javascript. I wouldn't do this if it has anything to do with security, because then anyone can not only read the cookie but could change it and do whatever they want with it.
You mentioned it's a token - the big question is: what is the token for in terms of security? You mention using it to determine if you should have more than 10 logs. Is that a business requirement? Would something bad happen (like you lose money, a breach, etc?) if someone manipulated it to show 20, 30, 1,000?
If your business needs to show the user only 10 except in the case where his/her token increases that limit, and you don't want the user to manipulate the limit, leave it as httpOnly, read it in getServerSideProps, and then pass the number to your component. Then, nothing can be manipulated or changed because the client can't mess with the token to unlock more logs.

How to decode access token from Microsoft OAuth2

I am building a web application using CakePHP 4.1.
And it has authorization from Microsoft Active Directory(OAuth2).
What I am going to do is to decode the access token and redirect to Microsoft login page when it is expired.
So I can get token from authorization like this.
https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize?client_id=CLIENT_ID&response_type=token&redirect_uri=LOGIN_REDIRECT_URI&response_mode=form_post&scope=offline_access https://graph.microsoft.com/.default'
I tried to decode using firebase/jwt, but I am not sure what should be $key.
JWT::decode($accessToken, $key, array('RS256'))
I can get the decoded result if I enter the token in jwt.ms
I don't have any special claims, so $key should be plain.
And I want to know if there is another way to decode jwt.
Firebase JWT only supports decoding with signature validation, so a key is always required.
You can manually decode the token, it's just JSON as base64, however without validation there's no way to know whether the information hasn't been forged, so I wouldn't trust any information obtained that way, not even the expiration time.
Usually OAuth APIs return an expiration time in the response for access token requests (and so does Microsofts Identity Platform API), which your app can store alongside the token and use for checking for possible token expiration.
Refreshing upon receiving invalid token errors from the API is usually an option too, then you don't need to care about the expiration time yourself at all.
I would certainly suggest any of those options over trusting arbitrary client data.
That being said, if you still want the unvalidated expiration time from the token, just do what Firebase JWT does internally:
$tks = \explode('.', $accessToken);
if (\count($tks) != 3) {
throw new \UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) {
throw new \UnexpectedValueException('Invalid header encoding');
}
if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) {
throw new \UnexpectedValueException('Invalid claims encoding');
}
if (false === ($sig = JWT::urlsafeB64Decode($cryptob64))) {
throw new \UnexpectedValueException('Invalid signature encoding');
}
https://github.com/firebase/php-jwt/blob/v5.2.0/src/JWT.php#L81-L94

Why does WebApp2 auth.get_user_by_session() change the token?

I am using WebApp2 with auth for user sessions. My client will occasionally make nearly simultaneous requests to the server. The first one will make a request with session data that looks like this:
{
'cache_ts': 1408106895,
'token': u'GXpsaVQh5ZWtqxJMUBpGTr',
'user_id': 5690665774088192L,
'remember': 1,
'token_ts': 1408034938
}
Then after a call to auth.get_user_by_session(), the session comes back like this:
{
'cache_ts': 1408124980,
'token': u'0IVduczdGR5PkrMqNhBvzW',
'user_id': 5690665774088192L,
'remember': 1,
'token_ts': 1408124980
}
As you can see the token has been changed, and the timestamps updated.
Nearly simutaneously, another request is made that contains the same initial session data.
{
'cache_ts': 1408106895,
'token': u'GXpsaVQh5ZWtqxJMUBpGTr',
'user_id': 5690665774088192L,
'remember': 1,
'token_ts': 1408034938
}
However, that token is now invalid, so the session data is set to None. This wipes the users session, and causes lots of problems. Is there some setting I should be using to extend the life of the UserToken? Is there a more appropriate method than get_user_by_session()? I woud imagine that nearly simultaneous requests with the same session data shouldn't cause enormous issues. The ideal situation would be that if auth received invalid or expired tokens it would just ignore them, and throw an error.
Update 1
Hoped it was something simple like passing False to get_user_by_session(). That of course killed the session immediately.
Update 2
I've found that I only really need the user_id field, and that comes for free with the cookie data. Implementing that reduces the frequency of the issue. However the problem isn't actually fixed, and I'd love some input from anyone with familiarity of this library.
This is due to token_new_age parameter which defaults to 1 day so... every 24h the token will change.
This is a security measure because if someone hacks that session it will only work for 24h.
Parameter 'token_max_age' will also delete the token when time is consumed.

Google Channel API sending message with token

In documents it says 'client_id' part can actually be the token, however it doesn't work. Anyone know why?
https://developers.google.com/appengine/docs/python/channel/functions
If the client_id parameter is actually a token returned by a create_channel call then send_message can be used for different versions of the app. For instance you could create the channel on the front end and then send messages from a backend of the app.
the reason i want to use this, is because i want to send messages to anonymous users as well, without requiring them to login. i don't know if it is possible to assign them a 'client_id' if token doesn't work.
this is how i am creating the token
user = users.get_current_user()
if user:
token = channel.create_channel(user.user_id())
else:
token = channel.create_channel(str(uuid.uuid4()))
then injecting into client
template_values = {
'token' : token,
}
on the client side open the channel
openChannel = function() {
var token = '{{ token }}';
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpened,
'onmessage': onMessage,
'onerror': function() {},
'onclose': function() {}
};
var socket = channel.open(handler);
socket.onopen = onOpened;
socket.onmessage = onMessage;
}
now send a message
var xhr = new XMLHttpRequest();
xhr.open('POST', path, true);
xhr.send();
in the server,
when the message is received send back a message using the token
channel.send_message(token, someMessage)
back to client
onMessage = function(m) {
alert("you have some message");
}
this sequence works fine if client_id() is used instead of token when calling send_message
In response to btevfik's initial question: Allowing tokens or client_id in send_message is a feature released in 1.7.5 (very recently). Some people may not be familiar with it yet so therefore they suggest to use client_id. Both should work!
The only thing that I can see in your code is the fact that you should not rely on token variable to be correct in between two requests. They may not even land on the same instance of the app. If you share your code with more details I may be able to spot something. The proper way would be to either store the token in the datastore or pass it from the client as a parameter when you send the message that will trigger a message back.
The purpose of this feature was to allow people to send messages from backends (or other versions). Before was not possible whereas now you can do it if you use directly the tokens instead of the client_id.
Long time this post has been around, but just curious about your usage of the token global variable?
I don't see this code:
global token
before you set the token
user = users.get_current_user()
if user:
token = channel.create_channel(user.user_id())
else:
token = channel.create_channel(str(uuid.uuid4()))
If that code is missing, then token will be set in the local scope of the function above and not globally. So, the token value used later will be None (or to what ever the token was initialised with.)
Just a thought, if its still relevant.
I don't think you actually have a problem here.
You are able to send messages to users that are logged in or not.
The problem you are having I think is knowing that there are multiple ways to use the channel API re: tokens.
https://developers.google.com/appengine/docs/python/channel/overview#Life_of_a_Typical_Channel_Message
In this example, it shows the JavaScript client explicitly requests a token and sends its Client ID to the server. In contrast, you could choose to design your application to inject the token into the client before the page loads in the browser, or some other implementation if preferred.
This diagram shows the creation of a channel on the server. In this
example, it shows the JavaScript client explicitly requests a token
and sends its Client ID to the server. In contrast, you could choose
to design your application to inject the token into the client before
the page loads in the browser, or some other implementation if
preferred.
Here's my demo implementation, hope it helps somehow: https://github.com/Paul1234321/channelapidemo.git
Here's the code for creating the channel on GAE:
client_id = str(uuid.uuid4()).replace("-",'')
channel_token = channel.create_channel(client_id)
And in the JS:
channel = new goog.appengine.Channel('{{ token }}');
Have a look at it in action: http://pppredictor.appspot.com/
You shouldn't store request-specific values in global variables. Store them in a cookie or pass them as a request parameter instead.

Resources