{"id":23359786,"url":"https://github.com/firehed/jwt","last_synced_at":"2025-07-13T05:38:09.417Z","repository":{"id":30144869,"uuid":"33695030","full_name":"Firehed/jwt","owner":"Firehed","description":"JSON Web Tokens for PHP","archived":false,"fork":false,"pushed_at":"2024-07-19T23:02:08.000Z","size":112,"stargazers_count":4,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T09:21:11.024Z","etag":null,"topics":["jwt","php","php-jwt","php-security","security"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Firehed.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2015-04-09T22:03:04.000Z","updated_at":"2024-07-19T22:13:29.000Z","dependencies_parsed_at":"2024-12-21T11:12:02.322Z","dependency_job_id":null,"html_url":"https://github.com/Firehed/jwt","commit_stats":{"total_commits":44,"total_committers":3,"mean_commits":"14.666666666666666","dds":"0.15909090909090906","last_synced_commit":"67d7436239ae95911e90e029d7472ae05ea17559"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Firehed%2Fjwt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Firehed%2Fjwt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Firehed%2Fjwt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Firehed%2Fjwt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Firehed","download_url":"https://codeload.github.com/Firehed/jwt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248199136,"owners_count":21063641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["jwt","php","php-jwt","php-security","security"],"created_at":"2024-12-21T11:11:57.987Z","updated_at":"2025-04-10T10:32:05.619Z","avatar_url":"https://github.com/Firehed.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JWT - JSON Web Tokens\n\n[![Tests](https://github.com/Firehed/jwt/actions/workflows/test.yml/badge.svg)](https://github.com/Firehed/jwt/actions/workflows/test)\n[![codecov](https://codecov.io/gh/Firehed/jwt/branch/master/graph/badge.svg)](https://codecov.io/gh/Firehed/jwt)\n\n## Installation\n`composer require firehed/jwt`\n\n## Usage\nBasic encoding example:\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\nuse Firehed\\JWT;\nuse Firehed\\Security\\Secret;\n\n$keys = new JWT\\KeyContainer();\n$keys-\u003eaddKey(1, JWT\\Algorithm::HMAC_SHA_256, new Secret('some secret key'));\n\n$data = [\n\t'some' =\u003e 'data',\n\t'that' =\u003e 'you want to encode',\n];\n$token = new JWT($data);\n$token-\u003esetKeys($keys);\n$jwt_string = $token-\u003egetEncoded();\n```\n\nBasic decoding example:\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nuse Firehed\\JWT;\nuse Firehed\\Security\\Secret;\n\n$keys = new JWT\\KeyContainer();\n$keys-\u003eaddKey(1, JWT\\Algorithm::HMAC_SHA_256, new Secret('some secret key'));\n\n\n$jwt_string = 'some.jwt.string';\n$token = JWT::fromEncoded($jwt_string, $keys);\n$data = $token-\u003egetClaims();\n```\n\n## Security: Signing Keys\n\n### Generation\n\nThe HMAC-SHA family of algorithms supports a key length of up to 512 bits (64 bytes). It is recommended to use the longest supported key.\n\nIf on PHP\u003e=7, use `random_bytes()`: `$secret = random_bytes(64)`;\n\nSince these probably contain binary data, it's best to store them base64-encoded:\n\n```\n$secret = random_bytes(64);\n$encoded = base64_encode($secret);\n```\n\nYour configuration file, explained below, should `base64_decode` the encoded string before returning it\n\n### IDs and rotation\n\nIt is **highly recommended** to regularly rotate your signing keys, and the JWT spec makes this easy to handle thanks to the `kid` header. Encoded output will always include the key id used to sign the token, and that value will automatically be used during decoding.\n\nIn your application config, have multiple keys and their IDs defined:\n\n```php\n$keys = new KeyContainer();\n$keys-\u003eaddKey('20160101',\n              Algorithm::HMAC_SHA_256,\n              new Secret(base64_decode('string+generated/earlier')))\n     -\u003eaddKey('20160201',\n              Algorithm::HMAC_SHA_256,\n              new Secret(base64_decode('other+string/generated')));\n```\n\nSimply adding additional keys to the container should more-or-less automatically handle key rotation for all new tokens, but your application may behave in a different way that doesn't ensure this is the case.\n\nBy default, the `KeyContainer` will use the most recently added key if one is not explicitly requested. You may override this by explicitly setting a default key:\n\n`$keys-\u003esetDefaultKey('20160101');`\n\nNote: key ID can take any scalar format. The example above uses a datestamp, but sequential integers are also fine. It is recommended to use something semantically meaningful to the application, but not in any way meaningful to the end-user.\n\n## Security: Exception Handling\n\nWhen calling `getClaims()`, an exception may be thrown if the signature cannot be verified or the time validity specified in standard `nbf` or `exp` claims is out of line.\n\nBe prepared to catch `InvalidSignatureException`, `TokenExpiredException`, and `TokenNotYetValidException` when calling those methods.\n\nIf an invalid token is passed to `JWT::fromEncoded()`, an `InvalidFormatException` will be thrown.\n\nException tree:\n\n```\nException\n |--Firehed\\JWT\\JWTException\n     |--Firehed\\JWT\\InvalidFormatException\n     |--Firehed\\JWT\\InvalidSignatureException\n     |--Firehed\\JWT\\TokenExpiredException\n     |--Firehed\\JWT\\TokenNotYetValidException\n```\n\n## Algorithm Support\n\nAs of v2.0.0, the following algorithms are supported:\n\n* `none`\n* `HS256` (HMAC-SHA256)\n* `HS384` (HMAC-SHA384)\n* `HS512` (HMAC-SHA512)\n\nBecause the `none` algorithm is inherently insecure, the encoded data may only be accessed with the `getUnverifiedClaims()` API call. This is to call explicit attention to the fact that the data cannot be trusted. It is strongly recommended to never use the `none` algorithm.\n\nThe algorithm in the header is intentionally ignored during verification to avoid [algorithm-swapping attacks](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). This library instead uses the `kid` (Key ID) header, matching the value to the keys available in the `KeyContainer`. Asymmetric keys are not supported at this time.\n\n## Sessions\n\nBecause JWTs are cryptographically signed, it's now both possible and practical to keep basic session handling completely client-side, removing the dependency on a database connection or the filesystem (and the complexity of scaling that to multiple servers). A class implementing PHP's `SessionHandlerInterface` is included to make this easier.\n\nGenerally speaking, storing session data other than identifiers client-side *is a bad decision*. It's useful for quick prototying, or in extremely resource-constrained environments.\n\nThere are some very important considerations:\n\n1. The JWT session cookie will use the values from `session_get_cookie_params`. **PHP HAS INSECURE DEFAULT VALUES FOR THESE**, and you must reconfigure them with `session_set_cookie_params` to ensure the `secure` and `httponly` cookie flags are set. This is not done automatically nor enforced to make local testing easier.\n\n2. The session MUST NOT contain sensitive information. JWTs *are not encrypted*, just encoded and signed. You must be OK with any data in the session being visible to the user.\n\n3. Because this uses only cookies for storage, there is very limited space available (~4096b for all cookies) and all future network requests will incur the overhead. This makes basic authentication info (e.g. `$_SESSION['user_id'] = 12345;`) and state management practical, but a poor choice if your sessions contain a lot of data or you use several other cookies.\n\n4. Because the data is stored entirely client-side, it will be largely impractical to build functionality like \"log me out everywhere\" if used to store authentication data.\n\n5. Like any other session management, the whole thing is pointless if not done over HTTPS.\n\n6. The concept of a session ID is largely ignored\n\nWhenever possible, the `SessionHandler` class attempts to use existing PHP configuration for session handling to be a drop-in replacement.\n\nThat out of the way, here's how it's done:\n\n### Session Example\n\n```php\n\u003c?php\nini_set('session.use_cookies', 0); // Without this, PHP will also send a PHPSESSID cookie, which we neither need nor care about\nsession_set_cookie_params(\n\t$lifetime = 0,\n\t$path = '/',\n\t$domain = '',\n\t$secure = true, // \u003c-- Very important\n\t$httponly = true // \u003c-- Very important\n);\n\nrequire 'vendor/autoload.php';\n\nuse Firehed\\JWT;\nuse Firehed\\Security\\Secret;\n\n$keys = new JWT\\KeyContainer();\n$keys-\u003eaddKey(1, JWT\\Algorithm::HMAC_SHA_256, new Secret('some secret key'));\n\n$handler = new Firehed\\JWT\\SessionHandler($keys);\n\nsession_set_save_handler($handler);\n\ntry {\n    session_start();\n    $_SESSION['user_id'] = 12345;\n} catch (Firehed\\JWT\\InvalidSignatureException $e) {\n\t// The session cookie was tampered with and the signature is invalid\n    // You should log this and investigate\n    session_destroy();\n}\n```\n\n`SessionHandler` will always use the default value from the `KeyContainer`. That means the most recently key will be used unless one was specified with `-\u003esetDefaultKey()`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirehed%2Fjwt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffirehed%2Fjwt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirehed%2Fjwt/lists"}