What to store in a JWT? - session

How do you guys deal with the same user on multiple devices? Won't data such as {admin: true} become stale except for the device that changed it?
Should this even be in a JWT? If not, and we resort to only putting the user ID, won't that be just like a cookie-based session since we store the state on the server?

The JWT RFC establishes three classes of claims:
Registered claims like sub, iss, exp or nbf
Public claims with public names or names registered by IANA which contain values that should be unique like email, address or phone_number. See full list
Private claims to use in your own context and values can collision
None of these claims are mandatory
A JWT is self-contained and should avoid use the server session providing the necessary data to perform the authentication (no need of server storage and database access). Therefore, role info can be included in JWT.
When using several devices there are several reasons to revoke tokens before expiration, for example when user changes password, permissions or account deleted by admin. In this case you would need a blacklist or an alternative mechanism to reject the tokens
A blacklist can include the token unique ID jti or simply set an entry (sub - iss) after updating critical data on user (password, persmissions, etc) and currentTime - maxExpiryTime < last iss. The entry can be discarded when currentTime - maxExpiryTime > last_modified (no more non-expired tokens sent).
Registered Claims
The following Claim Names are registered in the IANA "JSON Web Token Claims" registry established by Section 10.1.
iss (issuer): identifies the principal that issued the JWT.
sub (subject): identifies the principal that is the subject of the JWT. Must be unique
aud (audience): identifies the recipients that the JWT is intended for (array of strings/uri)
exp (expiration time): identifies the expiration time (UTC Unix) after which you must no longer accept this token. It should be after the issued-at time.
nbf(not before): identifies the UTC Unix time before which the JWT must not be accepted
iat (issued at): identifies the UTC Unix time at which the JWT was issued
jti (JWT ID): provides a unique identifier for the JWT.
Example
{
"iss": "stackoverflow",
"sub": "joe",
"aud": ["all"],
"iat": 1300819370,
"exp": 1300819380,
"jti": "3F2504E0-4F89-11D3-9A0C-0305E82C3301"
"context": {
"user": {
"key": "joe",
"displayName": "Joe Smith"
},
"roles":["admin","finaluser"]
}
}
See alternatives here https://stackoverflow.com/a/37520125/6371459

Related

Is it safe for the default NEAR TLA to have a FullAccess key?

I noticed that the near top level account on NEAR MainNet has got this access key associated with it:
{
"public_key": "ed25519:5zset1JX4qp4PcR3N9KDSY6ATdgkrbBW5wFBGWC4ZjnU",
"access_key": {
"nonce": 1568,
"permission": "FullAccess"
}
}
This key is hard-coded in the genesis.
From what I understand, this effectively means that an entity in possession of the corresponding private key may at any time delete any account that has its ID ending with .near, transferring all funds from that account wherever they choose. This includes all accounts created via the official NEAR web wallet or by otherwise calling near.create_account().
I'd like to know if my understanding is correct, whether this is absolutely required for the network to function or not, and what security implications this might have for a typical user.
I'm pretty sure you cannot affect a subaccount from the parent like this. If someone deletes the near account, your account would not be affected. and unless the near account itself is holding a FullAccess key to subaccounts, it doesn't control them
This would also be easy to test. On TestNet try creating a subaccount and deleting it from the parent. If you don't have a FullAccess key to an account then your DeleteAccount action will be rejected by the network.

What is relation between AuthenticationScheme and IIdentity.AuthenticationType

How are they connected? I can use HttpContext.SignInAsync, and then HttpContext.User is set to provided identity, however I have one identity and multiple authentication ways - what if user login as Customer and then moves to AdminPanel?

How do I generate a unique token (for e-mail verification)?

I want to implement a system that after user signs up, user will receive an email includes a link to verify this email is for that user.
The way I generate the token for verifying the email is like this:
import (
"crypto/rand"
"encoding/base64"
)
func generateToken() (string, error) {
b := make([]byte, 35)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(b), nil
}
But what I want to ask is if this method is OK? How to make all the token generated by this method is unique ?
What is the normal way to implement this system ?
Please give me some ideas and tell me if this method of generating token is good.
Thanks.
Check out https://pkg.go.dev/github.com/google/uuid#NewRandom.
And you may want to consider storing this in a database with the email address and perhaps an expiry date / time so that the verification doesn't stay there forever. You may only want to allow people to verify within 24 hours, or 7 days and so on. Have another job that periodically cleans expired and non-verified emails.
Two points:
No, the method as presented won't guarantee them to be unique.
You don't need to have all your tokens to be unique.
To expand on these points…
You're dealing with a set of outstanding verification requests.
That is:
A request is made by the user;
You generate a unique verification token and store it into some presistent database. This is needed in order for verification to work anyway.
The user receives your e-mail and clicks that link from it which contain your token. At this point you remove the information about this pending verificaton request from your persistent storage.
As you can see, at any given time you only have several outstanding verification requests. Hence this situation has two important properties:
You only need the tokens of these outstanding requests be different from one another. It's OK to have a verification token to be the same as that of some past (or future) request.
Your tokens have to be hard-to-guess (obviously). I'm sure you already understand that.
So, the approach to generating a new token is as follows:
Generate something hard-to-guess.
Compare it with the tokens bound to the outstanding/pending verification requests persisted in your storage.
If you find an outstanding request with the same token, you have a collision so go to step (1) and repeat.
Otherwise the token is OK so proceed with it and persist the data about this request.
Once the request passed verification, remove it from your storage.
Exact algorythm for generating tokens does not matter much. I'd say an UUID or something looking like SHA-256/512 calculated over some random data is OK.

Creating Permanent AccessToken in loopback

How to create a permanent access token for a StrongLoop API. Now for every user login it creates an access token. And unnecessary entry in my db
I can increase the validity of access token(ttl) as mentioned here.
But still it will generate for new login.
Loopback has an option that will allow you to create a permanent access token:
allowEternalTokens Boolean Allow access tokens that never expire.
https://loopback.io/doc/en/lb3/Model-definition-JSON-file.html#advanced-options
Here's what I did:
Enable allowEternalTokens for the User model
In server/model-config.json:
"User": {
"dataSource": "db",
"options": {
"validateUpsert": true,
"allowEternalTokens": true
}
},
When logging in, set ttl to -1
User.login(
{
email: email,
password: password,
ttl: -1,
},
As you've already figured out, every time you log in a new (different) access token will be created. So if you want to reuse the same access token, log in only once. You can get the access token from the AccessToken model (or directly from the database)
AccessToken.findOne(
{
where: {
userId: userId,
},
},
If you have a custom user model, you can set allowEternalTokens directly in the model definition file. In addition, if you have a custom user model you'll also need to update the relations of the AccessToken model (either the built-in one or your custom one if you have it) to point to the custom user model.
More info on custom user/access token models here: http://loopback.io/doc/en/lb3/Authentication-authorization-and-permissions.html#preparing-access-control-models
You are mixing up 2 different things. The AccessToken entry creation and the ttl value for the AccessToken.
When a user logs in a new AccessToken is created. If the user logs out the AccessToken is removed. If the user logs in 2 times, for example from 2 different devices, then you will get 2 AccessTokens, so this way the user will be able to access your app from the 2 devices simultaneously.
If the user wants to log in from the same device and he already has a valid token, your app should recognise this and log him in automatically.
Obviously if the ttl value is expired, the token will not be valid any more. This token will be removed if is tried to be used. I guess if you don't want this records in your database, you could create a custom cron job that removes expired tokens.
Regarding the permanent access token, it will require to disable the ttl value, and that is not possible at the moment for the default AccessToken model. I created a pull request to support that, if you are interested you could chime in and see if it gets merged.

Phil Strugeon REST server - is there really security vulnerability in Digest Auth or I misunderstood something?

Recently I downloaded Phil Strugeon REST server for CodeIgniter.
I reviewed source code and when I come to Digest authentication I saw following code:
if ($this->input->server('PHP_AUTH_DIGEST'))
{
$digest_string = $this->input->server('PHP_AUTH_DIGEST');
}
elseif ($this->input->server('HTTP_AUTHORIZATION'))
{
$digest_string = $this->input->server('HTTP_AUTHORIZATION');
}
else
{
$digest_string = "";
}
And bit later after some checks for absence of $digest_string and presence of username:
// This is the valid response expected
$A1 = md5($digest['username'].':'.$this->config->item('rest_realm').':'.$valid_pass);
$A2 = md5(strtoupper($this->request->method).':'.$digest['uri']);
$valid_response = md5($A1.':'.$digest['nonce'].':'.$digest['nc'].':'.$digest['cnonce'].':'.$digest['qop'].':'.$A2);
if ($digest['response'] != $valid_response)
{
header('HTTP/1.0 401 Unauthorized');
header('HTTP/1.1 401 Unauthorized');
exit;
}
In Wikipedia I see following text about HTTP Digest Auth:
For subsequent requests, the hexadecimal request counter (nc) must be greater than the last value it used – otherwise an attacker could simply "replay" an old request with the same credentials. It is up to the server to ensure that the counter increases for each of the nonce values that it has issued, rejecting any bad requests appropriately.
The server should remember nonce values that it has recently generated. It may also remember when each nonce value was issued, expiring them after a certain amount of time. If an expired value is used, the server should respond with the "401" status code and add stale=TRUE to the authentication header, indicating that the client should re-send with the new nonce provided, without prompting the user for another username and password.
However I can't see anything about checking cnonce, nc or nonce in source code.
Does it mean that somebody who recorded request from Client to Server that passed authentification may just "replay" it in future and receive fresh value of resource?
Is it really vulnarability? Or I misunderstood something?
I noticed this too when looking at codeigniter-restserver. It IS vulnerable to replay attacks because, as you said, it does not enforce the nonce.
Digest authentication requires a handshake:
client makes request with Authorization. It will fail because the client does not yet know the nonce
server responds with WWW-Authenticate header, that contains the correct nonce to use
client makes same request using the nonce provided in the server response
server checks that the nonces match and provides the requested url.
To accomplish this, you'll need to start a session on your REST server to remember the nonce. An easy scheme for ensuring nonce is always unique is to base it on current time using a function such as uniqid()

Resources