Class: LlmGateway::Clients::ClaudeCode::OAuthFlow
- Inherits:
-
Object
- Object
- LlmGateway::Clients::ClaudeCode::OAuthFlow
- Defined in:
- lib/llm_gateway/clients/claude_code/oauth_flow.rb
Constant Summary collapse
- CLIENT_ID =
"9d1c250a-e61b-44d9-88ed-5944d1962f5e"- TOKEN_URL =
"https://api.anthropic.com/v1/oauth/token"- AUTH_URL =
"https://claude.ai/oauth/authorize"- REDIRECT_URI =
"https://console.anthropic.com/oauth/code/callback"- DEFAULT_SCOPES =
"org:create_api_key user:profile user:inference"
Instance Attribute Summary collapse
-
#client_id ⇒ Object
readonly
Returns the value of attribute client_id.
-
#redirect_uri ⇒ Object
readonly
Returns the value of attribute redirect_uri.
-
#scopes ⇒ Object
readonly
Returns the value of attribute scopes.
Instance Method Summary collapse
-
#exchange_code(auth_code_or_callback, code_verifier, state: nil) ⇒ Object
Step 2: Exchange the authorization code for tokens.
-
#initialize(client_id: CLIENT_ID, redirect_uri: REDIRECT_URI, scopes: DEFAULT_SCOPES) ⇒ OAuthFlow
constructor
A new instance of OAuthFlow.
- #parse_callback(callback_url) ⇒ Object
-
#start(state: SecureRandom.hex(16)) ⇒ Object
Step 1: Generate the authorization URL for the user to visit.
Constructor Details
#initialize(client_id: CLIENT_ID, redirect_uri: REDIRECT_URI, scopes: DEFAULT_SCOPES) ⇒ OAuthFlow
Returns a new instance of OAuthFlow.
23 24 25 26 27 28 29 30 31 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 23 def initialize( client_id: CLIENT_ID, redirect_uri: REDIRECT_URI, scopes: DEFAULT_SCOPES ) @client_id = client_id @redirect_uri = redirect_uri @scopes = scopes end |
Instance Attribute Details
#client_id ⇒ Object (readonly)
Returns the value of attribute client_id.
21 22 23 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 21 def client_id @client_id end |
#redirect_uri ⇒ Object (readonly)
Returns the value of attribute redirect_uri.
21 22 23 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 21 def redirect_uri @redirect_uri end |
#scopes ⇒ Object (readonly)
Returns the value of attribute scopes.
21 22 23 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 21 def scopes @scopes end |
Instance Method Details
#exchange_code(auth_code_or_callback, code_verifier, state: nil) ⇒ Object
Step 2: Exchange the authorization code for tokens. Accepts one of:
-
“code#state” (legacy format)
-
a raw authorization code, with state passed separately
-
a full callback URL containing ?code=…&state=…
Returns { access_token:, refresh_token:, expires_at: }
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 53 def exchange_code(auth_code_or_callback, code_verifier, state: nil) code, resolved_state = extract_code_and_state(auth_code_or_callback, state) uri = URI(TOKEN_URL) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.read_timeout = 30 http.open_timeout = 10 request = Net::HTTP::Post.new(uri) request["Content-Type"] = "application/json" request.body = { grant_type: "authorization_code", client_id: @client_id, code: code, state: resolved_state || "", redirect_uri: @redirect_uri, code_verifier: code_verifier }.to_json response = http.request(request) if response.code.to_i == 200 data = JSON.parse(response.body) expires_at = if data["expires_in"] Time.now + data["expires_in"].to_i elsif data["expires_at"] Time.parse(data["expires_at"]) end { access_token: data["access_token"], refresh_token: data["refresh_token"], expires_at: expires_at } else error_body = begin JSON.parse(response.body) rescue StandardError {} end raise Errors::AuthenticationError.new( "OAuth token exchange failed: #{error_body["error_description"] || error_body["error"] || response.body}", error_body["error"] ) end end |
#parse_callback(callback_url) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 103 def parse_callback(callback_url) uri = URI(callback_url) code = uri.query && URI.decode_www_form(uri.query).to_h["code"] state = uri.query && URI.decode_www_form(uri.query).to_h["state"] raise ArgumentError, "Callback URL is missing code parameter" if code.nil? || code.empty? { code: code, state: state } rescue URI::InvalidURIError => e raise ArgumentError, "Invalid callback URL: #{e.}" end |
#start(state: SecureRandom.hex(16)) ⇒ Object
Step 1: Generate the authorization URL for the user to visit. Returns a hash with everything needed to complete the flow later.
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/llm_gateway/clients/claude_code/oauth_flow.rb', line 35 def start(state: SecureRandom.hex(16)) code_verifier, code_challenge = generate_pkce auth_url = (code_challenge, state) { authorization_url: auth_url, code_verifier: code_verifier, state: state } end |