Skip to content

Conversation

brianjlai
Copy link
Contributor

@brianjlai brianjlai commented Sep 30, 2025

What

When first developing the JWT authenticator, based on various specs and usage by connectors, we did not see a use case where the signed JWT token needed to be injected anywhere but under the Authorization header of requests.

While working on a Box connector, this is the first usage where we need to load it into the request_json_body under the assertion key.

How

This PR adds functionality so that the JwtAuthenticator can now specify an optional request_option to inject the signed token into different request options. Optional so that we retain backwards compatibility

Validation steps

  • I wrote a test manifest using this flow and loading into the json body
  • Validate against existing connectors like source-google-search-console which use the default Authorization header behavior to ensure regressions

Summary by CodeRabbit

  • New Features

    • JWT authenticator supports configurable token injection targets (header, query parameter, form data, JSON body) and customizable header key (default: Authorization).
  • Schema

    • Declarative schema now allows specifying request_option on JWT authenticators.
  • Parser

    • Manifests with request_option for JWT auth are propagated into created components.
  • Tests

    • Added coverage for JWT injection into headers, params, form data, JSON body, and custom header keys.
  • Chores

    • Dependency bumped: dagger-io updated.

@github-actions github-actions bot added the enhancement New feature or request label Sep 30, 2025
Copy link

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

Testing This CDK Version

You can test this version of the CDK using the following:

# Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@brian/jwt_authenticator_configurable_request_option#egg=airbyte-python-cdk[dev]' --help

# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch brian/jwt_authenticator_configurable_request_option

Helpful Resources

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /autofix - Fixes most formatting and linting issues
  • /poetry-lock - Updates poetry.lock file
  • /test - Runs connector tests with the updated CDK
  • /poe build - Regenerate git-committed build artifacts, such as the pydantic models which are generated from the manifest JSON schema in YAML.
  • /poe <command> - Runs any poe command in the CDK environment

📝 Edit this welcome message.

Copy link

github-actions bot commented Sep 30, 2025

PyTest Results (Fast)

3 777 tests  +5   3 765 ✅ +5   6m 20s ⏱️ +5s
    1 suites ±0      12 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 0ce9630. ± Comparison against base commit 25132b6.

This pull request removes 1 and adds 6 tests. Note that renamed tests count towards both.
unit_tests.sources.declarative.parsers.test_model_to_component_factory ‑ test_create_jwt_authenticator[config1-\n            authenticator:\n                type: JwtAuthenticator\n                secret_key: "{{ config['secret_key'] }}"\n                base64_encode_secret_key: True\n                algorithm: RS256\n                token_duration: 3600\n                header_prefix: Bearer\n                jwt_headers:\n                    kid: "{{ config['kid'] }}"\n                    cty: "JWT"\n                    typ: "Alt"\n                additional_jwt_headers:\n                    test: "{{ config['test']}}"\n                jwt_payload:\n                    iss: "{{ config['iss'] }}"\n                    sub: "test sub"\n                    aud: "test aud"\n                additional_jwt_payload:\n                    test: "test custom payload"\n            -expected1]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_headers[test_get_request_headers]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_headers[test_with_default_authorization_header]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers0]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers1]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers2]
unit_tests.sources.declarative.parsers.test_model_to_component_factory ‑ test_create_jwt_authenticator[config1-\n            authenticator:\n                type: JwtAuthenticator\n                secret_key: "{{ config['secret_key'] }}"\n                base64_encode_secret_key: True\n                algorithm: RS256\n                token_duration: 3600\n                header_prefix: Bearer\n                jwt_headers:\n                    kid: "{{ config['kid'] }}"\n                    cty: "JWT"\n                    typ: "Alt"\n                additional_jwt_headers:\n                    test: "{{ config['test']}}"\n                jwt_payload:\n                    iss: "{{ config['iss'] }}"\n                    sub: "test sub"\n                    aud: "test aud"\n                additional_jwt_payload:\n                    test: "test custom payload"\n                request_option:\n                    type: RequestOption\n                    inject_into: body_json\n                    field_name: authorization\n            -expected1]

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Sep 30, 2025

PyTest Results (Full)

3 780 tests  +5   3 768 ✅ +5   11m 2s ⏱️ ±0s
    1 suites ±0      12 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 0ce9630. ± Comparison against base commit 25132b6.

This pull request removes 1 and adds 6 tests. Note that renamed tests count towards both.
unit_tests.sources.declarative.parsers.test_model_to_component_factory ‑ test_create_jwt_authenticator[config1-\n            authenticator:\n                type: JwtAuthenticator\n                secret_key: "{{ config['secret_key'] }}"\n                base64_encode_secret_key: True\n                algorithm: RS256\n                token_duration: 3600\n                header_prefix: Bearer\n                jwt_headers:\n                    kid: "{{ config['kid'] }}"\n                    cty: "JWT"\n                    typ: "Alt"\n                additional_jwt_headers:\n                    test: "{{ config['test']}}"\n                jwt_payload:\n                    iss: "{{ config['iss'] }}"\n                    sub: "test sub"\n                    aud: "test aud"\n                additional_jwt_payload:\n                    test: "test custom payload"\n            -expected1]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_headers[test_get_request_headers]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_headers[test_with_default_authorization_header]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers0]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers1]
unit_tests.sources.declarative.auth.test_jwt.TestJwtAuthenticator ‑ test_get_request_options[test_get_request_headers2]
unit_tests.sources.declarative.parsers.test_model_to_component_factory ‑ test_create_jwt_authenticator[config1-\n            authenticator:\n                type: JwtAuthenticator\n                secret_key: "{{ config['secret_key'] }}"\n                base64_encode_secret_key: True\n                algorithm: RS256\n                token_duration: 3600\n                header_prefix: Bearer\n                jwt_headers:\n                    kid: "{{ config['kid'] }}"\n                    cty: "JWT"\n                    typ: "Alt"\n                additional_jwt_headers:\n                    test: "{{ config['test']}}"\n                jwt_payload:\n                    iss: "{{ config['iss'] }}"\n                    sub: "test sub"\n                    aud: "test aud"\n                additional_jwt_payload:\n                    test: "test custom payload"\n                request_option:\n                    type: RequestOption\n                    inject_into: body_json\n                    field_name: authorization\n            -expected1]

♻️ This comment has been updated with latest results.

@brianjlai brianjlai marked this pull request as ready for review September 30, 2025 03:46
@brianjlai brianjlai requested a review from maxi297 September 30, 2025 03:46
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

📝 Walkthrough

Walkthrough

Adds RequestOption-driven JWT injection to declarative components. JwtAuthenticator now accepts an optional request_option to control injection target/key, exposes getters for params/body/json, derives header key from request options, schema/models gain InjectInto/RequestOption, parser wires request_option, and tests updated.

Changes

Cohort / File(s) Summary
JWT Authenticator implementation
airbyte_cdk/sources/declarative/auth/jwt.py
Adds optional request_option field; computes injection mappings for headers/params/body based on RequestOption; adds accessors get_request_params, get_request_body_data, get_request_body_json; derives auth header key from request options; initializes default RequestOption when missing; adds internal helper to assemble options.
Declarative schema (models + YAML)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py, airbyte_cdk/sources/declarative/declarative_component_schema.yaml
Introduces InjectInto enum and RequestOption model; updates JwtAuthenticator model to include optional request_option; YAML schema adds JwtAuthenticator.request_option referencing RequestOption.
Parser wiring
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py
Converts create_jwt_authenticator to an instance method; extracts request_option from model when present and passes it to JwtAuthenticator; also propagates request_option to ListPartitionRouter.
Unit tests — JWT
unit_tests/sources/declarative/auth/test_jwt.py
Adds parameterized tests validating get_request_params/get_request_body_data/get_request_body_json and header generation for various inject_into targets and custom header keys; verifies JWT encoding matches authenticator internals.
Unit tests — Parser
unit_tests/sources/declarative/parsers/test_model_to_component_factory.py
Updates tests to assert request_option in manifest propagates to JwtAuthenticator and that inject_into value maps correctly.
Project metadata
pyproject.toml
Bumps dagger-io dependency from 0.18.6 to 0.19.0.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Factory as ModelToComponentFactory
  participant Auth as JwtAuthenticator
  participant ReqOpt as RequestOption
  participant HTTP as HTTP Request

  Client->>Factory: Build components from manifest
  Factory->>Auth: new JwtAuthenticator(..., request_option)
  Note over Auth: Ensure default RequestOption if absent

  Client->>Auth: Prepare request
  Auth->>Auth: Generate JWT (payload, headers)
  Auth->>ReqOpt: Resolve injection target & key
  alt inject_into == header
    Auth->>HTTP: get_auth_header() -> {key: "Authorization" or custom, value: "Bearer <jwt>"}
  else inject_into == request_parameter
    Auth->>HTTP: get_request_params() -> {field_name: "<jwt>"}
  else inject_into == body_data
    Auth->>HTTP: get_request_body_data() -> {field_name: "<jwt>"} or string
  else inject_into == body_json
    Auth->>HTTP: get_request_body_json() -> {field_name: "<jwt>"}
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • @aldogonzalez8 — would you review the parser + model changes, wdyt?
  • @bazarnov — could you review the JWT auth changes and tests, wdyt?

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title clearly conveys the primary enhancement—making the JWT injection point configurable via request_option—and follows conventional commit style, providing sufficient context and specificity about the main change.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch brian/jwt_authenticator_configurable_request_option

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8fb6b0 and 0ce9630.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1 hunks)
  • pyproject.toml (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-11T16:34:46.319Z
Learnt from: pnilan
PR: airbytehq/airbyte-python-cdk#0
File: :0-0
Timestamp: 2024-12-11T16:34:46.319Z
Learning: In the airbytehq/airbyte-python-cdk repository, the `declarative_component_schema.py` file is auto-generated from `declarative_component_schema.yaml` and should be ignored in the recommended reviewing order.

Applied to files:

  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py
🧬 Code graph analysis (1)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py (4)
airbyte_cdk/sources/declarative/auth/jwt.py (1)
  • JwtAuthenticator (55-251)
airbyte_cdk/sources/declarative/requesters/request_option.py (1)
  • RequestOption (25-117)
airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py (1)
  • token_expiry_date_format (76-81)
airbyte_cdk/sources/streams/http/requests_native_auth/oauth.py (1)
  • token_expiry_date_format (131-132)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: Check: source-pokeapi
  • GitHub Check: Check: destination-motherduck
  • GitHub Check: Check: source-intercom
  • GitHub Check: Check: source-shopify
  • GitHub Check: Check: source-hardcoded-records
  • GitHub Check: Pytest (All, Python 3.10, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.11, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.12, Ubuntu)
  • GitHub Check: Pytest (All, Python 3.13, Ubuntu)
  • GitHub Check: Manifest Server Docker Image Build
  • GitHub Check: preview_docs
  • GitHub Check: SDM Docker Image Build
  • GitHub Check: Pytest (Fast)
🔇 Additional comments (2)
pyproject.toml (1)

123-123: LGTM - Dev dependency upgrade.

The dagger-io version bump to 0.19.0 looks good. Based on your earlier comment about build issues, I hope this upgrade helps resolve those problems, wdyt?

airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1)

1-3: Auto-generated file - changes align with PR objectives.

Based on learnings, this file is auto-generated from declarative_component_schema.yaml. The new InjectInto enum (lines 1093-1098), RequestOption model (lines 1100-1120), and the request_option field added to JwtAuthenticator (lines 1751-1755) all align with the PR's goal of making JWT token injection configurable. Since this is generated code, the real review should focus on the source YAML file.

Based on learnings


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
unit_tests/sources/declarative/parsers/test_model_to_component_factory.py (1)

3037-3040: Consider validating the field_name as well?

The test adds a request_option with field_name: authorization, but lines 3148-3151 only check inject_into. Should we also assert that authenticator._request_option.field_name.eval(config) == "authorization" to ensure the field name is correctly propagated? This would make the test more thorough, wdyt?

Apply this diff to add field_name validation:

     if authenticator_manifest.get("request_option"):
         assert authenticator._request_option.inject_into.value == authenticator_manifest.get(
             "request_option", {}
         ).get("inject_into")
+        assert authenticator._request_option.field_name.eval(config) == authenticator_manifest.get(
+            "request_option", {}
+        ).get("field_name")
unit_tests/sources/declarative/auth/test_jwt.py (2)

293-354: Fix the duplicated test IDs for clarity.

The test IDs at lines 303, 310, and 317 are all set to "test_get_request_headers", but this test is actually test_get_request_options. This makes it harder to identify which parametrized case failed. Could you update them to something like "inject_into_request_parameter", "inject_into_body_data", and "inject_into_body_json" to better reflect what's being tested? Wdyt?

Apply this diff:

             pytest.param(
                 RequestOption(
                     inject_into=RequestOptionType.request_parameter,
                     field_name="custom_parameter",
                     parameters={},
                 ),
                 "custom_parameter",
-                id="test_get_request_headers",
+                id="inject_into_request_parameter",
             ),
             pytest.param(
                 RequestOption(
                     inject_into=RequestOptionType.body_data, field_name="custom_body", parameters={}
                 ),
                 "custom_body",
-                id="test_get_request_headers",
+                id="inject_into_body_data",
             ),
             pytest.param(
                 RequestOption(
                     inject_into=RequestOptionType.body_json, field_name="custom_json", parameters={}
                 ),
                 "custom_json",
-                id="test_get_request_headers",
+                id="inject_into_body_json",
             ),

356-394: Consider a more descriptive test ID for the custom header case.

The test ID at line 366 is "test_get_request_headers", which just repeats the test name. For consistency with the second case ("test_with_default_authorization_header"), maybe something like "test_with_custom_authorization_header" would be clearer? Just a thought!

Apply this diff:

             pytest.param(
                 RequestOption(
                     inject_into=RequestOptionType.header,
                     field_name="custom_authorization",
                     parameters={},
                 ),
                 "custom_authorization",
-                id="test_get_request_headers",
+                id="test_with_custom_authorization_header",
             ),
airbyte_cdk/sources/declarative/auth/jwt.py (1)

227-228: The auth_header implementation is clever but could use a clarifying comment.

As noted in the past review comment, returning an empty string when not injecting into headers is a bit implicit—it relies on the caller (get_auth_header()) treating empty string as falsy. While this reuses the existing abstract method, it might be worth adding a brief comment explaining why an empty string is returned in the non-header case. This would help future readers understand the intention. Wdyt?

For example:

@property
def auth_header(self) -> str:
    # Returns the header key if injecting into headers, otherwise returns ""
    # which causes get_auth_header() to return an empty dict
    options = self._get_request_options(RequestOptionType.header)
    return next(iter(options.keys()), "")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25132b6 and f8fb6b0.

📒 Files selected for processing (6)
  • airbyte_cdk/sources/declarative/auth/jwt.py (5 hunks)
  • airbyte_cdk/sources/declarative/declarative_component_schema.yaml (1 hunks)
  • airbyte_cdk/sources/declarative/models/declarative_component_schema.py (2 hunks)
  • airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2 hunks)
  • unit_tests/sources/declarative/auth/test_jwt.py (2 hunks)
  • unit_tests/sources/declarative/parsers/test_model_to_component_factory.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
unit_tests/sources/declarative/auth/test_jwt.py (3)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py (2)
  • RequestOption (360-379)
  • JwtAuthenticator (429-491)
airbyte_cdk/sources/declarative/requesters/request_option.py (1)
  • RequestOptionType (13-21)
airbyte_cdk/sources/declarative/auth/jwt.py (7)
  • JwtAuthenticator (55-251)
  • _get_jwt_payload (154-178)
  • _get_secret_key (180-199)
  • _get_jwt_headers (135-152)
  • get_request_params (238-239)
  • get_request_body_data (241-242)
  • get_request_body_json (244-245)
airbyte_cdk/sources/declarative/auth/jwt.py (2)
airbyte_cdk/sources/declarative/auth/declarative_authenticator.py (1)
  • DeclarativeAuthenticator (14-29)
airbyte_cdk/sources/declarative/requesters/request_option.py (2)
  • RequestOptionType (13-21)
  • inject_into_request (71-117)
airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1)
  • JwtAuthenticator (429-491)
airbyte_cdk/sources/declarative/auth/jwt.py (1)
  • JwtAuthenticator (55-251)
airbyte_cdk/sources/declarative/models/declarative_component_schema.py (1)
airbyte_cdk/sources/declarative/requesters/request_option.py (1)
  • RequestOption (25-117)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Check: source-shopify
🔇 Additional comments (10)
airbyte_cdk/sources/declarative/declarative_component_schema.yaml (1)

1279-1282: LGTM! Clean addition of the request_option field.

The new request_option property is well-defined and appropriately optional, ensuring backward compatibility. The description clearly communicates its purpose, and the reference to RequestOption is correct. I can see that RequestOption (defined at lines 2530-2573) supports all the necessary injection targets including body_json, which addresses the Box connector use case mentioned in the PR objectives. Nice work!

unit_tests/sources/declarative/parsers/test_model_to_component_factory.py (1)

3148-3151: Incorrect review comment: default-case already tested The default Authorization header injection is covered by the pytest.param(None, "Authorization", id="test_with_default_authorization_header") in test_get_request_headers of unit_tests/sources/declarative/auth/test_jwt.py.

Likely an incorrect or invalid review comment.

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py (2)

2686-2714: LGTM! The request_option propagation looks good.

The method now correctly instantiates a RequestOption component from the model when provided and passes it through to JwtAuthenticator. The change from staticmethod to instance method is necessary and appropriate for accessing _create_component_from_model. This enables the new per-request injection feature while maintaining backward compatibility. Nice work!


2716-2730: Consistent request_option handling for ListPartitionRouter.

The method follows the same pattern as create_jwt_authenticator, conditionally creating and passing a request_option to ListPartitionRouter. This consistency makes the codebase easier to understand and maintain.

airbyte_cdk/sources/declarative/auth/jwt.py (3)

92-92: Great backward-compatible default for request_option!

The new request_option field with its default initialization ensures that existing connectors continue to work without any changes (injecting into the Authorization header). The explanatory comment is helpful for future maintainers. Nice design choice!

Also applies to: 128-133


238-245: Clean implementation of the authenticator interface methods.

The new get_request_params, get_request_body_data, and get_request_body_json methods properly implement the DeclarativeAuthenticator interface by delegating to the shared _get_request_options helper. This keeps the code DRY and makes the logic easy to follow.


247-251: Excellent separation of concerns in the injection logic.

The _get_request_options helper cleanly encapsulates the conditional injection logic. It only populates the options mapping when the target matches the configured inject_into type, which keeps the interface methods simple and the behavior predictable. Well done!

airbyte_cdk/sources/declarative/models/declarative_component_schema.py (3)

353-357: LGTM! The InjectInto enum values align with the runtime RequestOptionType.

The four injection targets (request_parameter, header, body_data, body_json) match the runtime implementation expectations.


360-379: Consider adding schema-level validation for field_name/field_path mutual exclusivity?

The runtime RequestOption class (in request_option.py) enforces that exactly one of field_name or field_path must be provided, and that field_path only works with body_json injection. Currently, the schema model allows both to be None or both to be set, which would fail at runtime.

Would it make sense to add a Pydantic validator here to catch these issues earlier during manifest parsing, or is the intent to keep the schema simple and rely on runtime validation? The current approach works, but early validation might provide a better developer experience. wdyt?


486-490: LGTM! The request_option field addition enables flexible JWT injection.

The optional request_option field is well-documented and maintains backward compatibility. The description clearly explains that it controls where the JWT token is injected into outbound requests.

Copy link
Contributor

@maxi297 maxi297 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@brianjlai brianjlai merged commit 7ab013d into main Sep 30, 2025
28 of 29 checks passed
@brianjlai brianjlai deleted the brian/jwt_authenticator_configurable_request_option branch September 30, 2025 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants