-
Notifications
You must be signed in to change notification settings - Fork 1
Jwt
Ori Pekelman edited this page May 11, 2026
·
1 revision
HS256 JWT encode / verify / decode. Interop-tested against the
canonical jwt gem — produced tokens validate there, and tokens
that gem produces validate here.
- Algorithm: HS256 only. No RS256, ES256, none.
-
Header:
{"alg":"HS256","typ":"JWT"}is hard-coded; users who need custom headers should look elsewhere. -
Payload: must be a JSON string the user composes — see
Tep::Json. The library doesn't enforce the standard claim set (iss/aud/exp/...) and doesn't auto-check expiry; both are the application's responsibility.
secret = ENV.fetch("JWT_SECRET")
payload = Tep::Json.from_str_hash({"sub" => "alice", "exp" => "1700000000"})
token = Tep::Jwt.encode_hs256(payload, secret)Returns "<header>.<payload>.<sig>" base64url-encoded.
Two flavours: separate steps (verify, then decode) or one-shot.
ok = Tep::Jwt.verify_hs256(token, secret)
if ok
payload = Tep::Jwt.decode_payload(token)
sub = Tep::Json.get_str(payload, "sub")
endpayload = Tep::Jwt.verify_and_decode(token, secret)
if payload.length > 0
sub = Tep::Json.get_str(payload, "sub")
endverify_and_decode returns "" on signature mismatch — easier to
chain than the bool-then-decode form.
The signature comparison is constant-time. If you need the underlying primitive (e.g. for comparing API keys against a stored hash), the same helper is exposed:
Tep::Jwt.timing_safe_eq(actual, expected) # true / falsepost '/login' do
if user_password_ok(params[:user], params[:password])
h = Tep.str_hash
h["sub"] = params[:user]
h["exp"] = (Time.now.to_i + 3600).to_s
payload = Tep::Json.from_str_hash(h)
token = Tep::Jwt.encode_hs256(payload, JWT_SECRET)
content_type 'application/json'
Tep::Json.from_str_hash({"token" => token})
else
halt 401, "nope"
end
endbefore do
auth = request.headers["authorization"]
if !auth.start_with?("Bearer ")
halt 401, "missing bearer"
end
token = auth[7, auth.length - 7]
payload = Tep::Jwt.verify_and_decode(token, JWT_SECRET)
if payload.length == 0
halt 401, "invalid token"
end
exp = Tep::Json.get_int(payload, "exp")
if exp != 0 && exp < Time.now.to_i
halt 401, "expired"
end
request.ivars["sub"] = Tep::Json.get_str(payload, "sub")
end-
Don't reuse the session secret.
Tep.session_secretsigns cookies;JWT_SECRETsigns tokens. If you reuse the same secret, an attacker who steals a JWT can forge a session cookie (or vice versa). -
No automatic expiry. The encoder writes
expif you put it in the payload, butverify_hs256won't reject expired tokens — that's the app'sexp < Time.now.to_icheck. -
Header is fixed. A token with a non-HS256 algorithm fails to
verify, regardless of the header's
algclaim. (This is the correct behaviour against thealg=nonesubstitution attack; document the constraint when interoperating with other libraries.)