fix: jwt generator (#34908)

The old JWT generator code provided the anon and authenticated claims as strings. The format of this string is different from the format determined by KJUR if an object is provided (I'm guessing probably a whitespace or quote character thing, but I didn't dive down this rabbit hole.) This leads to incompatibilities when the token is used.

To prevent this, the tokens are now defined as objects and KJUR's own serialization is used.
This commit is contained in:
Charis authored and GitHub committed 2025-04-18 12:30:07 -04:00
1 parent c5e0e89e80
commit 2f80b9b3b6
1 file changed
+32 -17
@@ -6,23 +6,19 @@ const JWT_HEADER = { alg: 'HS256', typ: 'JWT' }
const now = new Date()
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const fiveYears = new Date(now.getFullYear() + 5, now.getMonth(), now.getDate())
const anonToken = `
{
"role": "anon",
"iss": "supabase",
"iat": ${Math.floor(today.valueOf() / 1000)},
"exp": ${Math.floor(fiveYears.valueOf() / 1000)}
}
`.trim()
const serviceToken = `
{
"role": "service_role",
"iss": "supabase",
"iat": ${Math.floor(today.valueOf() / 1000)},
"exp": ${Math.floor(fiveYears.valueOf() / 1000)}
const anonToken = {
role: 'anon',
iss: 'supabase',
iat: Math.floor(today.valueOf() / 1000),
exp: Math.floor(fiveYears.valueOf() / 1000),
}
const serviceToken = {
role: 'service_role',
iss: 'supabase',
iat: Math.floor(today.valueOf() / 1000),
exp: Math.floor(fiveYears.valueOf() / 1000),
}
`.trim()
const generateRandomString = (length: number) => {
const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
@@ -63,12 +59,28 @@ export default function JwtGenerator({}) {
const [jwtSecret, setJwtSecret] = useState(secret)
const [token, setToken] = useState(anonToken)
const [signedToken, setSignedToken] = useState('')
const [err, setErr] = useState<string>(null)
const handleKeySelection = (e: ChangeEvent<HTMLSelectElement>) => {
const val = e.target.value
if (val == 'service') setToken(serviceToken)
else setToken(anonToken)
}
const handleClaimsChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
try {
const newTok = JSON.parse(e.target.value)
setToken(newTok)
setErr(null)
} catch (err) {
const errMessage =
!!err && typeof err === 'object' && 'message' in err && typeof err.message === 'string'
? err.message
: ''
setErr('Not a valid JSON body' + (errMessage ? `: ${errMessage}` : ''))
}
}
const generate = () => {
const signedJWT = KJUR.jws.JWS.sign(null, JWT_HEADER, token, jwtSecret)
setSignedToken(signedJWT)
@@ -102,10 +114,13 @@ export default function JwtGenerator({}) {
type="text"
rows={6}
placeholder="A valid JWT Token"
value={token}
defaultValue={JSON.stringify(token, null, 2)}
style={{ fontFamily: 'monospace' }}
onChange={(e) => setToken(e.target.value)}
onChange={handleClaimsChange}
/>
{err && (
<span className="text-sm text-destructive-600">Input must be valid JSON. {err}</span>
)}
</div>
<Button type="primary" onClick={generate}>