Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reissue JWT when expired #83

Closed
ashraful88 opened this issue Feb 4, 2016 · 11 comments
Closed

Reissue JWT when expired #83

ashraful88 opened this issue Feb 4, 2016 · 11 comments

Comments

@ashraful88
Copy link

@ashraful88 ashraful88 commented Feb 4, 2016

I'm storing jwt in a storage. of if client request with expired token which is in my storage then I want to reissue a jwt. but problem is how to get payloads from a expired jwt ?

@tyson1987

This comment has been minimized.

Copy link

@tyson1987 tyson1987 commented Feb 17, 2016

i would also like to know how to reissue a jwt token within datapower.

@SiasMey

This comment has been minimized.

Copy link

@SiasMey SiasMey commented Mar 8, 2016

You probably shouldnt.

The better thing and apparently more standard is to have a seperate second token in storage that is only allowed to refresh the main token.

So you have a token that expires in say ... an hour. You also have a second token that expires in 2 hours, but only has permission to refresh the main token. This will give the client an hour after their token expires to request a refreshed token using the "refresh" token that is still ok.

This interaction allows you to set up a workflow where if a client is idle for longer than a certain period, their tokens expire, but while they are active, their tokens will be kept up to date.

@tuupola

This comment has been minimized.

Copy link

@tuupola tuupola commented Mar 8, 2016

Tokens are just Base64 encoded data with a signature. You can access header and payload with something like following:

list($header, $payload, $signature) = explode(".", $token);

print base64_decode($header) . "\n";
print base64_decode($payload) . "\n";
@dperjar

This comment has been minimized.

Copy link

@dperjar dperjar commented Mar 9, 2016

@ashraful88 ashraful88 closed this Mar 15, 2016
@ashraful88 ashraful88 reopened this Mar 15, 2016
@BonnieDoug

This comment has been minimized.

Copy link

@BonnieDoug BonnieDoug commented Apr 25, 2016

I'm not sure if my way around this is correct, but I'll air it for opinions anyway.

What I do is issue a Token on successful authorization, with an hour validity. Then on every subsequent I check the token, and then create a new token return it to the client and save it to the file-system, overwriting the previous one. This way, as long as the user remains active their token will never be out of date.

@fjaguado

This comment has been minimized.

Copy link

@fjaguado fjaguado commented Aug 20, 2016

This is how I reissue the same token updating times. I catch the 'ExpiredException' and then I try to decode again with a huge leeway:

 public static function refresh($token) {
         try{
             $decoded = JWT::decode($token, JWT_SECRET, ['HS256']);
             //TODO: do something if exception is not fired
         }catch ( \Firebase\JWT\ExpiredException $e ) {
             JWT::$leeway = 720000;
             $decoded = (array) JWT::decode($token, JWT_SECRET, ['HS256']);
             // TODO: test if token is blacklisted
             $decoded['iat'] = time();
             $decoded['exp'] = time() + self::$offset;

             return JWT::encode($decoded, JWT_SECRET);
         }catch ( \Exception $e ){
             var_dump($e);
         }
     }
@parmap

This comment has been minimized.

Copy link

@parmap parmap commented Feb 1, 2018

Because i run into same problem, i used little bit different solution

        try {
            $data = JWT::decode($accessToken);
        } catch (ExpiredException $e) {
            \Firebase\JWT\JWT::$timestamp = 0;
            $data = JWT::decode($accessToken);
        }
        ...

it's little bit better, because if you have non expiring refresh token, which can gain new access token, then you don't have to worry which leeway is enough, cause the condition is ($timestamp - static::$leeway) >= $payload->exp, so this solution will success with any exp higher then zero.

@williamqian

This comment has been minimized.

Copy link

@williamqian williamqian commented Apr 7, 2018

@fjaguado so if the token was revealed,it will be used forever?

@cottton

This comment has been minimized.

Copy link

@cottton cottton commented Dec 17, 2018

Im worried about how users above are using JWT.

JWT is meant to be stateless. They are signed - so they are immutable.
They reason why we should use them is to NOT check the storage on the server side.

// incomming JWT
if isValid(JWT)
    data = getData(JWT)
    // further checks (authorization) and execute requested action
else
    return non success 401 Unauthorized

If you request f.e. a database to check if the JWT is valid on EVERY request
then you should not use JWT.


Refesh:
If a token is not valid then you create a NEW one.
Ofc you could reuse the data inside the JWT.
BUT this would mean you have to check if nothing else failed.
In this repo you are lucky: ExpiredException gets thrown last. Means all other checks has been passed - so f.e. the signature is valid.
BUT this does NOT mean you should reuse the data from the invalid token.
Few updates later the ExpiredException gets thrown at the beginning of ::decode and all of you are using non-validated data from a manipulated JWT.

What you do if the token has been expired?

You return an error response.
The client that requested the action now has to check if and WHY the action got denied.

Draft:

// client request 
jwt = this->getJWT() // load JWT from storage
client = new HttpClient()
client->setUrl(url) // set your request target (f.e. get list of product items ...)
client->setAuthorizationHeader(jwt)
response = client->get()
if response->isSuccess()
    data response->getBody()
    // ... success handling ...
else
    // request denied
    if response->getStatusCode === 401
        // invalid token server response
        // lets get a new one
        refreshToken = this->getRefreshToken() // load refresh token from storage
        client = new HttpClient()
        client->setUrl(this->getServerRefreshUrl())
        client->setAuthorizationHeader(refreshToken)
        response = client->get()
        if response->isSuccess()
            // refresh-request success
            // received new access- AND refresh-token from server
            // store new tokens
            data = response->getBody()
            jwt = data[access_token]
            resfreshToken = data[refresh_token]
            this->saveJWT(jwt) // overwrite existing
            this->save(refreshToken) overwrite existing
            // execute actual request again (see above)
        else
            // refresh-request failure
            // need to login to receive new JWT and refresh token
            loginCredentials = this->getLoginCredentials()
            client = new HttpClient()
            client->setUrl(this->getServerLoginUrl())
            response = client->post(loginCredentials)
            if response->isSuccess()
                // login-request success
                // received access- AND refresh-token from server
                // store tokens
                data = response->getBody()
                jwt = data[access_token]
                resfreshToken = data[refresh_token]
                this->saveJWT(jwt) // overwrite existing
                this->save(refreshToken) overwrite existing   
                // execute actual request again (see above)
            else
                // invalid login attempt
                // ... handle invalid login credentials ...
    else
        // response is not 401 --further error handling (404? ...)

Ofc this needs to be written clean in functions|methods.

To answer the original question: no, you should not reuse the JWT data.
Except you can make sure that all other checks have been made - so f.e. the signature on the JWT was valid.
The server creates a new JWT and MUST use the current data from the storage.
This f.e. would prevent using expired or modified data like permissions (that has been revoked|changed).

@AmrAlmagic

This comment has been minimized.

Copy link

@AmrAlmagic AmrAlmagic commented Dec 20, 2018

@williamqian No
this line

$decoded['exp'] = time() + self::$offset

refer to expiration date with $offset added to current time in seconds.

@ashraful88 ashraful88 closed this Dec 26, 2018
@nmvuong92

This comment has been minimized.

Copy link

@nmvuong92 nmvuong92 commented Jan 25, 2019

### ### ### ### good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.