Class: Ask::Auth::Providers::OAuth

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/auth/providers/oauth.rb

Overview

PKCE OAuth flow for interactive credential authorization.

This provider implements the OAuth interface. The full interactive flow can be deferred — the interface is wired for integration and the PKCE utility methods are ready.

provider = Ask::Auth::Providers::OAuth.new(storage: database_provider)
url = provider.authorize_url(user: current_user)
# redirect user to url, then:
provider.authorize!(user: current_user, code: params[:code])

Instance Method Summary collapse

Constructor Details

#initialize(storage: nil, client_id: nil, authorize_url: nil, token_url: nil) ⇒ OAuth

Returns a new instance of OAuth.



21
22
23
24
25
26
# File 'lib/ask/auth/providers/oauth.rb', line 21

def initialize(storage: nil, client_id: nil, authorize_url: nil, token_url: nil)
  @storage = storage
  @client_id = client_id
  @authorize_url = authorize_url
  @token_url = token_url
end

Instance Method Details

#authorize!(user:, code:) ⇒ Object

Exchange an authorization code for tokens.

user

The user to associate the credential with

code

The authorization code from the redirect

Raises:

  • (NotImplementedError)


73
74
75
# File 'lib/ask/auth/providers/oauth.rb', line 73

def authorize!(user:, code:)
  raise NotImplementedError, "Token exchange requires a configured token_url and client_id"
end

#authorize_url(user:, verifier: nil, state: nil) ⇒ Object

Returns the authorization URL to redirect the user to.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ask/auth/providers/oauth.rb', line 47

def authorize_url(user:, verifier: nil, state: nil)
  verifier ||= generate_code_verifier
  state ||= SecureRandom.hex(16)
  challenge = generate_code_challenge(verifier)

  # Store state and verifier for this user (requires a storage provider)
  if @storage && user
    @storage.call(:oauth_state, user: user)
  end

  uri = URI.parse(@authorize_url || "https://example.com/oauth/authorize")
  uri.query = URI.encode_www_form(
    response_type: "code",
    client_id: @client_id || "YOUR_CLIENT_ID",
    redirect_uri: "urn:ietf:wg:oauth:2.0:oob",
    scope: "",
    state: state,
    code_challenge: challenge,
    code_challenge_method: "S256"
  )
  uri.to_s
end

#call(name, user: nil) ⇒ Object

Returns nil (no automatic resolution) — OAuth requires interactive flow.



29
30
31
# File 'lib/ask/auth/providers/oauth.rb', line 29

def call(name, user: nil)
  nil
end

#generate_code_challenge(verifier) ⇒ Object

Generate a PKCE code challenge (SHA256 base64 digest of verifier).



39
40
41
42
43
44
# File 'lib/ask/auth/providers/oauth.rb', line 39

def generate_code_challenge(verifier)
  ::Base64.urlsafe_encode64(
    OpenSSL::Digest.digest("SHA256", verifier),
    padding: false
  )
end

#generate_code_verifierObject

Generate a PKCE code verifier (128-char alphanumeric string).



34
35
36
# File 'lib/ask/auth/providers/oauth.rb', line 34

def generate_code_verifier
  SecureRandom.alphanumeric(128)
end