Module: CardDB::DeckTokens

Defined in:
lib/carddb/deck_tokens.rb

Overview

Server-side helpers for minting direct signed deck tokens.

Constant Summary collapse

TOKEN_USE =
'carddb.deck_token.v1'
AUDIENCE =
'carddb.deck'
ED25519_PKCS8_PREFIX =
[
  '302e020100300506032b657004220420'
].pack('H*').freeze

Class Method Summary collapse

Class Method Details

.generate_direct_key_pairHash{Symbol => String}

Generate an Ed25519 keypair for CardDB direct deck token signing.

Returns:

  • (Hash{Symbol => String})

    Includes a PEM private key and the base64-encoded raw public key



21
22
23
24
25
26
27
28
# File 'lib/carddb/deck_tokens.rb', line 21

def generate_direct_key_pair
  signing_key = Ed25519::SigningKey.generate

  {
    private_key_pem: seed_to_pem(signing_key.to_bytes),
    public_key_base64: Base64.strict_encode64(signing_key.verify_key.to_bytes)
  }
end

.sign_direct_token(**attributes) ⇒ String

Sign a direct deck token that CardDB can verify with the issuer public key.

Parameters:

  • issuer_id (String)
  • api_application_id (String)
  • key_id (String)
  • private_key (OpenSSL::PKey::PKey, String)

    Ed25519 private key as an OpenSSL key or PEM/DER string

  • deck_id (String, nil)
  • publisher_slug (String, nil)
  • game_key (String, nil)
  • external_subject (String, nil)
  • scopes (Array<String>)
  • representation (String, nil)
  • subject (String, nil)
  • token_id (String, nil)
  • issued_at (Time)
  • expires_at (Time)

Returns:

  • (String)

Raises:

  • (ArgumentError)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/carddb/deck_tokens.rb', line 48

def sign_direct_token(**attributes)
  attributes = validate_signing_input!(attributes)
  normalized_scopes = normalize_scopes!(attributes[:scopes])
  validate_token_target!(attributes)
  key = normalize_private_key!(attributes[:private_key])
  header = { alg: 'EdDSA', kid: attributes[:key_id].strip }
  payload = build_payload(attributes.merge(scopes: normalized_scopes))

  encoded_header = base64url_encode(JSON.generate(header))
  encoded_payload = base64url_encode(JSON.generate(payload))
  signing_input = "#{encoded_header}.#{encoded_payload}"
  signature = sign_with_private_key(key, signing_input)

  [encoded_header, encoded_payload, base64url_encode(signature)].join('.')
end