{"id":49880016,"url":"https://github.com/pardnio/node-jwt-auth","last_synced_at":"2026-05-15T13:40:37.939Z","repository":{"id":295404922,"uuid":"989679605","full_name":"pardnio/node-jwt-auth","owner":"pardnio","description":"A JWT authentication package for Node.js providing both Access Token and Refresh Token mechanisms, featuring fingerprint recognition, Redis storage, and automatic refresh functionality","archived":false,"fork":false,"pushed_at":"2025-05-28T05:48:01.000Z","size":153,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T17:35:29.202Z","etag":null,"topics":["access-token","authentication","auto-refresh","jwt","pardnchiu","refresh-token"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pardnio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-24T15:48:34.000Z","updated_at":"2026-04-05T18:07:30.000Z","dependencies_parsed_at":"2025-05-26T17:54:08.325Z","dependency_job_id":null,"html_url":"https://github.com/pardnio/node-jwt-auth","commit_stats":null,"previous_names":["pardnltd-tools/nodejs-jwt-auth","pardnchiu/nodejs-jwt-auth","pardnio/node-jwt-auth"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pardnio/node-jwt-auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pardnio%2Fnode-jwt-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pardnio%2Fnode-jwt-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pardnio%2Fnode-jwt-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pardnio%2Fnode-jwt-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pardnio","download_url":"https://codeload.github.com/pardnio/node-jwt-auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pardnio%2Fnode-jwt-auth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33068889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T11:35:32.926Z","status":"ssl_error","status_checked_at":"2026-05-15T11:35:31.362Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["access-token","authentication","auto-refresh","jwt","pardnchiu","refresh-token"],"created_at":"2026-05-15T13:40:36.735Z","updated_at":"2026-05-15T13:40:37.924Z","avatar_url":"https://github.com/pardnio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JWT Auth (Node.js)\n\n\u003e A JWT authentication package providing both Access Token and Refresh Token mechanisms, featuring fingerprint recognition, Redis storage, and automatic refresh functionality.\u003cbr\u003e\n\u003e version Golang can get [here](https://github.com/pardnchiu/golang-jwt-auth)\n\n[![version](https://img.shields.io/npm/v/@pardnchiu/jwt-auth)](https://www.npmjs.com/package/@pardnchiu/jwt-auth)\n\n## Feature\n\n- ### Dual Token System\n  - Access Token (short-term) + Refresh Token (long-term)\n  - Automatic token refresh without requiring re-login\n  - ES256 algorithm (Elliptic Curve Digital Signature)\n- ### Device Fingerprinting\n  - Generates unique fingerprints based on User-Agent, Device ID, OS, and Browser\n  - Prevents token misuse across different devices\n  - Automatic device type detection (Desktop, Mobile, Tablet)\n- ### Token Revocation\n  - Adds Access Token to blacklist upon logout\n  - Redis TTL automatically cleans expired revocation records\n  - Prevents reuse of logged-out tokens\n- ### Version Control Protection\n  - Refresh Token version tracking\n  - Auto-generates new Refresh ID after 5 refresh attempts\n  - Prevents replay attacks\n- ### Smart Refresh Strategy\n  - Auto-regenerates when Refresh Token has less than half lifetime remaining\n  - 5-second grace period for old tokens to reduce concurrency issues\n  - Minimizes database queries\n- ### Multiple Authentication Methods\n  - Automatic cookie reading\n  - Authorization Bearer Header\n  - Custom Headers (X-Device-ID, X-Refresh-ID)\n- ### Flexible Configuration\n  - Supports file paths or direct key content\n  - Customizable Cookie names\n  - Production/Development environment auto-switching\n\n## How to use\n\n- ### Installation\n  ```bash\n  npm install @pardnchiu/jwt-auth\n  ```\n- ### Initialize\n  ```typescript\n  import { JWTAuth } from '@pardnchiu/jwt-auth';\n\n  // initialize the JWT instance\n  await JWTAuth.init({\n    privateKeyPath: \"./keys/private.pem\",\n    publicKeyPath: \"./keys/public.pem\",\n    // or paste keys directly:\n    // privateKey: \"-----BEGIN EC PRIVATE KEY-----...\",\n    // publicKey: \"-----BEGIN PUBLIC KEY-----...\",\n    accessTokenExpires: 900, // seconds\n    refreshTokenExpires: 604800, // seconds\n    // true: domain=domain, samesite=none, secure=true\n    // false: domain=localhost, samesite=lax, secure=false\n    isProd: false,\n    domain: \"pardn.io\",\n    // cookie key, default access_token/refresh_id\n    AccessTokenCookieKey: \"access_token\",\n    RefreshTokenCookieKey: \"refresh_id\",\n    // store with redis\n    redis: {\n      host: \"localhost\",\n      port: 6379,\n      password: \"\", // optional\n      db: 0 // optional\n    },\n    checkUserExists: async (userId: string): Promise\u003cboolean\u003e =\u003e {\n      // return true if user exists, false otherwise\n      return true;\n    }\n  });\n\n  process.on(\"SIGINT\", async () =\u003e {\n    await JWTAuth.close();\n    process.exit(0);\n  });\n  ```\n- ### CreateJWT\n  ```typescript\n  import { Request, Response } from 'express';\n  import { JWTAuth } from '@pardnchiu/jwt-auth';\n\n  async function loginHandler(req: Request, res: Response) {\n    // after verifying user login info...\n    \n    const userData = {\n      id: \"user123\",\n      name: \"\",\n      email: \"john@example.com\",\n      thumbnail: \"avatar.jpg\",\n      role: \"user\",\n      level: 1,\n      scope: [\"read\", \"write\"]\n    };\n\n    try {\n      const tokenResult = await JWTAuth.CreateJWT(req, res, userData);\n      \n      // automatically set in cookies\n      res.json({\n        success: true,\n        token: tokenResult.token,\n        refresh_id: tokenResult.refresh_id\n      });\n    } catch (error) {\n      res.status(500).json({ error: error.message });\n    }\n  }\n  ```\n- ### VerifyJWT\n  ```typescript\n  import { Request, Response } from 'express';\n  import { JWTAuth } from '@pardnchiu/jwt-auth';\n\n  async function protectedHandler(req: Request, res: Response) {\n    try {\n      const result = await JWTAuth.VerifyJWT(req, res);\n      \n      if (!result.isAuth) {\n        // Authentication failed\n        return res.status(result).json({ \n          error: result.isError ? \"Bad Request\" : \"Unauthorized\" \n        });\n      }\n      \n      // Authentication success, user result.data to get user data\n      res.json({\n        message: \"Protected resource accessed\",\n        user: result.data\n      });\n    } catch (error) {\n      res.status(500).json({ error: error.message });\n    }\n  }\n  ```\n- ### RevokeJWT\n  ```typescript\n  import { Request, Response } from \"express\";\n  import { JWTAuth } from \"@pardnchiu/jwt-auth\";\n\n  async function logoutHandler(req: Request, res: Response) {\n    try {\n      await JWTAuth.RevokeJWT(req, res);\n      \n      res.json({\n        message: \"Successfully logged out\"\n      });\n    } catch (error) {\n      res.status(500).json({ error: error.message });\n    }\n  }\n  ```\n\n## Configuration\n\n### Config\n- `privateKeyPath` / `privateKey`: private key file path or content\n- `publicKeyPath` / `publicKey`: public key file path or content  \n- `accessTokenExpires`: access token expire time\n- `refreshTokenExpires`: refresh id expire time\n- `isProd`: is production or not (affects cookie setting)\n- `domain`: cookie domain\n- `redis`: redis connection\n  - `host`: redis host\n  - `port`: redis port\n  - `password`: redis password (optional)\n  - `db`: redis db (optional)\n- `checkUserExists`: user existence check function\n- `AccessTokenCookieKey`: access token cookie name (default: 'access_token')\n- `RefreshTokenCookieKey`: refresh id cookie name (default: 'refresh_id')\n\n### Supported methods\n\n1. **Cookie**: Automatically reads token from cookie\n2. **Authorization Header**: `Authorization: Bearer \u003ctoken\u003e`\n3. **Custom Headers**: \n   - `X-Device-ID`: Device ID\n   - `X-Refresh-ID`: Custom Refresh ID\n\n## Token refresh\n\nThe system automatically generates a new Refresh ID in the following cases:\n- Refresh version exceeds 5 times\n- Remaining Refresh Token time is less than half\n\nThe new tokens are returned via:\n- HTTP Header: `X-New-Access-Token`\n- HTTP Header: `X-New-Refresh-ID`\n- Cookie auto-update\n\n## Security features\n\n- **Fingerprint recognition**: Generates a unique fingerprint based on User-Agent, Device-ID, OS, Browser, and Device type\n- **Token revocation**: Adds token to a blacklist on logout\n- **Automatic expiration**: Supports TTL to automatically clean up expired tokens\n- **Version control**: Tracks Refresh Token versions to prevent replay attacks\n- **Fingerprint validation**: Ensures tokens are used from the same device/browser\n\n## Error handling\n\nThe `VerifyJWT` method returns:\n- `AuthData` object on successful authentication\n- HTTP status code number on failure:\n  - `401`: Unauthorized (invalid/expired tokens, user doesn't exist)\n  - `400`: Bad Request (invalid fingerprint, malformed tokens)\n\nCommon error scenarios:\n- Token revoked\n- Fingerprint mismatch\n- Refresh data not found\n- JWT expired or invalid\n- User not found\n\n## License\n\nThis source code project is licensed under the [MIT](https://github.com/pardnchiu/nodejs-jwt-auth/blob/main/LICENSE) license.\n\n## Creator\n\n\u003cimg src=\"https://avatars.githubusercontent.com/u/25631760\" align=\"left\" width=\"96\" height=\"96\" style=\"margin-right: 0.5rem;\"\u003e\n\n\u003ch4 style=\"padding-top: 0\"\u003e邱敬幃 Pardn Chiu\u003c/h4\u003e\n\n\u003ca href=\"mailto:dev@pardn.io\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://pardn.io/image/email.svg\" width=\"48\" height=\"48\"\u003e\n\u003c/a\u003e \u003ca href=\"https://linkedin.com/in/pardnchiu\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://pardn.io/image/linkedin.svg\" width=\"48\" height=\"48\"\u003e\n\u003c/a\u003e\n\n***\n\n©️ 2025 [邱敬幃 Pardn Chiu](https://pardn.io)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpardnio%2Fnode-jwt-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpardnio%2Fnode-jwt-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpardnio%2Fnode-jwt-auth/lists"}