How to get token expiration with `jsonwebtoken` using typescript
Asked Answered
P

9

13

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"
  }
}
Pend answered 27/11, 2017 at 10:12 Comment(0)
C
16

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);
    });
});
Chiou answered 16/11, 2020 at 9:40 Comment(0)
J
9

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

Joy answered 29/11, 2017 at 10:21 Comment(2)
thanks for updating your question. The problem is caused by import jwt from 'jsonwebtoken'. I updated my answer accordingly. Hope it will work for you. If you get an error that require can't be found do npm install @types/node --save-dev, see #31174238Joy
This works, but it would be nice if anyone has a solution where one could still use import instead of require. Perhaps the typings file should be altered.Fluoro
W
9

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.

Weikert answered 22/2, 2019 at 13:54 Comment(2)
How about this error after implementing your answer? Argument of type 'string | undefined' is not assignable to parameter of type 'Secret'' Any ideas?Jurist
@Jurist The only way I found to get rid of ts Argument of type 'string | undefined' is not assignable to parameter of type 'Secret was to create my own type definitions. With regards to the secret key I had to do the following ts // eslint-disable-next-line no-unused-vars declare namespace NodeJS { export interface ProcessEnv { JWT_SECRET: string, JWT_EXPIRATION: string, TOKEN_ISSUER: string } } Tizzy
I
6

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
Integumentary answered 6/9, 2019 at 1:45 Comment(1)
I am having this issue: Module '"./node_modules/@types/jsonwebtoken/index"' has no default export.Patricia
D
4

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

Declivous answered 31/7, 2019 at 7:8 Comment(0)
P
2
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

Patricia answered 16/4, 2020 at 17:25 Comment(1)
yeah this worked amongst all other optionsEnt
B
2

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})

Brentonbrentt answered 10/12, 2020 at 19:7 Comment(0)
T
0

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...
  };
}
Tonina answered 8/9, 2021 at 19:45 Comment(0)
C
0

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'.
Catechize answered 10/8, 2023 at 0:12 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Obduce

© 2022 - 2024 — McMap. All rights reserved.