Class: SimpleOAuth::OAuth2::PKCE

Inherits:
Object
  • Object
show all
Defined in:
lib/simple_oauth/oauth2/pkce.rb,
sig/simple_oauth/oauth2.rbs

Overview

A Proof Key for Code Exchange verifier and challenge

Constant Summary collapse

S256 =

Challenge method that hashes the verifier with SHA-256

Returns:

  • (String)
"S256"
PLAIN =

Challenge method that sends the verifier itself

Returns:

  • (String)
"plain"
VERIFIER_PATTERN =

A valid verifier: 43 to 128 unreserved characters (RFC 7636 Section 4.1)

Returns:

  • (Regexp)
/\A[A-Za-z0-9\-._~]{43,128}\z/
INVALID_VERIFIER =

The error message for an invalid verifier

Returns:

  • (String)
"PKCE verifier must be 43 to 128 unreserved characters"
VERIFIER_BYTES =

Random bytes in a generated verifier, which encode to 64 characters

Returns:

  • (Integer)
48

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(verifier:, challenge_method: S256) ⇒ PKCE

Initialize from an existing verifier

Examples:

SimpleOAuth::OAuth2::PKCE.new(verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")

Parameters:

  • verifier (String)

    the code verifier

  • challenge_method (String) (defaults to: S256)

    the challenge method: S256 or plain

  • verifier: (String)
  • challenge_method: (String) (defaults to: S256)

Raises:

  • (ArgumentError)

    if the verifier or challenge method is invalid



69
70
71
72
73
74
75
76
# File 'lib/simple_oauth/oauth2/pkce.rb', line 69

def initialize(verifier:, challenge_method: S256)
  raise ArgumentError, INVALID_VERIFIER unless VERIFIER_PATTERN.match?(verifier)

  @verifier = verifier
  @challenge_method = challenge_method
  @challenge = compute_challenge
  freeze
end

Instance Attribute Details

#challengeString (readonly)

The code challenge, sent with the authorization request

Examples:

pkce.challenge # => "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

Returns:

  • (String)

    the code challenge



48
49
50
# File 'lib/simple_oauth/oauth2/pkce.rb', line 48

def challenge
  @challenge
end

#challenge_methodString (readonly)

The challenge method: S256 or plain

Examples:

pkce.challenge_method # => "S256"

Returns:

  • (String)

    the challenge method



40
41
42
# File 'lib/simple_oauth/oauth2/pkce.rb', line 40

def challenge_method
  @challenge_method
end

#verifierString (readonly)

The code verifier, sent with the token request

Examples:

pkce.verifier # => "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Returns:

  • (String)

    the code verifier



32
33
34
# File 'lib/simple_oauth/oauth2/pkce.rb', line 32

def verifier
  @verifier
end

Class Method Details

.generate(challenge_method: S256) ⇒ PKCE

Generate a random verifier and its challenge

Examples:

SimpleOAuth::OAuth2::PKCE.generate

Parameters:

  • challenge_method (String) (defaults to: S256)

    the challenge method: S256 or plain

  • challenge_method: (String) (defaults to: S256)

Returns:

  • (PKCE)

    the verifier and challenge



57
58
59
# File 'lib/simple_oauth/oauth2/pkce.rb', line 57

def self.generate(challenge_method: S256)
  new(verifier: SecureRandom.urlsafe_base64(VERIFIER_BYTES), challenge_method:)
end

Instance Method Details

#base64_url(data) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Encodes data as base64url without padding (RFC 7636 Section 4.2)

Parameters:

  • data (String)

    the data to encode

Returns:

  • (String)

    the encoded data



98
99
100
101
# File 'lib/simple_oauth/oauth2/pkce.rb', line 98

def base64_url(data)
  # "m0" is Base64 with no line breaks, which base64url then re-spells
  [data].pack("m0").tr("+/", "-_").delete("=")
end

#compute_challengeString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Compute the challenge for the verifier with the challenge method

Returns:

  • (String)

    the code challenge

Raises:

  • (ArgumentError)

    if the challenge method is unknown



85
86
87
88
89
90
91
# File 'lib/simple_oauth/oauth2/pkce.rb', line 85

def compute_challenge
  case challenge_method
  when S256 then base64_url(OpenSSL::Digest.digest("SHA256", verifier))
  when PLAIN then verifier
  else raise ArgumentError, "Unknown PKCE challenge method: #{challenge_method}"
  end
end