Module: Keycardai::OAuth::PKCE

Defined in:
lib/keycardai/oauth/pkce.rb

Overview

PKCE primitives (RFC 7636): generate a code verifier and derive its challenge. S256 is the default and the method public clients should use; plain is RFC-permitted but not recommended.

Defined Under Namespace

Classes: Pair

Constant Summary collapse

VERIFIER_CHARSET =
[*"A".."Z", *"a".."z", *"0".."9", "-", ".", "_", "~"].freeze
DEFAULT_VERIFIER_LENGTH =
128
METHODS =
%w[S256 plain].freeze

Class Method Summary collapse

Class Method Details

.generate_code_challenge(code_verifier, method: "S256") ⇒ String

Derive the challenge for a verifier (RFC 7636 §4.2).

Parameters:

  • code_verifier (String)
  • method ("S256", "plain") (defaults to: "S256")

Returns:

  • (String)


36
37
38
39
40
41
42
43
44
45
# File 'lib/keycardai/oauth/pkce.rb', line 36

def generate_code_challenge(code_verifier, method: "S256")
  case method
  when "S256"
    [OpenSSL::Digest.digest("SHA256", code_verifier)].pack("m0").tr("+/", "-_").delete("=")
  when "plain"
    code_verifier
  else
    raise ArgumentError, "unsupported code_challenge_method #{method.inspect}"
  end
end

.generate_code_verifier(length: DEFAULT_VERIFIER_LENGTH) ⇒ String

Generate a cryptographically random verifier (RFC 7636 §4.1).

Parameters:

  • length (Integer) (defaults to: DEFAULT_VERIFIER_LENGTH)

    43 to 128

Returns:

  • (String)

Raises:

  • (ArgumentError)


25
26
27
28
29
# File 'lib/keycardai/oauth/pkce.rb', line 25

def generate_code_verifier(length: DEFAULT_VERIFIER_LENGTH)
  raise ArgumentError, "verifier length must be between 43 and 128, got #{length}" unless (43..128).cover?(length)

  Array.new(length) { VERIFIER_CHARSET[SecureRandom.random_number(VERIFIER_CHARSET.length)] }.join
end

.generate_pair(length: DEFAULT_VERIFIER_LENGTH, method: "S256") ⇒ Pair

Generate a verifier and its challenge together.

Parameters:

  • length (Integer) (defaults to: DEFAULT_VERIFIER_LENGTH)

    verifier length, 43 to 128

  • method ("S256", "plain") (defaults to: "S256")

Returns:



52
53
54
55
56
57
58
59
# File 'lib/keycardai/oauth/pkce.rb', line 52

def generate_pair(length: DEFAULT_VERIFIER_LENGTH, method: "S256")
  code_verifier = generate_code_verifier(length: length)
  Pair.new(
    code_verifier: code_verifier,
    code_challenge: generate_code_challenge(code_verifier, method: method),
    code_challenge_method: method
  )
end