Module: Basecamp::Oauth
- Defined in:
- lib/basecamp/oauth.rb,
lib/basecamp/oauth/pkce.rb,
lib/basecamp/oauth/token.rb,
lib/basecamp/oauth/config.rb,
lib/basecamp/oauth/fetcher.rb,
lib/basecamp/oauth/exchange.rb,
lib/basecamp/oauth/resource.rb,
lib/basecamp/oauth/discovery.rb,
lib/basecamp/oauth/oauth_error.rb,
lib/basecamp/oauth/refresh_request.rb,
lib/basecamp/oauth/discovery_result.rb,
lib/basecamp/oauth/exchange_request.rb,
lib/basecamp/oauth/discovery_selection_error.rb,
lib/basecamp/oauth/protected_resource_metadata.rb
Overview
OAuth 2 module for Basecamp SDK.
Provides OAuth discovery, token exchange, and token refresh functionality. Supports both standard OAuth 2 and Basecamp's Launchpad legacy format.
Defined Under Namespace
Modules: Fetcher, Pkce Classes: Config, Discovery, DiscoveryResult, DiscoverySelectionError, Exchange, ExchangeRequest, OauthError, ProtectedResourceMetadata, RefreshRequest, Resource, Token
Constant Summary collapse
- LAUNCHPAD_BASE_URL =
"https://launchpad.37signals.com"- FALLBACK_RESOURCE_DISCOVERY_FAILED =
Soft fallback reasons — the ONLY two outcomes under which discover_from_resource yields a fallback (Launchpad) rather than a selected config. Every other failure raises DiscoverySelectionError.
"resource_discovery_failed"- FALLBACK_NO_AS_ADVERTISED =
"no_as_advertised"
Class Method Summary collapse
-
.discover(base_url, timeout: 10) ⇒ Config
Discovers RFC 8414 Authorization Server Metadata and binds
issuertobase_urlby code-point. -
.discover_from_resource(resource_origin, expected_issuer: nil, timeout: 10) ⇒ DiscoveryResult
Resource-first discovery orchestrator (SPEC.md §16).
- .discover_launchpad(timeout: 10) ⇒ Object
-
.discover_protected_resource(resource_origin, timeout: 10) ⇒ ProtectedResourceMetadata
Discovers RFC 9728 protected-resource metadata for a resource origin.
- .exchange_code(token_endpoint:, code:, redirect_uri:, client_id:, client_secret: nil, code_verifier: nil, use_legacy_format: false, timeout: 30) ⇒ Object
- .refresh_token(token_endpoint:, refresh_token:, client_id: nil, client_secret: nil, use_legacy_format: false, timeout: 30) ⇒ Object
- .token_expired?(token, buffer_seconds = 60) ⇒ Boolean
Class Method Details
.discover(base_url, timeout: 10) ⇒ Config
Discovers RFC 8414 Authorization Server Metadata and binds issuer to
base_url by code-point.
61 62 63 |
# File 'lib/basecamp/oauth.rb', line 61 def self.discover(base_url, timeout: 10) Discovery.new(timeout: timeout).discover(base_url) end |
.discover_from_resource(resource_origin, expected_issuer: nil, timeout: 10) ⇒ DiscoveryResult
Resource-first discovery orchestrator (SPEC.md §16). Composes RFC 9728 (resource metadata) and RFC 8414 (AS metadata) and applies the stage-sensitive fallback state machine.
Returns a DiscoveryResult that is either selected (a bound AS config)
or a soft fallback whose reason is resource_discovery_failed or
no_as_advertised ONLY. Every hard failure raises
DiscoverySelectionError — callers MUST NOT convert a raise into a
Launchpad request.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/basecamp/oauth.rb', line 97 def self.discover_from_resource(resource_origin, expected_issuer: nil, timeout: 10) # Origin-root validation of the *caller's* input is a usage error — let it # propagate as-is (not a soft fallback). # Hop 1: resource metadata. Any failure here is soft (before selection). # Pass the RAW resource_origin so binding is code-point-exact against the # caller's identifier (SPEC.md §16); discover_protected_resource normalizes # only its fetch URL. A malformed caller origin raises UsageError → re-raised. resource = begin discover_protected_resource(resource_origin, timeout: timeout) rescue Basecamp::UsageError raise rescue OauthError nil end if resource.nil? DiscoveryResult.fallback(FALLBACK_RESOURCE_DISCOVERY_FAILED) else select_and_bind(resource. || [], expected_issuer, timeout) end end |
.discover_launchpad(timeout: 10) ⇒ Object
65 66 67 |
# File 'lib/basecamp/oauth.rb', line 65 def self.discover_launchpad(timeout: 10) discover(LAUNCHPAD_BASE_URL, timeout: timeout) end |
.discover_protected_resource(resource_origin, timeout: 10) ⇒ ProtectedResourceMetadata
Discovers RFC 9728 protected-resource metadata for a resource origin.
74 75 76 |
# File 'lib/basecamp/oauth.rb', line 74 def self.discover_protected_resource(resource_origin, timeout: 10) Resource.new(timeout: timeout).discover(resource_origin) end |
.exchange_code(token_endpoint:, code:, redirect_uri:, client_id:, client_secret: nil, code_verifier: nil, use_legacy_format: false, timeout: 30) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/basecamp/oauth.rb', line 119 def self.exchange_code( token_endpoint:, code:, redirect_uri:, client_id:, client_secret: nil, code_verifier: nil, use_legacy_format: false, timeout: 30 ) request = ExchangeRequest.new( token_endpoint: token_endpoint, code: code, redirect_uri: redirect_uri, client_id: client_id, client_secret: client_secret, code_verifier: code_verifier, use_legacy_format: use_legacy_format ) Exchange.new(timeout: timeout).exchange(request) end |
.refresh_token(token_endpoint:, refresh_token:, client_id: nil, client_secret: nil, use_legacy_format: false, timeout: 30) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/basecamp/oauth.rb', line 133 def self.refresh_token( token_endpoint:, refresh_token:, client_id: nil, client_secret: nil, use_legacy_format: false, timeout: 30 ) request = RefreshRequest.new( token_endpoint: token_endpoint, refresh_token: refresh_token, client_id: client_id, client_secret: client_secret, use_legacy_format: use_legacy_format ) Exchange.new(timeout: timeout).refresh(request) end |
.token_expired?(token, buffer_seconds = 60) ⇒ Boolean
146 147 148 |
# File 'lib/basecamp/oauth.rb', line 146 def self.token_expired?(token, buffer_seconds = 60) token.expired?(buffer_seconds) end |