{"id":"sec-dev-jwt-config-standard","audience":"developers","category":"security","track":"security","technique":"constraints","title":"Check token settings against your auth standard","free":false,"task":"Check this token config against your auth standard: RS256 only, access tokens expire in 15 minutes or less, refresh tokens in 30 days or less, signing keys come from the secret store, and the issuer and audience are both checked. List only the settings that break the standard, numbered, as 'setting: current -> required'. Plain text, under 70 words.","materialLabel":"Auth config (attached to your prompt automatically)","material":"// auth/tokens.config.js  (service: orders-api)\nmodule.exports = {\n  access: {\n    algorithms: [\"RS256\", \"HS256\", \"none\"],\n    expiresIn: \"24h\",\n    issuer: \"https://auth.example.com\",\n    audience: \"orders-api\",\n    verifyIssuer: true,\n    verifyAudience: false,\n  },\n  refresh: {\n    expiresIn: \"30d\",\n    rotateOnUse: true,\n  },\n  keys: {\n    privateKey: \"changeme-dev-key\",   // TODO load from vault\n    publicKeyUrl: \"https://auth.example.com/.well-known/jwks.json\",\n  },\n  cookie: {\n    httpOnly: true,\n    secure: true,\n    sameSite: \"lax\",\n  },\n};\n\n// Comment from the last PR: \"rotateOnUse added per security review, sameSite lax is fine for our flows.\"\n\nContext from the team channel:\n- orders-api is called by the web app and by the mobile app. Both get tokens from auth.example.com.\n- The 24h access token was set during a load test in August \"so the test scripts wouldn't have to refresh\". Nobody changed it back.\n- The secret store is already set up for orders-api (path: secret/orders-api/jwt-private-key). Two other services read their keys from it.\n- Mobile users complained in Q2 about being logged out too often, which is why refresh tokens last 30 days.","rules":[{"type":"numberedItems","count":4},{"type":"plainText"},{"type":"maxWords","max":70}],"mistakes":[{"title":"Grading by general best practice","why":"The AI flags sameSite lax or the 30-day refresh token, which your standard allows.","fix":"Paste your standard and say: \"Only flag what breaks it.\"","checks":["r1"]},{"title":"Missing a quiet setting","why":"The AI catches 'none' but skips verifyAudience: false.","fix":"List every rule, including \"issuer and audience both checked.\"","checks":["r0"]},{"title":"Fixes without values","why":"The AI says 'shorten the expiry' without the number.","fix":"Show the shape: \"setting: current -> required.\"","checks":["r2","numberedItems"]},{"title":"Trusting the AI to count words","why":"AI is bad at counting. Ask for \"under 70 words\" and it often lands well over.","fix":"Ask for less than the limit and cap each line: \"each line under 15 words, under 50 words total.\"","checks":["maxWords"]},{"title":"Letting markdown in","why":"AI adds bold and headings by default, and in the PR comment they show up as stray symbols.","fix":"Add: \"Plain text only, no bold, no headings.\"","checks":["plainText"]}],"promptHints":[{"label":"Gives the standard","patterns":["rs256","15 min","secret store","audience","standard"]},{"label":"Lists only failures","patterns":["only (the )?(settings|failures|ones)","break","violat"]},{"label":"Shows the line shape","patterns":["->","current","required"]},{"label":"Sets a word limit","patterns":["\\d+\\s*words","word (limit|count)"]},{"label":"Asks for plain text","patterns":["plain","no bold","no markdown","no formatting","no headings"]}],"hint":"The AI will flag anything it has opinions about. Give it your exact standard and ask only for the settings that break it, with current and required values.","rubricLabels":["Lists exactly these 4: algorithms (allows HS256 and none, must be RS256 only), access expiresIn 24h (must be 15 minutes or less), verifyAudience false (must be true), and the hardcoded privateKey (must come from the secret store)","Does not flag refresh expiresIn 30d, rotateOnUse, or the cookie settings","Each line shows the current value and the required value"]}