### Manage Client Parameters and Headers Source: https://context7.com/ueberauth/oauth2/llms.txt Provides methods to dynamically customize OAuth2 client configuration by adding or merging parameters and headers. Includes examples for adding single/multiple parameters, single/multiple headers, and setting up basic authentication. ```elixir client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com" ]) # Add single parameter client = OAuth2.Client.put_param(client, :scope, "user,email,repo") client = OAuth2.Client.put_param(client, "state", "random_state_123") # Merge multiple parameters client = OAuth2.Client.merge_params(client, %{ scope: "user email", state: "security_token", access_type: "offline" }) # Add single header client = OAuth2.Client.put_header(client, "accept", "application/json") client = OAuth2.Client.put_header(client, "x-api-version", "v2") # Add multiple headers client = OAuth2.Client.put_headers(client, [ {"accept", "application/json"}, {"user-agent", "MyApp/1.0"}, {"x-request-id", "req-123"} ]) # Add basic authentication header client = OAuth2.Client.basic_auth(client) # Adds header: "authorization" => "Basic " ``` -------------------------------- ### Configure Serializers for Content Types Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates how to register custom serializers for handling different content types when making API requests. This example shows how to register the 'Jason' library for JSON serialization. ```elixir client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com" ]) # Register JSON serializer client = OAuth2.Client.put_serializer(client, "application/json", Jason) ``` -------------------------------- ### Making Authenticated API Requests Source: https://context7.com/ueberauth/oauth2/llms.txt Guides on executing HTTP requests with OAuth2 access tokens, including various methods and error handling. ```APIDOC ## Making Authenticated API Requests ### Description Execute various HTTP requests (GET, POST, PUT, PATCH, DELETE) using an authenticated OAuth2 client. Includes examples with error handling, custom headers, and query parameters. ### Method `OAuth2.Client.get/3`, `OAuth2.Client.get!/3`, `OAuth2.Client.post/4`, `OAuth2.Client.put/4`, `OAuth2.Client.put!/4`, `OAuth2.Client.patch/4`, `OAuth2.Client.delete/3`, `OAuth2.Client.delete!/3` ### Parameters - **client** (OAuth2.Client) - The authenticated client object. - **url** (string) - The API endpoint URL. - **body** (map or any) - Optional - The request body for POST, PUT, PATCH requests. - **headers** (list) - Optional - A list of custom headers to include. - **params** (map) - Optional - Query parameters for the request. ### Request Example (API Calls) ```elixir # Assume client is an authenticated OAuth2.Client instance client = OAuth2.Client.new(token: "access_token_123", site: "https://api.example.com") # GET request with error handling case OAuth2.Client.get(client, "/api/user") do {:ok, %OAuth2.Response{status_code: 200, body: body}} -> IO.puts("User data: #{inspect(body)}") {:ok, %OAuth2.Response{status_code: 404}} -> IO.puts("User not found") {:error, %OAuth2.Response{status_code: 401, body: body}} -> IO.puts("Unauthorized: #{inspect(body)}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Request failed: #{reason}") end # GET request with bang variant (raises on error) response = OAuth2.Client.get!(client, "/api/user") user_data = response.body # POST request with body new_post = %{title: "My Post", content: "Post content here"} {:ok, response} = OAuth2.Client.post(client, "/api/posts", new_post) created_post = response.body # PUT request updated_data = %{id: 1, title: "Updated Title"} response = OAuth2.Client.put!(client, "/api/posts/1", updated_data) # PATCH request patch_data = %{status: "published"} {:ok, response} = OAuth2.Client.patch(client, "/api/posts/1", patch_data) # DELETE request {:ok, response} = OAuth2.Client.delete(client, "/api/posts/1") # Request with custom headers response = OAuth2.Client.get!(client, "/api/resource", [{"x-custom-header", "value"}] ) # Request with query parameters {:ok, response} = OAuth2.Client.get(client, "/api/search", [], params: [q: "search term", limit: 10] ) ``` ### Response #### Success Response - **response** (OAuth2.Response) - The API response object, containing `status_code` and `body`. #### Error Response - **error** (OAuth2.Response or OAuth2.Error) - An error object detailing the request failure. ``` -------------------------------- ### Install OAuth2 Dependency in mix.exs Source: https://github.com/ueberauth/oauth2/blob/master/README.md This snippet shows how to add the oauth2 library and its dependency, hackney, to your Elixir project's mix.exs file. Ensure you have the correct versions specified. ```elixir defp deps do # Add the dependency [ {:oauth2, "~> 2.0"}, {:hackney, "~> 1.18"} # depending on what tesla adapter you use ] end ``` -------------------------------- ### Implement Custom OAuth2 Strategy for GitHub Source: https://context7.com/ueberauth/oauth2/llms.txt Provides an example of creating a custom OAuth2 strategy for GitHub. This involves defining a module that uses `OAuth2.Strategy` and configuring client options like client ID, secret, redirect URI, and site-specific URLs. It also shows how to implement authorization and token retrieval logic. ```elixir # Define a custom GitHub OAuth2 strategy defmodule MyApp.GitHub do use OAuth2.Strategy # Public API def client do OAuth2.Client.new([ strategy: __MODULE__, client_id: System.get_env("GITHUB_CLIENT_ID"), client_secret: System.get_env("GITHUB_CLIENT_SECRET"), redirect_uri: "http://localhost:4000/auth/callback", site: "https://api.github.com", authorize_url: "https://github.com/login/oauth/authorize", token_url: "https://github.com/login/oauth/access_token" ]) |> OAuth2.Client.put_serializer("application/json", Jason) end def authorize_url!(params \ []) do client() |> OAuth2.Client.put_param(:scope, "user,public_repo") |> OAuth2.Client.authorize_url!(params) end def get_token!(params \ [], headers \ [], opts \ []) do OAuth2.Client.get_token!(client(), params, headers, opts) end # Strategy Callbacks def authorize_url(client, params) do OAuth2.Strategy.AuthCode.authorize_url(client, params) end def get_token(client, params, headers) do client |> OAuth2.Client.put_header("accept", "application/json") |> OAuth2.Strategy.AuthCode.get_token(params, headers) end end # Usage of custom strategy # Step 1: Redirect user to authorization URL auth_url = MyApp.GitHub.authorize_url!() # Redirect user's browser to auth_url # Step 2: Handle callback and exchange code for token client = MyApp.GitHub.get_token!(code: "code_from_callback") # Step 3: Make authenticated requests case OAuth2.Client.get(client, "/user") do {:ok, %OAuth2.Response{body: user}} -> IO.puts("GitHub user: #{user["login"]}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Error: #{reason}") end # Get user repositories {:ok, %OAuth2.Response{body: repos}} = OAuth2.Client.get(client, "/user/repos") Enum.each(repos, fn repo -> IO.puts("Repository: #{repo["full_name"]}") end) ``` -------------------------------- ### Make Authenticated API Requests (GET, POST, PUT, PATCH, DELETE) Source: https://context7.com/ueberauth/oauth2/llms.txt Executes various HTTP requests to an API using an OAuth2 access token. Includes examples for GET, POST, PUT, PATCH, and DELETE methods, with options for error handling, custom headers, and query parameters. ```elixir # GET request with error handling client = OAuth2.Client.new(token: "access_token_123", site: "https://api.example.com") case OAuth2.Client.get(client, "/api/user") do {:ok, %OAuth2.Response{status_code: 200, body: body}} -> IO.puts("User data: #{inspect(body)}") {:ok, %OAuth2.Response{status_code: 404}} -> IO.puts("User not found") {:error, %OAuth2.Response{status_code: 401, body: body}} -> IO.puts("Unauthorized: #{inspect(body)}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Request failed: #{reason}") end # GET request with bang variant (raises on error) response = OAuth2.Client.get!(client, "/api/user") user_data = response.body # POST request with body new_post = %{title: "My Post", content: "Post content here"} {:ok, response} = OAuth2.Client.post(client, "/api/posts", new_post) created_post = response.body # PUT request updated_data = %{id: 1, title: "Updated Title"} response = OAuth2.Client.put!(client, "/api/posts/1", updated_data) # PATCH request patch_data = %{status: "published"} {:ok, response} = OAuth2.Client.patch(client, "/api/posts/1", patch_data) # DELETE request {:ok, response} = OAuth2.Client.delete(client, "/api/posts/1") # Request with custom headers response = OAuth2.Client.get!(client, "/api/resource", [{"x-custom-header", "value"}] ) # Request with query parameters {:ok, response} = OAuth2.Client.get(client, "/api/search", [], params: [q: "search term", limit: 10] ) ``` -------------------------------- ### Elixir GitHub OAuth2 Strategy Usage: Access Resources Source: https://github.com/ueberauth/oauth2/blob/master/README.md Shows how to use the obtained access token to make authenticated requests to the GitHub API. It includes examples of handling successful responses and potential errors like unauthorized access. ```elixir user = OAuth2.Client.get!(client, "/user").body # Or case OAuth2.Client.get(client, "/user") do {:ok, %OAuth2.Response{body: user}} -> user {:error, %OAuth2.Response{status_code: 401, body: body}} -> Logger.error("Unauthorized token") {:error, %OAuth2.Error{reason: reason}} -> Logger.error("Error: #{inspect reason}") end ``` -------------------------------- ### Authorization Code Flow Source: https://context7.com/ueberauth/oauth2/llms.txt Guides through the Authorization Code flow for user authentication, including generating authorization URLs and exchanging codes for tokens. ```APIDOC ## Authorization Code Flow Complete OAuth 2.0 authorization code flow for user authentication. ### Method `OAuth2.Client.authorize_url!/2` `OAuth2.Client.get_token/2` `OAuth2.Client.get_token!/2` ### Endpoint N/A (Client-side flow) ### Parameters #### For `authorize_url!/2`: - **client** (OAuth2.Client) - The initialized OAuth2 client. - **scope** (string) - Optional - The requested scopes for the authorization. #### For `get_token/2`: - **client** (OAuth2.Client) - The initialized OAuth2 client. - **code** (string) - Required - The authorization code received from the callback. ### Request Example ```elixir # Step 1: Initialize client and generate authorization URL client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "github_client_id", client_secret: "github_client_secret", site: "https://api.github.com", authorize_url: "https://github.com/login/oauth/authorize", token_url: "https://github.com/login/oauth/access_token", redirect_uri: "https://myapp.com/auth/callback" ]) # Generate authorization URL (redirect user to this URL) auth_url = OAuth2.Client.authorize_url!(client, scope: "user,public_repo") # => "https://github.com/login/oauth/authorize?client_id=github_client_id&redirect_uri=https%3A%2F%2Fmyapp.com%2Fauth%2Fcallback&response_type=code&scope=user%2Cpublic_repo" # Step 2: After user authorizes, exchange code for access token # (code is received as query parameter in callback URL) case OAuth2.Client.get_token(client, code: "authorization_code_from_callback") do {:ok, client} -> # Success! Client now has access token access_token = client.token.access_token refresh_token = client.token.refresh_token IO.puts("Access token: #{access_token}") {:error, %OAuth2.Response{status_code: status, body: body}} -> IO.puts("Token request failed with status #{status}: #{inspect(body)}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Error: #{reason}") end # Or use the bang variant that raises on error client = OAuth2.Client.get_token!(client, code: "authorization_code_from_callback") ``` ### Response #### Success Response (200) - **client** (OAuth2.Client) - The client struct with the updated access token. #### Error Response - **{:error, %OAuth2.Response{...}}** - Indicates an HTTP error during the token request. - **{:error, %OAuth2.Error{...}}** - Indicates a library-level OAuth2 error. ``` -------------------------------- ### Elixir GitHub OAuth2 Strategy Usage: Generate URL Source: https://github.com/ueberauth/oauth2/blob/master/README.md Example of how to generate the authorization URL using the defined GitHub strategy. This is typically the first step in the OAuth2 flow, redirecting the user to the provider for authentication. ```elixir GitHub.authorize_url! ``` -------------------------------- ### Authorization Code Flow: Access Protected Resource Source: https://github.com/ueberauth/oauth2/blob/master/README.md Once you have a valid access token, you can use it to make requests to protected resources on the API. The `get!` function is a convenience method for making GET requests with the token. ```elixir # Use the access token to make a request for resources resource = OAuth2.Client.get!(client, "/api/resource").body ``` -------------------------------- ### Elixir GitHub OAuth2 Strategy Usage: Get Token Source: https://github.com/ueberauth/oauth2/blob/master/README.md Demonstrates how to obtain an access token after receiving the authorization code from the OAuth2 provider. This code snippet assumes the `code` variable is available from the callback. ```elixir client = GitHub.get_token!(code: code) ``` -------------------------------- ### Client Credentials Flow: Get Initial Access Token Source: https://github.com/ueberauth/oauth2/blob/master/README.md This Elixir snippet shows how to initialize an OAuth2 client using the Client Credentials flow and obtain an initial access token. The token is automatically stored within the client struct. ```elixir # Initializing a client with the strategy `OAuth2.Strategy.ClientCredentials` client = OAuth2.Client.new([ strategy: OAuth2.Strategy.ClientCredentials, client_id: "client_id", client_secret: "abc123", site: "https://auth.example.com" ]) # Request a token from with the newly created client # Token will be stored inside the `%OAuth2.Client{}` struct (client.token) client = OAuth2.Client.get_token!(client) # client.token contains the `%OAuth2.AccessToken{}` struct # raw access token access_token = client.token.access_token ``` -------------------------------- ### Authorization Code Flow: Get Authorization URL Source: https://github.com/ueberauth/oauth2/blob/master/README.md Initialize an OAuth2 client for the Authorization Code flow and generate the authorization URL. This URL is used to redirect the user to the OAuth provider for authentication and authorization. ```elixir # Initialize a client with client_id, client_secret, site, and redirect_uri. # The strategy option is optional as it defaults to `OAuth2.Strategy.AuthCode`. client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, #default client_id: "client_id", client_secret: "abc123", site: "https://auth.example.com", redirect_uri: "https://example.com/auth/callback" ]) # Generate the authorization URL and redirect the user to the provider. OAuth2.Client.authorize_url!(client) # => "https://auth.example.com/oauth/authorize?client_id=client_id&redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Fcallback&response_type=code" ``` -------------------------------- ### OAuth2 Client Initialization Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates how to initialize an OAuth2 client with different configurations, including strategy, credentials, and existing tokens. ```APIDOC ## OAuth2 Client Initialization Initialize a client with provider credentials and configuration. ### Method `OAuth2.Client.new/1` ### Parameters #### Request Body (for initialization options) - **strategy** (module) - Required - The OAuth2 strategy to use (e.g., `OAuth2.Strategy.AuthCode`). - **client_id** (string) - Required - The client identifier. - **client_secret** (string) - Required - The client secret. - **site** (string) - Required - The base URL of the OAuth2 provider. - **redirect_uri** (string) - Optional - The callback URL registered with the provider. - **authorize_url** (string) - Optional - The authorization endpoint URL. - **token_url** (string) - Optional - The token endpoint URL. - **token** (struct or map) - Optional - An existing access token or AccessToken struct. ### Request Example ```elixir # Create a basic OAuth2 client for Authorization Code flow client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "your_client_id_here", client_secret: "your_client_secret_here", site: "https://auth.example.com", redirect_uri: "https://yourapp.com/auth/callback", authorize_url: "/oauth/authorize", token_url: "/oauth/token" ]) # Create a client with just an access token for API requests authenticated_client = OAuth2.Client.new(token: "existing_access_token_123") # Create a client with an AccessToken struct token = OAuth2.AccessToken.new(%{ "access_token" => "abc123", "refresh_token" => "refresh456", "expires_in" => 3600, "token_type" => "Bearer" }) client = OAuth2.Client.new(token: token, site: "https://api.example.com") ``` ``` -------------------------------- ### OAuth2 Password Strategy Authentication Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates how to initialize an OAuth2 client with the Password strategy and authenticate using a username and password. ```APIDOC ## OAuth2 Password Strategy Authentication ### Description Initializes an OAuth2 client using the Password strategy and authenticates with provided username and password. ### Method `OAuth2.Client.new/1`, `OAuth2.Client.get_token/2` ### Parameters #### Client Initialization Parameters - **strategy** (atom) - Required - `:password` for password flow. - **client_id** (string) - Required - Your application's client ID. - **client_secret** (string) - Required - Your application's client secret. - **site** (string) - Required - The base URL of the OAuth2 provider. - **token_url** (string) - Required - The endpoint for token requests. #### Authentication Parameters - **username** (string) - Required - The user's username. - **password** (string) - Required - The user's password. - **scope** (string) - Optional - Requested scopes for the access token. ### Request Example (Initialization and Authentication) ```elixir client = OAuth2.Client.new([ strategy: OAuth2.Strategy.Password, client_id: "app_client_id", client_secret: "app_client_secret", site: "https://api.example.com", token_url: "/oauth/token" ]) case OAuth2.Client.get_token(client, username: "user@example.com", password: "userpassword") do {:ok, client} -> # Successfully authenticated user_token = client.token.access_token IO.puts("User authenticated with token: #{user_token}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Authentication failed: #{reason}") end # With additional scope parameter client = OAuth2.Client.get_token!(client, username: "user@example.com", password: "userpassword", scope: "read write" ) ``` ### Response #### Success Response - **client** (OAuth2.Client) - The authenticated client object with an access token. #### Error Response - **error** (OAuth2.Error) - An error object detailing the authentication failure. ``` -------------------------------- ### Initialize and Authenticate with Password Strategy Source: https://context7.com/ueberauth/oauth2/llms.txt Initializes an OAuth2 client using the Password strategy and authenticates a user with their username and password to obtain an access token. Handles successful authentication and authentication failures. ```elixir client = OAuth2.Client.new([ strategy: OAuth2.Strategy.Password, client_id: "app_client_id", client_secret: "app_client_secret", site: "https://api.example.com", token_url: "/oauth/token" ]) case OAuth2.Client.get_token(client, username: "user@example.com", password: "userpassword") do {:ok, client} -> user_token = client.token.access_token IO.puts("User authenticated with token: #{user_token}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Authentication failed: #{reason}") end # With additional scope parameter client = OAuth2.Client.get_token!(client, username: "user@example.com", password: "userpassword", scope: "read write" ) ``` -------------------------------- ### Working with OAuth2 Access Tokens Source: https://context7.com/ueberauth/oauth2/llms.txt Explains how to create, inspect, and manage OAuth2 access tokens. This includes creating tokens from strings or provider responses, checking expiration status, and accessing token properties like access token, refresh token, and expiry time. ```elixir # Create token from string token = OAuth2.AccessToken.new("simple_access_token") # => %OAuth2.AccessToken{ # access_token: "simple_access_token", # refresh_token: nil, # expires_at: nil, # token_type: "Bearer", # other_params: %{} # } # Create token from provider response map token_response = %{ "access_token" => "abc123xyz", "refresh_token" => "refresh_token_456", "expires_in" => 3600, "token_type" => "Bearer", "scope" => "user email" } token = OAuth2.AccessToken.new(token_response) # Check if token expires if OAuth2.AccessToken.expires?(token) do IO.puts("Token expires at: #{token.expires_at}") else IO.puts("Token does not expire") end # Check if token has expired if OAuth2.AccessToken.expired?(token) do IO.puts("Token has expired, need to refresh") client = OAuth2.Client.refresh_token!(client) else IO.puts("Token is still valid") end # Access token properties IO.puts("Access token: #{token.access_token}") IO.puts("Refresh token: #{token.refresh_token}") IO.puts("Token type: #{token.token_type}") IO.puts("Expires at (unix timestamp): #{token.expires_at}") IO.puts("Additional params: #{inspect(token.other_params)})" ``` -------------------------------- ### Configuring Serializers Source: https://context7.com/ueberauth/oauth2/llms.txt Shows how to register custom serializers for different content types. ```APIDOC ## Configuring Serializers ### Description Register custom serializers to handle encoding and decoding of request and response bodies for specific content types. ### Method `OAuth2.Client.put_serializer/3` ### Parameters - **client** (OAuth2.Client) - The client object to configure. - **content_type** (string) - The content type for which to register the serializer (e.g., `"application/json"`). - **serializer_module** (module) - The module implementing the serializer (e.g., `Jason`). ### Request Example (Serializer Configuration) ```elixir client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com" ]) # Register JSON serializer client = OAuth2.Client.put_serializer(client, "application/json", Jason) ``` ### Response - **client** (OAuth2.Client) - The client object with the serializer registered. ``` -------------------------------- ### Create OAuth2 Client in Elixir Source: https://context7.com/ueberauth/oauth2/llms.txt Initializes an OAuth2 client with provider credentials and configuration. Supports different grant types and can be initialized with an existing token or AccessToken struct. ```elixir client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "your_client_id_here", client_secret: "your_client_secret_here", site: "https://auth.example.com", redirect_uri: "https://yourapp.com/auth/callback", authorize_url: "/oauth/authorize", token_url: "/oauth/token" ]) authenticated_client = OAuth2.Client.new(token: "existing_access_token_123") token = OAuth2.AccessToken.new(%{ "access_token" => "abc123", "refresh_token" => "refresh456", "expires_in" => 3600, "token_type" => "Bearer" }) client = OAuth2.Client.new(token: token, site: "https://api.example.com") ``` -------------------------------- ### Managing Client Parameters and Headers Source: https://context7.com/ueberauth/oauth2/llms.txt Explains how to dynamically add or modify client parameters and headers. ```APIDOC ## Managing Client Parameters and Headers ### Description Customize the OAuth2 client's configuration by adding or merging parameters and headers programmatically. ### Method `OAuth2.Client.put_param/3`, `OAuth2.Client.merge_params/2`, `OAuth2.Client.put_header/3`, `OAuth2.Client.put_headers/2`, `OAuth2.Client.basic_auth/1` ### Parameters - **client** (OAuth2.Client) - The client object to modify. - **key** (string or atom) - The parameter or header key. - **value** (string) - The parameter or header value. - **params** (map) - A map of parameters to merge. - **headers** (list) - A list of headers to add. ### Request Example (Configuration) ```elixir client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com" ]) # Add single parameter client = OAuth2.Client.put_param(client, :scope, "user,email,repo") client = OAuth2.Client.put_param(client, "state", "random_state_123") # Merge multiple parameters client = OAuth2.Client.merge_params(client, %{ scope: "user email", state: "security_token", access_type: "offline" }) # Add single header client = OAuth2.Client.put_header(client, "accept", "application/json") client = OAuth2.Client.put_header(client, "x-api-version", "v2") # Add multiple headers client = OAuth2.Client.put_headers(client, [ {"accept", "application/json"}, {"user-agent", "MyApp/1.0"}, {"x-request-id", "req-123"} ]) # Add basic authentication header client = OAuth2.Client.basic_auth(client) # Adds header: "authorization" => "Basic " ``` ### Response - **client** (OAuth2.Client) - The modified client object with updated parameters or headers. ``` -------------------------------- ### Client Credentials Flow: Refresh Access Token Source: https://github.com/ueberauth/oauth2/blob/master/README.md Demonstrates how to refresh an existing access token using its corresponding refresh token. This requires initializing a new client configured with the `OAuth2.Strategy.Refresh` strategy. ```elixir # raw refresh token - use a client with `OAuth2.Strategy.Refresh` for refreshing the token refresh_token = client.token.refresh_token refresh_client = OAuth2.Client.new([ strategy: OAuth2.Strategy.Refresh, client_id: "client_id", client_secret: "abc123", site: "https://auth.example.com", params: %{"refresh_token" => refresh_token} ]) # refresh_client.token contains the `%OAuth2.AccessToken{}` struct again refresh_client = OAuth2.Client.get_token!(refresh_client) ``` -------------------------------- ### Elixir GitHub OAuth2 Strategy Implementation Source: https://github.com/ueberauth/oauth2/blob/master/README.md Defines a custom OAuth2 strategy for GitHub integration. It includes methods for client initialization, generating authorization URLs, and obtaining access tokens. This strategy leverages the OAuth2.Strategy and OAuth2.Client modules. ```elixir defmodule GitHub do use OAuth2.Strategy # Public API def client do OAuth2.Client.new([ strategy: __MODULE__, client_id: System.get_env("GITHUB_CLIENT_ID"), client_secret: System.get_env("GITHUB_CLIENT_SECRET"), redirect_uri: "http://myapp.com/auth/callback", site: "https://api.github.com", authorize_url: "https://github.com/login/oauth/authorize", token_url: "https://github.com/login/oauth/access_token" ]) |> OAuth2.Client.put_serializer("application/json", Jason) end def authorize_url! do OAuth2.Client.authorize_url!(client(), scope: "user,public_repo") end # you can pass options to the underlying http library via `opts` parameter def get_token!(params \ [], headers \ [], opts \ []) do OAuth2.Client.get_token!(client(), params, headers, opts) end # Strategy Callbacks def authorize_url(client, params) do OAuth2.Strategy.AuthCode.authorize_url(client, params) end def get_token(client, params, headers) do client |> put_header("accept", "application/json") |> OAuth2.Strategy.AuthCode.get_token(params, headers) end end ``` -------------------------------- ### Configure HTTP Client Options for OAuth2 Source: https://context7.com/ueberauth/oauth2/llms.txt Shows how to configure the underlying HTTP client (Tesla) for the OAuth2 library. This includes setting the adapter (e.g., Hackney, Mint), adding custom middleware, enabling debug mode for verbose logging, and configuring request-specific options like timeouts. ```elixir # In config/config.exs - Configure Tesla adapter config :oauth2, adapter: Tesla.Adapter.Hackney # Or use another adapter config :oauth2, adapter: Tesla.Adapter.Mint # Add custom Tesla middleware config :oauth2, middleware: [ Tesla.Middleware.Retry, {Tesla.Middleware.Timeout, timeout: 30_000}, {Tesla.Middleware.Fuse, name: :oauth2_fuse, opts: []} ] # Enable debug mode to see provider responses config :oauth2, debug: true # Use request_opts for per-request configuration client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com", request_opts: [ timeout: 30_000, recv_timeout: 15_000 ] ]) # Override request_opts per request {:ok, response} = OAuth2.Client.get(client, "/api/slow-endpoint", [], timeout: 60_000, recv_timeout: 60_000 ) ``` -------------------------------- ### Configure OAuth2 Client with Proxy Source: https://context7.com/ueberauth/oauth2/llms.txt This snippet shows how to initialize an OAuth2 client with specific request options, including a proxy server for outgoing HTTP requests. It's useful for environments where network requests must pass through a proxy. ```elixir client = OAuth2.Client.new([ client_id: "client_id", site: "https://api.example.com", request_opts: [ proxy: "http://proxy.example.com:8080" ] ]) ``` -------------------------------- ### Refreshing Access Tokens Source: https://context7.com/ueberauth/oauth2/llms.txt Details on how to obtain a new access token using a refresh token. ```APIDOC ## Refreshing Access Tokens ### Description Obtain a new access token using a refresh token when the current access token has expired. ### Method `OAuth2.Client.refresh_token/1`, `OAuth2.Client.refresh_token!/1` ### Parameters - **client** (OAuth2.Client) - The client object containing the refresh token. ### Request Example (Refreshing Token) ```elixir # Assume we have a client with an expired token but valid refresh_token client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "client_id", client_secret: "client_secret", site: "https://api.example.com", token: %OAuth2.AccessToken{ access_token: "expired_token", refresh_token: "valid_refresh_token", expires_at: 1234567890 } ]) # Refresh the access token case OAuth2.Client.refresh_token(client) do {:ok, refreshed_client} -> # New access token obtained new_token = refreshed_client.token.access_token new_refresh = refreshed_client.token.refresh_token IO.puts("New access token: #{new_token}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Token refresh failed: #{reason}") end # Using bang variant refreshed_client = OAuth2.Client.refresh_token!(client) # Manual refresh using Refresh strategy refresh_client = OAuth2.Client.new([ strategy: OAuth2.Strategy.Refresh, client_id: "client_id", client_secret: "client_secret", site: "https://api.example.com", params: %{"refresh_token" => "valid_refresh_token"} ]) refresh_client = OAuth2.Client.get_token!(refresh_client) ``` ### Response #### Success Response - **refreshed_client** (OAuth2.Client) - A new client object with an updated access token. #### Error Response - **error** (OAuth2.Error) - An error object detailing the token refresh failure. ``` -------------------------------- ### Complete Client Credentials Flow in Elixir Source: https://context7.com/ueberauth/oauth2/llms.txt Implements the client credentials grant type for server-to-server authentication. This flow does not involve user interaction and is used for machine-to-machine authorization. It shows how to initialize the client and request an access token, with an option to send credentials in the request body. ```elixir # Initialize client with Client Credentials strategy client = OAuth2.Client.new([ strategy: OAuth2.Strategy.ClientCredentials, client_id: "service_client_id", client_secret: "service_client_secret", site: "https://api.example.com", token_url: "/oauth/token" ]) # Request access token (no authorization code needed) case OAuth2.Client.get_token(client) do {:ok, client} -> # Token is now available in client.token IO.puts("Access token: #{client.token.access_token}") IO.puts("Token type: #{client.token.token_type}") IO.puts("Expires at: #{client.token.expires_at}") {:error, error} -> IO.puts("Failed to get token: #{inspect(error)}") end # Using auth in request body instead of header client = OAuth2.Client.get_token!(client, auth_scheme: "request_body") ``` -------------------------------- ### OAuth2 Error Handling Patterns Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates comprehensive error handling for OAuth2 operations in Elixir. It covers success cases, specific HTTP error codes (400, 401, 500), and general OAuth2 errors using pattern matching and bang variants with exception handling. Includes handling for token refresh failures. ```elixir client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "client_id", client_secret: "client_secret", site: "https://api.example.com" ]) # Pattern match on all possible error cases case OAuth2.Client.get_token(client, code: "auth_code") do {:ok, client} -> # Success - proceed with authenticated requests {:ok, %OAuth2.Response{body: data}} = OAuth2.Client.get(client, "/api/resource") process_data(data) {:error, %OAuth2.Response{status_code: 400, body: body}} -> # Bad request - invalid parameters Logger.error("Invalid request: #{inspect(body)}") {:error, :invalid_request} {:error, %OAuth2.Response{status_code: 401, body: body}} -> # Unauthorized - invalid credentials Logger.error("Authentication failed: #{inspect(body)}") {:error, :unauthorized} {:error, %OAuth2.Response{status_code: 500, body: body}} -> # Server error Logger.error("Server error: #{inspect(body)}") {:error, :server_error} {:error, %OAuth2.Error{reason: reason}} -> # Network error or other failure Logger.error("Request error: #{reason}") {:error, :network_error} end # Using bang variants with exception handling try do client = OAuth2.Client.get_token!(client, code: "auth_code") response = OAuth2.Client.get!(client, "/api/resource") {:ok, response.body} rescue e in OAuth2.Error -> Logger.error("OAuth2 error: #{e.reason}") {:error, :oauth2_error} end # Refresh token error handling case OAuth2.Client.refresh_token(client) do {:ok, refreshed_client} -> # Token refreshed successfully {:ok, refreshed_client} {:error, %OAuth2.Error{reason: "Refresh token not available."}} -> # No refresh token - need to re-authenticate {:error, :needs_reauth} {:error, error} -> # Other refresh errors Logger.error("Token refresh failed: #{inspect(error)}") {:error, :refresh_failed} end ``` -------------------------------- ### Refresh Access Token Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates how to refresh an expired OAuth2 access token using a valid refresh token. It covers both error handling with `case` and direct usage of the bang variant for immediate results. Also shows manual refresh using the Refresh strategy. ```elixir # Assume we have a client with an expired token but valid refresh_token client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "client_id", client_secret: "client_secret", site: "https://api.example.com", token_url: "/oauth/token", token: %OAuth2.AccessToken{ access_token: "expired_token", refresh_token: "valid_refresh_token", expires_at: 1234567890 } ]) # Refresh the access token case OAuth2.Client.refresh_token(client) do {:ok, refreshed_client} -> new_token = refreshed_client.token.access_token new_refresh = refreshed_client.token.refresh_token IO.puts("New access token: #{new_token}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Token refresh failed: #{reason}") end # Using bang variant refreshed_client = OAuth2.Client.refresh_token!(client) # Manual refresh using Refresh strategy refresh_client = OAuth2.Client.new([ strategy: OAuth2.Strategy.Refresh, client_id: "client_id", client_secret: "client_secret", site: "https://api.example.com", params: %{"refresh_token" => "valid_refresh_token"} ]) refresh_client = OAuth2.Client.get_token!(refresh_client) ``` -------------------------------- ### Configure Tesla HTTP Client Adapter and Middleware Source: https://github.com/ueberauth/oauth2/blob/master/README.md This Elixir code demonstrates how to configure the underlying HTTP client library (Tesla) used by OAuth2. You can specify a different adapter or add custom Tesla middleware for request processing. ```elixir config :oauth2, adapter: Tesla.Adapter.Mint ``` ```elixir config :oauth2, middleware: [ Tesla.Middleware.Retry, {Tesla.Middleware.Fuse, name: :example} ] ``` -------------------------------- ### Client Credentials Flow Source: https://context7.com/ueberauth/oauth2/llms.txt Handles server-to-server authentication using the Client Credentials grant type, suitable for machine-to-machine interactions. ```APIDOC ## Client Credentials Flow Server-to-server authentication without user interaction. ### Method `OAuth2.Client.get_token/1` `OAuth2.Client.get_token!(1)` ### Endpoint N/A (Client-side flow) ### Parameters #### For `get_token/1`: - **client** (OAuth2.Client) - The initialized OAuth2 client configured with `OAuth2.Strategy.ClientCredentials`. - **auth_scheme** (string) - Optional - How to send authentication, e.g., `"request_body"`. ### Request Example ```elixir # Initialize client with Client Credentials strategy client = OAuth2.Client.new([ strategy: OAuth2.Strategy.ClientCredentials, client_id: "service_client_id", client_secret: "service_client_secret", site: "https://api.example.com", token_url: "/oauth/token" ]) # Request access token (no authorization code needed) case OAuth2.Client.get_token(client) do {:ok, client} -> # Token is now available in client.token IO.puts("Access token: #{client.token.access_token}") IO.puts("Token type: #{client.token.token_type}") IO.puts("Expires at: #{client.token.expires_at}") {:error, error} -> IO.puts("Failed to get token: #{inspect(error)}") end # Using auth in request body instead of header client = OAuth2.Client.get_token!(client, auth_scheme: "request_body") ``` ### Response #### Success Response (200) - **client** (OAuth2.Client) - The client struct with the new access token. - **token.access_token** (string) - The obtained access token. - **token.token_type** (string) - The type of the token (e.g., "Bearer"). - **token.expires_at** (DateTime) - The expiration time of the token. #### Error Response - **{:error, ...}** - Indicates an error during the token retrieval process. ``` -------------------------------- ### Register and Manage OAuth2 Serializers Source: https://context7.com/ueberauth/oauth2/llms.txt Demonstrates how to register, update, and remove custom serializers for handling data serialization and deserialization based on content-type headers. This allows the client to automatically encode outgoing requests and decode incoming responses. ```elixir defmodule MyApp.XMLSerializer do def encode!(data), do: MyApp.XML.encode(data) def decode!(binary), do: MyApp.XML.decode(binary) end client = OAuth2.Client.put_serializer(client, "application/xml", MyApp.XMLSerializer) # Register JSON API serializer client = OAuth2.Client.put_serializer(client, "application/vnd.api+json", Jason) # Remove a serializer client = OAuth2.Client.delete_serializer(client, "application/xml") # Serializers are automatically used based on content-type headers # Request with JSON automatically encoded response = OAuth2.Client.post!(client, "/api/resource", %{key: "value"}) # Response with JSON automatically decoded {:ok, %OAuth2.Response{body: decoded_body}} = OAuth2.Client.get(client, "/api/data") ``` -------------------------------- ### Enable Debug Mode for OAuth2 Responses Source: https://github.com/ueberauth/oauth2/blob/master/README.md Enable debug mode for the OAuth2 client to log responses from the provider, which is useful for troubleshooting token acquisition. This is configured in your application's configuration. ```elixir config :oauth2, debug: true ``` -------------------------------- ### Complete Authorization Code Flow in Elixir Source: https://context7.com/ueberauth/oauth2/llms.txt Handles the OAuth 2.0 authorization code flow for user authentication. This involves generating an authorization URL, redirecting the user, and then exchanging the received authorization code for an access token. It includes error handling for token exchange. ```elixir # Step 1: Initialize client and generate authorization URL client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, client_id: "github_client_id", client_secret: "github_client_secret", site: "https://api.github.com", authorize_url: "https://github.com/login/oauth/authorize", token_url: "https://github.com/login/oauth/access_token", redirect_uri: "https://myapp.com/auth/callback" ]) # Generate authorization URL (redirect user to this URL) auth_url = OAuth2.Client.authorize_url!(client, scope: "user,public_repo") # => "https://github.com/login/oauth/authorize?client_id=github_client_id&redirect_uri=https%3A%2F%2Fmyapp.com%2Fauth%2Fcallback&response_type=code&scope=user%2Cpublic_repo" # Step 2: After user authorizes, exchange code for access token # (code is received as query parameter in callback URL) case OAuth2.Client.get_token(client, code: "authorization_code_from_callback") do {:ok, client} -> # Success! Client now has access token access_token = client.token.access_token refresh_token = client.token.refresh_token IO.puts("Access token: #{access_token}") {:error, %OAuth2.Response{status_code: status, body: body}} -> IO.puts("Token request failed with status #{status}: #{inspect(body)}") {:error, %OAuth2.Error{reason: reason}} -> IO.puts("Error: #{reason}") end # Or use the bang variant that raises on error client = OAuth2.Client.get_token!(client, code: "authorization_code_from_callback") ``` -------------------------------- ### Authorization Code Flow: Obtain Access Token Source: https://github.com/ueberauth/oauth2/blob/master/README.md After the user is redirected back from the OAuth provider with an authorization code, use this code to obtain an access token. The `get_token!` function exchanges the code for a token and updates the client struct. ```elixir # Use the authorization code returned from the provider to obtain an access token. client = OAuth2.Client.get_token!(client, code: "someauthcode") ``` -------------------------------- ### Register Custom Serializers for MIME Types Source: https://github.com/ueberauth/oauth2/blob/master/README.md Configure the OAuth2 client to automatically handle encoding and decoding for specific MIME types by registering serializer modules. Each module must export `encode!/1` and `decode!/1` functions. ```elixir OAuth2.Client.put_serializer(client, "application/vnd.api+json", Jason) OAuth2.Client.put_serializer(client, "application/xml", MyApp.Parsers.XML) ``` ```elixir defmodule MyApp.Parsers.XML do def encode!(data), do: # ... def decode!(binary), do: # ... end ```