Class: StandardId::Oauth::AuthorizationCodeFlow

Inherits:
TokenGrantFlow show all
Defined in:
lib/standard_id/oauth/authorization_code_flow.rb

Instance Attribute Summary

Attributes inherited from TokenGrantFlow

#params, #request

Attributes inherited from BaseRequestFlow

#current_account, #params, #request

Instance Method Summary collapse

Methods inherited from TokenGrantFlow

#execute, extra_permitted_keys, #initialize

Methods inherited from BaseRequestFlow

expect_params, expected_params, extra_permitted_keys, #initialize, permit_params, permitted_params

Constructor Details

This class inherits a constructor from StandardId::Oauth::TokenGrantFlow

Instance Method Details

#authenticate!Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/standard_id/oauth/authorization_code_flow.rb', line 7

def authenticate!
  @client = StandardId::ClientApplication.find_by(client_id: params[:client_id])
  raise StandardId::InvalidClientError, "Client authentication failed" if @client.nil?

  # Confidential clients authenticate with a client secret. Public clients
  # (e.g. native/SPA/MCP clients per RFC 8252 / OAuth 2.1) cannot keep a
  # secret and authenticate via PKCE alone — they MUST NOT send one.
  if @client.confidential?
    @credential = validate_client_secret!(params[:client_id], params[:client_secret])
  elsif params[:client_secret].present?
    raise StandardId::InvalidClientError, "Public clients must not send a client_secret"
  end

  @authorization_code = find_authorization_code(params[:code])
  unless @authorization_code&.valid_for_client?(params[:client_id])
    raise StandardId::InvalidGrantError, "Invalid or expired authorization code"
  end

  if params[:redirect_uri].present? && @authorization_code.redirect_uri != params[:redirect_uri]
    raise StandardId::InvalidGrantError, "Redirect URI mismatch"
  end

  # Fail closed: a public client's only authentication factor is PKCE, so a
  # code minted without a code_challenge offers no client authentication at
  # all. (pkce_valid? returns true when code_challenge is blank, which is
  # safe for confidential clients but would be a bypass for public ones.)
  if @client.public? && @authorization_code.code_challenge.blank?
    raise StandardId::InvalidGrantError, "PKCE is required for public clients"
  end

  unless @authorization_code.pkce_valid?(params[:code_verifier])
    raise StandardId::InvalidGrantError, "Invalid PKCE code_verifier"
  end

  @authorization_code.mark_as_used!
  emit_code_consumed
end