Class: Tep::Jwt
- Inherits:
-
Object
- Object
- Tep::Jwt
- Defined in:
- lib/tep/jwt.rb
Constant Summary collapse
- HEADER_B64U =
base64url-encoded ‘“alg”:“HS256”,“typ”:“JWT”`.
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
Class Method Summary collapse
-
.decode_payload(token) ⇒ Object
Pull the JSON-encoded payload back out of a token.
-
.encode_hs256(payload_json, secret) ⇒ Object
Build a token from a JSON-encoded payload string and the signing secret.
-
.timing_safe_eq(a, b) ⇒ Object
Constant-time string compare.
-
.verify_and_decode(token, secret) ⇒ Object
One-shot: verify, then decode.
-
.verify_hs256(token, secret) ⇒ Object
Verify the signature on a token.
Class Method Details
.decode_payload(token) ⇒ Object
Pull the JSON-encoded payload back out of a token. No signature verification – call ‘verify_hs256` first if you haven’t, OR use the wrapped ‘verify_and_decode` form.
82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/tep/jwt.rb', line 82 def self.decode_payload(token) d1 = Tep.str_find(token, ".", 0) if d1 < 0 return "" end d2 = Tep.str_find(token, ".", d1 + 1) if d2 < 0 return "" end payload_b64 = token[d1 + 1, d2 - d1 - 1] Crypto.sp_crypto_b64url_decode(payload_b64) end |
.encode_hs256(payload_json, secret) ⇒ Object
Build a token from a JSON-encoded payload string and the signing secret. Returns the three-segment ‘header.payload.sig` string.
55 56 57 58 59 60 |
# File 'lib/tep/jwt.rb', line 55 def self.encode_hs256(payload_json, secret) payload_b64 = Crypto.sp_crypto_b64url_encode(payload_json) signing_input = HEADER_B64U + "." + payload_b64 sig = Crypto.sp_crypto_hmac_sha256_b64url(secret, signing_input) signing_input + "." + sig end |
.timing_safe_eq(a, b) ⇒ Object
Constant-time string compare. Returns true iff strings are byte-identical. Used so a token-signature mismatch leaks no timing info about how many leading bytes matched.
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/tep/jwt.rb', line 108 def self.timing_safe_eq(a, b) if a.length != b.length return false end diff = 0 i = 0 n = a.length while i < n # getbyte(i), NOT bytes[i] -- the same O(n^2)-allocation + # GC-pressure hazard fixed in Tep::Session.timing_safe_eq # (allocation storm can free the FFI-returned signature local # mid-compare; #1052-family spinel rooting gap, see tep#157). diff = diff | (a.getbyte(i) ^ b.getbyte(i)) i += 1 end diff == 0 end |
.verify_and_decode(token, secret) ⇒ Object
One-shot: verify, then decode. Returns the JSON payload on success, “” on bad signature / malformed token. Saves callers an explicit early-return check.
98 99 100 101 102 103 |
# File 'lib/tep/jwt.rb', line 98 def self.verify_and_decode(token, secret) if !Jwt.verify_hs256(token, secret) return "" end Jwt.decode_payload(token) end |
.verify_hs256(token, secret) ⇒ Object
Verify the signature on a token. Returns true / false. Does NOT check claim semantics (exp / nbf / iss / aud).
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/tep/jwt.rb', line 64 def self.verify_hs256(token, secret) d1 = Tep.str_find(token, ".", 0) if d1 < 0 return false end d2 = Tep.str_find(token, ".", d1 + 1) if d2 < 0 return false end signing_input = token[0, d2] provided_sig = token[d2 + 1, token.length - d2 - 1] expected_sig = Crypto.sp_crypto_hmac_sha256_b64url(secret, signing_input) Jwt.timing_safe_eq(provided_sig, expected_sig) end |