Class: Seekmodo::Sdk::Pairing

Inherits:
Object
  • Object
show all
Defined in:
lib/seekmodo/sdk/pairing.rb

Constant Summary collapse

DEFAULT_JWKS_URL =
"https://seekmodo.com/.well-known/jwks.json"
KEYS_CACHE_KEY =
"numinix.seekmodo.pairing.jwks"
KEYS_CACHE_TTL_SECONDS =
86400
MAX_TOKEN_AGE_SECONDS =
600

Instance Method Summary collapse

Constructor Details

#initialize(connection, cache, jwks_url: DEFAULT_JWKS_URL, clock: nil) ⇒ Pairing

Returns a new instance of Pairing.



20
21
22
23
24
25
# File 'lib/seekmodo/sdk/pairing.rb', line 20

def initialize(connection, cache, jwks_url: DEFAULT_JWKS_URL, clock: nil)
  @connection = connection
  @cache = cache
  @jwks_url = jwks_url
  @clock = clock || -> { Time.now.to_i }
end

Instance Method Details

#refresh_keysObject



66
67
68
# File 'lib/seekmodo/sdk/pairing.rb', line 66

def refresh_keys
  fetch_keys
end

#verify_and_extract(jwt_token) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/seekmodo/sdk/pairing.rb', line 27

def verify_and_extract(jwt_token)
  header, payload, _sig = JWT.decode(jwt_token, nil, false)
  alg = header["alg"].to_s
  raise SeekmodoError, "Unsupported pairing JWT alg \"#{alg}\"; expected EdDSA." unless alg == "EdDSA"

  kid = header["kid"].to_s
  raise SeekmodoError, "Pairing JWT is missing kid header." if kid.empty?

  jwk = lookup_key(kid, refresh: false)
  jwk = lookup_key(kid, refresh: true) if jwk.nil?
  raise SeekmodoError, "No pairing JWKS key matches kid=\"#{kid}\"; rotation lag?" if jwk.nil?

  public_key_raw = base64url_decode(jwk["x"].to_s)
  raise SeekmodoError, "Pairing JWKS key has wrong byte length for Ed25519." unless public_key_raw.bytesize == 32

  public_key = OpenSSL::PKey.read({
    kty: "OKP",
    crv: "Ed25519",
    x: jwk["x"]
  }.to_json)

  JWT.decode(
    jwt_token,
    public_key,
    true,
    { algorithm: "EdDSA" }
  )

  now = @clock.call
  exp = payload["exp"].to_i
  iat = payload["iat"].to_i
  raise SeekmodoError, "Pairing JWT has expired." if exp > 0 && exp < now
  raise SeekmodoError, "Pairing JWT is older than the 10-minute replay window." if iat > 0 && now - iat > MAX_TOKEN_AGE_SECONDS

  payload
rescue JWT::DecodeError => e
  raise SeekmodoError, "Pairing JWT signature verification failed.", e.backtrace
end