How to get token expiration with `jsonwebtoken` using typescript

44.1k Views Asked by At

I'm using jsonwebtoken to decode a token, and I'm trying to get the expiration date. Typescript is throwing errors regarding the exp property, and I'm not quite sure how to solve them:

import jwt from 'jsonwebtoken'

const tokenBase64 = 'ey...' /* some valid token */

const token = jwt.decode(tokenBase64)
const tokenExpirationDate = token.exp
//                                ^^^
// Property 'exp' does not exist on type 'string | object'. Property 'exp' does not exist on type 'string'.

I have installed @types/jsonwebtoken, and looked for a token type to cast token, but did not find any. Suggestions?

Using

.tsconfig:

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "jsx": "Preserve",
    "moduleResolution": "Node",
    "module": "ESNext",
    "sourceMap": true,
    "removeComments": true,
    "allowSyntheticDefaultImports": true,
    "target": "ESNext"
  }
}
9

There are 9 best solutions below

2
jps On

I got the same error message when I used the line import jwt from 'jsonwebtoken' With var jwt = require('jsonwebtoken'); [1] instead it works fine:

var jwt = require('jsonwebtoken');
var tokenBase64 = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoiMTUxMTk1MDcwMyIsImFkbWluIjp0cnVlfQ.wFC-9ZsqA9QuvLkRSkQmVYpUmgH9R-j8M4D0GECuPHY';

const token = jwt.decode(tokenBase64);
const tokenExpirationDate = token.exp
console.log(tokenExpirationDate);

[1] see also https://github.com/auth0/node-jsonwebtoken

2
user3582315 On

The only way I found to user import is:

import { sign, verify } from 'jsonwebtoken';
sign('Hello', 'secret');

But I think the require method is better so that you don't have to explicitly import every single function.

0
Elias On

I think import * as jwt from 'jsonwebtoken'; should work as expected.

1
paulboony On

As of jsonwebtoken 8.3, jsonwebtoken.decode() has the following type definitions:

export function decode(
    token: string,
    options?: DecodeOptions,
): null | { [key: string]: any } | string;

Since Typescript cannot infer the correct type and exp is not known, the simplest way out is to cast the result to any.

import jwt from 'jsonwebtoken'

const tokenBase64 = 'ey...' /* some valid token */

const token: any = jwt.decode(tokenBase64)
const tokenExpirationDate = token.exp
1
Alan On
import * as jwt from 'jsonwebtoken'

const { authorization } = ctx.req.headers
const token = authorization.replace('Bearer ', '')
const decoded = jwt.verify(token, 'APP_SECRET')
const userId = (decoded as any).userId

Of course you can type decoded the way you use the token instead of any

0
JSGuru On

This is how I am using decode with TS

import jwt from 'jsonwebtoken';

export const isTokenExpired = (token: string): boolean => {
    try {
        const { exp } = jwt.decode(token) as {
            exp: number;
        };
        const expirationDatetimeInSeconds = exp * 1000;

        return Date.now() >= expirationDatetimeInSeconds;
    } catch {
        return true;
    }
};

Not needed but here you go as well

import 'jest';
import jwt from 'jsonwebtoken';

import { isTokenExpired } from 'path-to-isTokenExpired/isTokenExpired';

describe('isTokenExpired', () => {
    it('should return true if jwt token expired', () => {
        const currentTimeInSecondsMinusThirtySeconds = Math.floor(Date.now() / 1000) - 30;
        const expiredToken = jwt.sign({ foo: 'bar', exp: currentTimeInSecondsMinusThirtySeconds }, 'shhhhh');

        expect(isTokenExpired(expiredToken)).toEqual(true);
    });

    it('should return false if jwt token not expired', () => {
        const currentTimeInSecondsPlusThirtySeconds = Math.floor(Date.now() / 1000) + 30;
        const notExpiredToken = jwt.sign({ foo: 'bar', exp: currentTimeInSecondsPlusThirtySeconds }, 'shhhhh');

        expect(isTokenExpired(notExpiredToken)).toEqual(false);
    });

    it('should return true if jwt token invalid', () => {
        expect(isTokenExpired('invalidtoken')).toEqual(true);
    });
});
0
pa1nd On

The return type of jwt.verify and jwt.decode is 'string | object'.

In your case, you have some additional information that Typescript does not have about the type of the return type. So you can add it like this:

const token = jwt.decode(tokenBase64) as {exp: number}
const tokenExpirationDate = token.exp

Of course you can add any other value in the object as well.

While it's reasonable to assume that exp is present, other keys might not be present. Make sure that the token you are decoding actually includes them or add it as an optional value: ({exp: number; random?: string})

0
Emzaw On

I found myself creating a helper for this (class based solution - can be used as separate function of course):

import { JwtPayload, verify } from "jsonwebtoken";

export class BaseController {
  // ...
  static decodeJWT = <T extends { [key: string]: any }>(token: string) => {
    return verify(token, process.env.JWT_ACCESS_TOKEN!) as JwtPayload & T; 
    // this typing allows us to keep both our encoded data and JWT original properties
  };
}

used in controllers like:

import { BaseController } from "./BaseController";

export class UserController extends BaseController {
  static getUser = async (
    // ...
  ) => {
    // get token
    // username may or may not be here - safer to check before use
    const payload = this.decodeJWT<{ username?: string }>(token); 
    // no error here, we can extract all properties defined by us and original JWT props
    const { username, exp } = payload;
    // do stuff...
  };
}
1
Ronner On

You can do it like this:

import jwt, { JwtPayload } from 'jsonwebtoken'

const tokenBase64 = 'ey...' /* some valid token */

const token = jwt.decode(tokenBase64) as JwtPayload
const tokenExpirationDate = token.exp
//                                ^^^
// token is now seen as a JwtPayload instance and you can use it's properties such as 'exp'.