Functional Weave
Code in Python

auth.access-token@1.0.0

vectors.json

18,587 bytes · view raw

[
  {"fn": "issueAccessToken", "name": "a one-hour token at noon expires at 13:00Z", "args": ["42", "Ada Lovelace", "ada@example.com", 0, "9f86d081884c7d65", 1790424000, 3600, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expect": {"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", "tokenType": "Bearer", "expiresAt": "2026-09-26T13:00:00Z", "expiresIn": 3600}},
  {"fn": "issueAccessToken", "name": "a 15-minute token for a user whose tokens were revoked three times", "args": ["7", "Zoë Ölander", "zoe@example.org", 3, "jti-2", 1790424000, 900, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expect": {"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InpvZUBleGFtcGxlLm9yZyIsImV4cCI6MTc5MDQyNDkwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiJqdGktMiIsIm5hbWUiOiJab8OrIMOWbGFuZGVyIiwic3ViIjoiNyIsInZlciI6M30.2TzwxRO7g32ZtSgfRmYGob6_2kQfafhrSHnjK0VbqHs", "tokenType": "Bearer", "expiresAt": "2026-09-26T12:15:00Z", "expiresIn": 900}},
  {"fn": "issueAccessToken", "name": "a one-second token issued at 23:59:59 expires at midnight", "args": ["42", "Ada Lovelace", "ada@example.com", 0, "late", 1790510399, 1, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expect": {"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDUxMDQwMCwiaWF0IjoxNzkwNTEwMzk5LCJqdGkiOiJsYXRlIiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.7MegvbhlVxpqr7Oey_Zswiz72BxmLFlnQcCru5AWxuE", "tokenType": "Bearer", "expiresAt": "2026-09-27T12:00:00Z", "expiresIn": 1}},
  {"fn": "issueAccessToken", "name": "an empty name and email are allowed; the epoch as now", "args": ["1", "", "", 0, "x", 0, 1, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expect": {"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IiIsImV4cCI6MSwiaWF0IjowLCJqdGkiOiJ4IiwibmFtZSI6IiIsInN1YiI6IjEiLCJ2ZXIiOjB9.iXhMTZBU3fhaUpqa0NrFRU6dUod71CLujxTCl4NzN0s", "tokenType": "Bearer", "expiresAt": "1970-01-01T00:00:01Z", "expiresIn": 1}},
  {"fn": "issueAccessToken", "name": "a year-long token", "args": ["42", "Ada", "ada@example.com", 0, "j", 1790424000, 31536000, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expect": {"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTgyMTk2MDAwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiJqIiwibmFtZSI6IkFkYSIsInN1YiI6IjQyIiwidmVyIjowfQ.u5gV7rQkembwwaeHFEYXbtWknBhxEPnLm2ofQ4CFnrA", "tokenType": "Bearer", "expiresAt": "2027-09-26T12:00:00Z", "expiresIn": 31536000}},
  {"fn": "issueAccessToken", "name": "an empty subject", "args": ["", "Ada", "a@b.co", 0, "j", 1790424000, 3600, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expectError": "subject must be a non-empty string"},
  {"fn": "issueAccessToken", "name": "an empty jti cannot be revoked alone", "args": ["42", "Ada", "a@b.co", 0, "", 1790424000, 3600, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expectError": "jti must be a non-empty string"},
  {"fn": "issueAccessToken", "name": "a zero lifetime", "args": ["42", "Ada", "a@b.co", 0, "j", 1790424000, 0, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expectError": "ttlSeconds must be a whole number of at least 1"},
  {"fn": "issueAccessToken", "name": "a negative token version", "args": ["42", "Ada", "a@b.co", -1, "j", 1790424000, 3600, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103]], "expectError": "tokenVersion must be a whole number, 0 or more"},
  {"fn": "issueAccessToken", "name": "a short secret", "args": ["42", "Ada", "a@b.co", 0, "j", 1790424000, 3600, [115, 104, 111, 114, 116]], "expectError": "secret must be at least 32 bytes"},
  {"fn": "readAccessToken", "name": "a token issueAccessToken made, half an hour in", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790425800, 30], "expect": {"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}},
  {"fn": "readAccessToken", "name": "the scheme is case-insensitive", "args": ["bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790425800, 30], "expect": {"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}},
  {"fn": "readAccessToken", "name": "no Authorization header", "args": [null, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "missing_token", "message": "the request has no Bearer token"}},
  {"fn": "readAccessToken", "name": "Basic credentials are not a Bearer token", "args": ["Basic dXNlcjpwYXNz", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "missing_token", "message": "the request has no Bearer token"}},
  {"fn": "readAccessToken", "name": "expired: at exp plus the leeway", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790427630, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_expired", "message": "the token has expired"}},
  {"fn": "readAccessToken", "name": "still accepted one second before exp plus the leeway", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790427629, 30], "expect": {"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}},
  {"fn": "readAccessToken", "name": "signed with another key", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkYUBleGFtcGxlLmNvbSIsImV4cCI6MTc5MDQyNzYwMCwiaWF0IjoxNzkwNDI0MDAwLCJqdGkiOiI5Zjg2ZDA4MTg4NGM3ZDY1IiwibmFtZSI6IkFkYSBMb3ZlbGFjZSIsInN1YiI6IjQyIiwidmVyIjowfQ.5UVROO8Ny4NUoEPQpTJpmrWcvOfVqjeK_wb0GjnBoaA", [97, 110, 111, 116, 104, 101, 114, 45, 115, 101, 99, 114, 101, 116, 45, 116, 104, 97, 116, 45, 105, 115, 45, 51, 50, 45, 98, 121, 116, 101, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "invalid_signature", "message": "the token's signature does not match"}},
  {"fn": "readAccessToken", "name": "not a JWT", "args": ["Bearer abc.def", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "malformed_token", "message": "the token is not a well-formed JWT"}},
  {"fn": "readAccessToken", "name": "a validly signed token without jti or ver is not one this API issued", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3OTA0MjQwNjAsInN1YiI6IjQyIn0.K2Az1-0K29sMjBQJlfg0S0S3Z49d3gE6l8u2QvkNUjM", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "invalid_claims", "message": "the token lacks the sub, jti, ver or exp this API issues"}},
  {"fn": "readAccessToken", "name": "a validly signed token with no exp would never expire, so it is refused", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJqIiwic3ViIjoiNDIiLCJ2ZXIiOjB9.hPnOOMcexeI2CIs8yxRegpJJl55Wg-JW0z09NyP6qrY", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "invalid_claims", "message": "the token lacks the sub, jti, ver or exp this API issues"}},
  {"fn": "readAccessToken", "name": "a numeric sub is not the string this API writes", "args": ["Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3OTA0MjQwNjAsImp0aSI6ImoiLCJzdWIiOjQyLCJ2ZXIiOjB9.9d1Ta3t-bbJRfPdDlNhUJQ2QpPN4LRcLLsjXDuKsq2A", [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, 30], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "invalid_claims", "message": "the token lacks the sub, jti, ver or exp this API issues"}},
  {"fn": "readAccessToken", "name": "a short secret is a configuration error even with no header", "args": [null, [115, 104, 111, 114, 116], 1790424000, 30], "expectError": "secret must be at least 32 bytes"},
  {"fn": "readAccessToken", "name": "negative leeway", "args": [null, [97, 45, 115, 116, 114, 105, 110, 103, 45, 115, 101, 99, 114, 101, 116, 45, 97, 116, 45, 108, 101, 97, 115, 116, 45, 50, 53, 54, 45, 98, 105, 116, 115, 45, 108, 111, 110, 103], 1790424000, -5], "expectError": "leewaySeconds must be a whole number, 0 or more"},
  {"fn": "confirmAccessToken", "name": "the user exists, the token version matches and the jti is not revoked", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, 0, false], "expect": {"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}},
  {"fn": "confirmAccessToken", "name": "logged out: the jti is on the revoked list", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, 0, true], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_revoked", "message": "the token has been revoked by logging out"}},
  {"fn": "confirmAccessToken", "name": "the password changed: the user's token version moved on", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, 1, false], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_revoked", "message": "the token was issued before the user's tokens were revoked (password changed)"}},
  {"fn": "confirmAccessToken", "name": "the user was deleted", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, null, false], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "user_not_found", "message": "the token's user no longer exists"}},
  {"fn": "confirmAccessToken", "name": "a check that already failed is passed through unchanged", "args": [{"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_expired", "message": "the token has expired"}, 0, false], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_expired", "message": "the token has expired"}},
  {"fn": "confirmAccessToken", "name": "a failed check stays failed even if not revoked and the version would match", "args": [{"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "missing_token", "message": "the request has no Bearer token"}, 0, false], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "missing_token", "message": "the request has no Bearer token"}},
  {"fn": "confirmAccessToken", "name": "version 3 matches version 3", "args": [{"ok": true, "subject": "7", "tokenVersion": 3, "jti": "jti-2", "expiresAt": "2026-09-26T12:15:00Z", "claims": {"sub": "7", "name": "Zoë Ölander", "email": "zoe@example.org", "ver": 3, "jti": "jti-2", "iat": 1790424000, "exp": 1790424900}, "error": null, "message": null}, 3, false], "expect": {"ok": true, "subject": "7", "tokenVersion": 3, "jti": "jti-2", "expiresAt": "2026-09-26T12:15:00Z", "claims": {"sub": "7", "name": "Zoë Ölander", "email": "zoe@example.org", "ver": 3, "jti": "jti-2", "iat": 1790424000, "exp": 1790424900}, "error": null, "message": null}},
  {"fn": "confirmAccessToken", "name": "a stale version is refused even when the database's version is lower", "args": [{"ok": true, "subject": "7", "tokenVersion": 3, "jti": "jti-2", "expiresAt": "2026-09-26T12:15:00Z", "claims": {"sub": "7", "name": "Zoë Ölander", "email": "zoe@example.org", "ver": 3, "jti": "jti-2", "iat": 1790424000, "exp": 1790424900}, "error": null, "message": null}, 2, false], "expect": {"ok": false, "subject": null, "tokenVersion": null, "jti": null, "expiresAt": null, "claims": null, "error": "token_revoked", "message": "the token was issued before the user's tokens were revoked (password changed)"}},
  {"fn": "confirmAccessToken", "name": "a fractional token version from the database", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, 0.5, false], "expectError": "currentTokenVersion must be a whole number or null"},
  {"fn": "confirmAccessToken", "name": "revoked must be a boolean", "args": [{"ok": true, "subject": "42", "tokenVersion": 0, "jti": "9f86d081884c7d65", "expiresAt": "2026-09-26T13:00:00Z", "claims": {"sub": "42", "name": "Ada Lovelace", "email": "ada@example.com", "ver": 0, "jti": "9f86d081884c7d65", "iat": 1790424000, "exp": 1790427600}, "error": null, "message": null}, 0, "no"], "expectError": "revoked must be true or false"}
]