{"id":14989867,"url":"https://github.com/boylesoftware/x2node-ws-auth-jwt","last_synced_at":"2026-01-19T18:01:33.137Z","repository":{"id":20108730,"uuid":"88375111","full_name":"boylesoftware/x2node-ws-auth-jwt","owner":"boylesoftware","description":"JWT authenticator implementation for X2 Framework's web services module.","archived":false,"fork":false,"pushed_at":"2022-02-11T05:07:15.000Z","size":120,"stargazers_count":2,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-17T05:25:36.393Z","etag":null,"topics":["auth0","authentication","authenticator","jsonwebtoken","jwt","jwt-auth","jwt-authentication","node-js","node-module","nodejs","nodejs-modules","x2node"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/boylesoftware.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}},"created_at":"2017-04-15T21:28:57.000Z","updated_at":"2019-11-10T15:48:03.000Z","dependencies_parsed_at":"2022-07-25T07:01:54.012Z","dependency_job_id":null,"html_url":"https://github.com/boylesoftware/x2node-ws-auth-jwt","commit_stats":null,"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/boylesoftware/x2node-ws-auth-jwt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boylesoftware%2Fx2node-ws-auth-jwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boylesoftware%2Fx2node-ws-auth-jwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boylesoftware%2Fx2node-ws-auth-jwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boylesoftware%2Fx2node-ws-auth-jwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boylesoftware","download_url":"https://codeload.github.com/boylesoftware/x2node-ws-auth-jwt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boylesoftware%2Fx2node-ws-auth-jwt/sbom","scorecard":{"id":249535,"data":{"date":"2025-08-11","repo":{"name":"github.com/boylesoftware/x2node-ws-auth-jwt","commit":"f90a99942167aebe48961abb72c5d60fa14afdce"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"16 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T08:12:02.074Z","repository_id":20108730,"created_at":"2025-08-17T08:12:02.074Z","updated_at":"2025-08-17T08:12:02.074Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28578952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T17:42:58.221Z","status":"ssl_error","status_checked_at":"2026-01-19T17:40:54.158Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["auth0","authentication","authenticator","jsonwebtoken","jwt","jwt-auth","jwt-authentication","node-js","node-module","nodejs","nodejs-modules","x2node"],"created_at":"2024-09-24T14:19:03.047Z","updated_at":"2026-01-19T18:01:33.118Z","avatar_url":"https://github.com/boylesoftware.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# X2 Framework for Node.js | Web Services JWT Authenticator\n\nJSON Web Token (JWT) authenticator implementation for X2 Framework's [x2node-ws](https://www.npmjs.com/package/x2node-ws) module. For the JWT specification, see [RFC 7519](https://tools.ietf.org/html/rfc7519).\n\nSee module's [API Reference Documentation](https://boylesoftware.github.io/x2node-api-reference/module-x2node-ws-auth-jwt.html).\n\n## Table of Contents\n\n* [Usage](#usage)\n* [Provider Examples](#provider-examples)\n  * [Auth0](#auth0)\n  * [Google Sign-In](#google-sign-in)\n\n## Usage\n\nTo create an authenticator the application needs to provide:\n\n* An actors registry (implementation of `ActorsRegistry` interface defined in `x2node-ws` module). Normally, the JWT's _subject_ (the \"sub\" claim) is used as the actor handle to lookup actors in the registry. A different claim can be used too (see authenticator constructor arguments).\n\n* A secret or public key (depending on the algorithm), used to verify the token JWS signature.\n\nThen, an authenticator can be added to a web service like the following:\n\n```javascript\nconst ws = require('x2node-ws');\nconst JWTAuthenticator = require('x2node-ws-auth-jwt');\n\nconst MY_CLIENT_ID = '...';\nconst MY_SECRET_KEY = '...';\n\nws.createApplication()\n    .addAuthenticator('/.*', new JWTAuthenticator(\n        new MyActorsRegistry(), // custom application component\n        new Buffer(MY_SECRET_KEY, 'base64'), {\n            aud: MY_CLIENT_ID\n        }\n    ))\n    ...\n    .run(port);\n```\n\nThe above installs the authenticator on all the web service endpoints.\n\nThe authenticator constructor takes the following arguments:\n\n* `actorsRegistry` - Implementation of the `ActorsRegistry` interface (see [x2node-ws](https://www.npmjs.com/package/x2node-ws) module). The authenticator extracts an actor handle (whatever the application uses to identify sign users in, which may be the user id, login name, email, etc.) and uses it to lookup  the actor record in the actors registry.\n\n* `secretOrKey` - Depending on the digital signature algorithm used by the provider that issues the JWTs, this is the public or the private key used to verify the signature. The key can be provided in one of the following forms:\n\n  * A _Node.js_ `Buffer` with the key data. Normally, it's a secret key used with HMAC algorithms.\n  * A string with the public key or certificate. Normally used with RSA and ECDSA algorithms.\n  * A function, which takes the decoded JWT object, normally includes `header`, `payload` and `signature` elements, and returns the key string, `Buffer` or a `Promise` of either. Rejection of the promise leads to an application error, resolving it with a \"falsy\" value (`false`, `null`, `undefined`, etc.) leaves the call unauthenticated.\n\n* `claimsTest` - An optional object with additional tests for the claims in the decoded token payload. The authenticator always automatically verifies \"nbf\" and \"exp\" claims, but the application can register additional tests. For example, it is almost always a good idea to include an \"iss\" and an \"aud\" claim test.\n\n  The object provided as `claimsTest` has properties for each additional claim test. The name of the property is the claim name (\"iss\", \"aud\", etc.). The value can be one of the following:\n\n  * A function, in which case it is passed the claim value as the first argument and the whole token payload object (aka claims set) as the second argument and returns either `true` if the claim is valid or `false` to deny authentication.\n  * A `RegExp`, in which case the claim value is tested against it.\n  * Anything else, in which case a simple equivalency test is used.\n\n  If the claim test is for \"aud\" claim and the claim value in the JWT is an array, the test will succeed if _any_ value in the array passes the test.\n\n* `actorHandleClaim` - Optional name of the claim to use as the actor handle for the actors registry. If not specified, \"sub\" claim is used.\n\nThe authenticator uses `X2_APP_AUTH` section for debug logging. Add it to `NODE_DEBUG` environment variable to see the authenticator's debug messages (see [Node.js API docs](https://nodejs.org/docs/latest-v4.x/api/util.html#util_util_debuglog_section) for details).\n\n## Provider Examples\n\nThe authenticator is written in a generic way to support JWT verification on the server side. Diffrerent authentication providers, however, use JWT in slightly different ways. Below are usage examples for some such providers.\n\n### Auth0\n\nWhen an [Auth0](https://auth0.com/) account is created for an application, it is issued a _Client ID_, which is used as the \"aud\" claim, and a _Client Secret_, which is the secret key for verification of JWT HMAC signatures. The \"sub\" claim can be normally used as the actor handle (note, that if Auth0's user database is used, the user id in the \"sub\" claim is prefixed with \"auth0|\"). Therefore, the JWT authenticator can be used as the following:\n\n```javascript\nconst MY_CLIENT_ID = '...';\nconst MY_CLIENT_SECRET = '...';\n\nws.createApplication()\n    .addAuthenticator('/.*', new JWTAuthenticator(\n        new MyActorsRegistry(),\n        new Buffer(MY_CLIENT_SECRET, 'base64'),\n        {\n            iss: 'https://myproject.auth0.com/',\n            aud: MY_CLIENT_ID\n        }\n    ))\n    ...\n```\n\nThe example above verifies \"iss\" and \"aud\" claims as well.\n\nThe above will work only with HMAC signatures. If you implement a Custom API and your clients authenticate with your API as the \"audience\", the tokens will be signed using RSA. In the case of RSA, the key is the certificate normally available in JWKS advertised via the \"jwks_uri\" property in the OpenID Connect discovery document. The `JWTAuthenticator` provides a special static factory function used to create the key provider that loads the keys from the JWKS. In the case of Auth0 it can be implemented like the this:\n\n```javascript\nconst MY_API_AUD = 'https://backend.myproject.com/api/';\n\nws.createApplication()\n    .addAuthenticator('/.*', new JWTAuthenticator(\n        new MyActorsRegistry(),\n        JWTAuthenticator.jwksKey('https://myproject.auth0.com/.well-known/jwks.json'),\n        {\n            iss: 'https://myproject.auth0.com/',\n            aud: MY_API_AUD\n        }\n    ))\n    ...\n```\n\nIn the example above the domain is \"myproject.auth0.com\" and the Custom API representing the server-side application is given the ID of \"https://backend.myproject.com/api/\". No keys are involved on the server side, they are loaded from the well-known URL for the JWKS.\n\n### Google Sign-In\n\nTo use [Google Sign-In](https://developers.google.com/identity/sign-in/web/), a project is created in Google API Console and then assigned a _Client ID_, which is used as the \"aud\" claim. Instead of a secret key, however, Google Sign-In uses Google public keys to verify JWT RSA signatures.\n\nUnfortunately, the JWKs provided by Google currently do not include \"x5c\" property required to verify the signature. Instead, Google provides current public keys at https://www.googleapis.com/oauth2/v1/certs.\n\nHere is an example of how it can be used:\n\n```javascript\nconst request = require('request');\n\nconst MY_CLIENT_ID = 'XXX.apps.googleusercontent.com';\n\nlet keysExpireAt = 0;\nlet keys;\n\nfunction getGooglePublicKeys(token) {\n\n    // check if the keys are still fresh\n    const now = Date.now();\n    if (now \u003c keysExpireAt)\n        return keys;\n\n    // give the get keys call 30 seconds to complete\n    keysExpireAt = now + 30000;\n\n    // do the call\n    return keys = new Promise((resolve, reject) =\u003e {\n        request.get(\n            {\n                url: 'https://www.googleapis.com/oauth2/v1/certs',\n                json: true\n            },\n            (err, res, data) =\u003e {\n\n                // reset expiration\n                keysExpireAt = 0;\n\n                // check if errors\n                if (err)\n                    return reject(err);\n                if (res.statusCode !== 200)\n                    return reject(`got ${res.statusCode} response`);\n\n                // update expiration from the response\n                keysExpireAt = (new Date(res.headers['expires'])).getTime();\n\n                // fulfill promise with the keys\n                resolve(data);\n        });\n    });\n}\n\nws.createApplication()\n    .addAuthenticator('/.*', new JWTAuthenticator(\n        new MyActorsRegistry(),\n        token =\u003e getGooglePublicKeys().then(\n            keys =\u003e keys[token.header \u0026\u0026 token.header.kid],\n            err =\u003e Promise.reject(err)\n        ),\n        {\n            iss: 'accounts.google.com',\n            aud: MY_CLIENT_ID,\n            hd: 'mydomain.com' // can be used to restrict G Suite logins\n        },\n        'email' // use \"email\" claim as the user handle\n    ))\n    ...\n```\n\nThe example above uses [request](https://www.npmjs.com/package/request) module to fetch the public certificates from Google. It also shows _very crude_ response caching logic.\n\nAlso, \"email\" claim is used as the user handle instead of the standard \"sub\".\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboylesoftware%2Fx2node-ws-auth-jwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboylesoftware%2Fx2node-ws-auth-jwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboylesoftware%2Fx2node-ws-auth-jwt/lists"}