@@ -631,6 +631,206 @@ access = client.auth_code.get_token("code_value", redirect_uri: "http://localhos
631631You can always use the ` #request ` method on the ` OAuth2::Client ` instance to make
632632requests for tokens for any Authentication grant type.
633633
634+ ## 📘 Comprehensive Usage
635+
636+ ### Common Flows (end-to-end)
637+
638+ - Authorization Code (server-side web app):
639+
640+ ``` ruby
641+ require " oauth2"
642+ client = OAuth2 ::Client .new (
643+ ENV [" CLIENT_ID" ],
644+ ENV [" CLIENT_SECRET" ],
645+ site: " https://provider.example.com" ,
646+ redirect_uri: " https://my.app.example.com/oauth/callback" ,
647+ )
648+
649+ # Step 1: redirect user to consent
650+ state = SecureRandom .hex(16 )
651+ auth_url = client.auth_code.authorize_url(scope: " openid profile email" , state: state)
652+ # redirect_to auth_url
653+
654+ # Step 2: handle the callback
655+ # params[:code], params[:state]
656+ raise " state mismatch" unless params[:state ] == state
657+ access = client.auth_code.get_token(params[:code ])
658+
659+ # Step 3: call APIs
660+ profile = access.get(" /api/v1/me" ).parsed
661+ ```
662+
663+ - Client Credentials (machine-to-machine):
664+
665+ ``` ruby
666+ client = OAuth2 ::Client .new (ENV [" CLIENT_ID" ], ENV [" CLIENT_SECRET" ], site: " https://provider.example.com" )
667+ access = client.client_credentials.get_token(audience: " https://api.example.com" )
668+ resp = access.get(" /v1/things" )
669+ ```
670+
671+ - Resource Owner Password (legacy; avoid when possible):
672+
673+ ``` ruby
674+ access = client.password.get_token(" jdoe" , " s3cret" , scope: " read" )
675+ ```
676+
677+ ### Refresh Tokens
678+
679+ When the server issues a refresh_token, you can refresh manually or implement an auto-refresh wrapper.
680+
681+ - Manual refresh:
682+
683+ ``` ruby
684+ if access.expired?
685+ access = access.refresh
686+ end
687+ ```
688+
689+ - Auto-refresh wrapper pattern:
690+
691+ ``` ruby
692+ class AutoRefreshingToken
693+ def initialize (token_provider , store: nil )
694+ @token = token_provider
695+ @store = store # e.g., something that responds to read/write for token data
696+ end
697+
698+ def with (& blk )
699+ tok = ensure_fresh!
700+ blk ? blk.call(tok) : tok
701+ rescue OAuth2 ::Error => e
702+ # If a 401 suggests token invalidation, try one refresh and retry once
703+ if e.response && e.response.status == 401 && @token .refresh_token
704+ @token = @token .refresh
705+ @store .write(@token .to_hash) if @store
706+ retry
707+ end
708+ raise
709+ end
710+
711+ private
712+
713+ def ensure_fresh!
714+ if @token .expired? && @token .refresh_token
715+ @token = @token .refresh
716+ @store .write(@token .to_hash) if @store
717+ end
718+ @token
719+ end
720+ end
721+
722+ # usage
723+ keeper = AutoRefreshingToken .new (access)
724+ keeper.with { |tok | tok.get(" /v1/protected" ) }
725+ ```
726+
727+ Persist the token across processes using ` AccessToken#to_hash ` and ` AccessToken.from_hash(client, hash) ` .
728+
729+ ### Token Revocation (RFC 7009)
730+
731+ You can revoke either the access token or the refresh token.
732+
733+ ``` ruby
734+ # Revoke the current access token
735+ access.revoke(token_type_hint: :access_token )
736+
737+ # Or explicitly revoke the refresh token (often also invalidates associated access tokens)
738+ access.revoke(token_type_hint: :refresh_token )
739+ ```
740+
741+ ### Client Configuration Tips
742+
743+ - Authentication schemes for the token request:
744+
745+ ``` ruby
746+ OAuth2 ::Client .new (
747+ id,
748+ secret,
749+ site: " https://provider.example.com" ,
750+ auth_scheme: :basic_auth , # default. Alternatives: :request_body, :tls_client_auth, :private_key_jwt
751+ )
752+ ```
753+
754+ - Faraday connection, timeouts, proxy, custom adapter/middleware:
755+
756+ ``` ruby
757+ client = OAuth2 ::Client .new (
758+ id,
759+ secret,
760+ site: " https://provider.example.com" ,
761+ connection_opts: {
762+ request: {open_timeout: 5 , timeout: 15 },
763+ proxy: ENV [" HTTPS_PROXY" ],
764+ ssl: {verify: true },
765+ },
766+ ) do |faraday |
767+ faraday.request(:url_encoded )
768+ # faraday.response :logger, Logger.new($stdout) # see OAUTH_DEBUG below
769+ faraday.adapter(:net_http_persistent ) # or any Faraday adapter you need
770+ end
771+ ```
772+
773+ - Redirection: The library follows up to ` max_redirects ` (default 5). You can override per-client via ` options[:max_redirects] ` .
774+
775+ ### Handling Responses and Errors
776+
777+ - Parsing:
778+
779+ ``` ruby
780+ resp = access.get(" /v1/thing" )
781+ resp.status # Integer
782+ resp.headers # Hash
783+ resp.body # String
784+ resp.parsed # SnakyHash::StringKeyed or Array when JSON array
785+ ```
786+
787+ - Error handling:
788+
789+ ``` ruby
790+ begin
791+ access.get(" /v1/forbidden" )
792+ rescue OAuth2 ::Error => e
793+ e.code # OAuth2 error code (when present)
794+ e.description # OAuth2 error description (when present)
795+ e.response # OAuth2::Response (full access to status/headers/body)
796+ end
797+ ```
798+
799+ - Disable raising on 4xx/5xx to inspect the response yourself:
800+
801+ ``` ruby
802+ client = OAuth2 ::Client .new (id, secret, site: site, raise_errors: false )
803+ res = client.request(:get , " /v1/maybe-errors" )
804+ if res.status == 429
805+ sleep res.headers[" retry-after" ].to_i
806+ end
807+ ```
808+
809+ ### Making Raw Token Requests
810+
811+ If a provider requires non-standard parameters or headers, you can call ` client.get_token ` directly:
812+
813+ ``` ruby
814+ access = client.get_token({
815+ grant_type: " client_credentials" ,
816+ audience: " https://api.example.com" ,
817+ headers: {" X-Custom" => " value" },
818+ parse: :json , # override parsing
819+ })
820+ ```
821+
822+ ### OpenID Connect (OIDC) Notes
823+
824+ - If the token response includes an ` id_token ` (a JWT), this gem surfaces it but does not validate the signature. Use a JWT library and your provider's JWKs to verify it.
825+ - For private_key_jwt client authentication, provide ` auth_scheme: :private_key_jwt ` and ensure your key configuration matches the provider requirements.
826+
827+ ### Debugging
828+
829+ - Set environment variable ` OAUTH_DEBUG=true ` to enable verbose Faraday logging (uses the client-provided logger).
830+ - To mirror a working curl request, ensure you set the same auth scheme, params, and content type. The Quick Example at the top shows a curl-to-ruby translation.
831+
832+ ---
833+
634834## 🦷 FLOSS Funding
635835
636836While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
0 commit comments