Module: UnivapayClientSdk::AppJwt

Defined in:
lib/univapay_client_sdk/extensions.rb

Overview

── App token (JWT) claim decoding ────────────────────────────────────────

A UnivaPay app token JWT carries the context it was issued for. A store-level token has both merchant_id and store_id; a merchant-level token has only merchant_id.

Decoding only reads the payload segment -- it does NOT verify the signature, which is deliberate. The value is the caller's own credential, already trusted by virtue of being configured on the client; nothing here is an authorization decision. Never use these values to authenticate a third party's token.

Constant Summary collapse

UUID_PATTERN =

Matches the canonical 8-4-4-4-12 hexadecimal UUID form.

/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i.freeze

Class Method Summary collapse

Class Method Details

.decode_payload(jwt_token) ⇒ Hash?

Decodes the payload segment of a JWT without verifying its signature.

Parameters:

  • jwt_token (String, nil)

    The JWT to decode.

Returns:

  • (Hash, nil)

    The decoded claims, or nil unless the token is a well-formed three-segment JWT whose payload segment is base64url-encoded JSON describing an object.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/univapay_client_sdk/extensions.rb', line 148

def self.decode_payload(jwt_token)
  return nil if jwt_token.nil? || !jwt_token.is_a?(String) || jwt_token.empty?

  segments = jwt_token.split('.', -1)
  return nil unless segments.length == 3

  begin
    # unpack1('m0') is strict base64 and needs no `base64` gem, which stopped
    # being a default gem in Ruby 3.4. It requires correct padding, so
    # translate base64url to base64 and pad first.
    base64 = segments[1].tr('-_', '+/')
    base64 += '=' * ((4 - (base64.length % 4)) % 4)
    payload = JSON.parse(base64.unpack1('m0'))
  rescue ArgumentError, JSON::ParserError
    return nil
  end
  payload.is_a?(Hash) ? payload : nil
end

.read_uuid_claim(jwt_token, claim) ⇒ String?

Reads a claim from a JWT payload and returns it only if it is a UUID.

Anything else -- claim absent, nil, not a string, or a string that is not a canonical UUID -- yields nil, so a caller never has to distinguish "not set" from "could not decode".

Parameters:

  • jwt_token (String, nil)

    The JWT to decode.

  • claim (String)

    Name of the claim to read.

Returns:

  • (String, nil)

    The claim value as a UUID string, or nil.



176
177
178
179
180
181
182
# File 'lib/univapay_client_sdk/extensions.rb', line 176

def self.read_uuid_claim(jwt_token, claim)
  payload = decode_payload(jwt_token)
  return nil if payload.nil?

  value = payload[claim]
  value.is_a?(String) && UUID_PATTERN.match?(value) ? value : nil
end