Skip to content

Add Device Authorization Grant (RFC 8628) for headless/container environments - #916

Open
mickume wants to merge 5 commits into
jumpstarter-dev:mainfrom
mickume:main
Open

Add Device Authorization Grant (RFC 8628) for headless/container environments#916
mickume wants to merge 5 commits into
jumpstarter-dev:mainfrom
mickume:main

Conversation

@mickume

@mickume mickume commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Add device_authorization_grant() method to the OIDC Config class implementing RFC 8628 Device Authorization Grant flow. This enables login from containerized IDEs (e.g., OpenShift Dev Spaces) where localhost callbacks are unreachable.

Changes:

  • Add JMP_OIDC_DEVICE_FLOW env var constant to env.py
  • Add device_authorization_grant() to Config in oidc.py with full RFC 8628
    compliance: device code request, verification URI display, token polling
    with authorization_pending/slow_down/expired_token/access_denied handling
  • Add --device-flow CLI flag to opt_oidc decorator
  • Add should_use_device_flow() helper for flag + env var detection
  • Wire device flow selection in login() and relogin_client() in login.py
  • Add comprehensive tests for device flow grant, polling, error handling,
    flow selection logic, and env var auto-detection

mickume added 2 commits July 22, 2026 14:48
…eadless/container environments

Add device_authorization_grant() method to the OIDC Config class implementing
RFC 8628 Device Authorization Grant flow. This enables login from containerized
IDEs (e.g., OpenShift Dev Spaces) where localhost callbacks are unreachable.

Changes:
- Add JMP_OIDC_DEVICE_FLOW env var constant to env.py
- Add device_authorization_grant() to Config in oidc.py with full RFC 8628
  compliance: device code request, verification URI display, token polling
  with authorization_pending/slow_down/expired_token/access_denied handling
- Add --device-flow CLI flag to opt_oidc decorator
- Add should_use_device_flow() helper for flag + env var detection
- Wire device flow selection in login() and relogin_client() in login.py
- Add comprehensive tests for device flow grant, polling, error handling,
  flow selection logic, and env var auto-detection
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds OAuth 2.0 device authorization selected by CLI flag or environment variable. Login and relogin use device flow when enabled, while expired-token handling now performs one automatic reauthentication retry.

Changes

OIDC device authorization

Layer / File(s) Summary
Device-flow selection contract
python/packages/jumpstarter/jumpstarter/config/env.py, python/packages/jumpstarter-cli-common/.../oidc.py, python/packages/jumpstarter-cli-common/.../oidc_test.py
Adds the environment constant, CLI option, selection helper, and precedence tests.
Device authorization grant
python/packages/jumpstarter-cli-common/.../oidc.py, python/packages/jumpstarter-cli-common/.../oidc_test.py
Implements discovery, device-code requests, verification output, polling, timeout handling, TLS behavior, and OAuth errors.
Login flow integration
python/packages/jumpstarter-cli/jumpstarter_cli/login.py, python/packages/jumpstarter-cli/jumpstarter_cli/login_test.py
Selects device authorization for login and relogin when enabled, with authorization-code fallback coverage.

Automatic reauthentication retry

Layer / File(s) Summary
Reauthentication retry control flow
python/packages/jumpstarter-cli-common/.../exceptions.py
Retries the original operation once after successful expired-token reauthentication and reports failures through CLI exceptions.
Reauthentication validation
python/packages/jumpstarter-cli-common/.../exceptions_test.py, python/packages/jumpstarter-cli/jumpstarter_cli/shell_test.py
Covers successful retry, reauthentication failure, retry bounds, and shell execution after token replacement.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Config
  participant OIDCProvider
  CLI->>Config: select device authorization grant
  Config->>OIDCProvider: request device code
  OIDCProvider-->>Config: verification URI and user code
  Config->>CLI: display verification details
  Config->>OIDCProvider: poll token endpoint
  OIDCProvider-->>Config: return token or device-flow error
  Config-->>CLI: complete login
Loading

Possibly related PRs

Suggested reviewers: mangelajo

Poem

A bunny logs in by moonlit code,
Device tokens hop the road.
Poll till approval’s shining bright,
Reauth retries through the night.
OIDC ears perk with delight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding RFC 8628 device authorization grant support for headless/container environments.
Description check ✅ Passed The description is directly related to the changeset and accurately outlines the new device-flow support and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py (1)

227-274: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider adding request timeouts on the device-flow HTTP calls.

Neither the device-authorization POST (Line 229) nor the token-poll POST (Line 266) sets a timeout. The overall deadline loop bounds the polling cadence, but an individual hung connection can still block past the intended expiry. Passing an aiohttp.ClientTimeout to the session or per-request timeout= would harden this against a stalled IdP.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py` around
lines 227 - 274, Update the OIDC device-flow HTTP calls in the session around
the device authorization POST and token-poll POST to apply an
aiohttp.ClientTimeout, either on ClientSession or per request. Ensure both
requests can terminate when the identity provider connection stalls, while
preserving the existing polling deadline behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py`:
- Around line 227-274: Update the OIDC device-flow HTTP calls in the session
around the device authorization POST and token-poll POST to apply an
aiohttp.ClientTimeout, either on ClientSession or per request. Ensure both
requests can terminate when the identity provider connection stalls, while
preserving the existing polling deadline behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e55cd38-afb4-4c13-b317-fce6d017268f

📥 Commits

Reviewing files that changed from the base of the PR and between e0b0cba and e68c961.

📒 Files selected for processing (5)
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc_test.py
  • python/packages/jumpstarter-cli/jumpstarter_cli/login.py
  • python/packages/jumpstarter-cli/jumpstarter_cli/login_test.py
  • python/packages/jumpstarter/jumpstarter/config/env.py

…ixes #18)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mangelajo

Copy link
Copy Markdown
Member

@mickume The e2e failures is unrelated, I wonder if there is a way to test this in E2E with our dex setup, or modified dex setup.

@mangelajo

Copy link
Copy Markdown
Member

@mickume: we also need to update the docs, explaining how to use this feature for login

…ehavior

The previous fix correctly implemented retry-after-reauth in the decorator
but left shell_test.py::test_expired_token_triggers_reauth asserting the
old "Please try again now" error message. Update the test to simulate
successful re-authentication (login_mock updates the token) and verify
that the retried command succeeds without raising an exception.

- fix(#17, nightshift): suppress noisy warnings and retry transparently on token refresh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/packages/jumpstarter-cli-common/jumpstarter_cli_common/exceptions.py`:
- Around line 261-301: Update _try_reauth_or_handle so _ReauthSucceeded is
always caught: return True when allow_reauth is enabled, and raise
ClickExceptionRed("Unexpected re-auth signal") from None when it is disabled.
Preserve the existing handler invocation behavior and ensure BaseExceptionGroup
retries surface the normal ClickExceptionRed instead of leaking the internal
signal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ea4cae8-3895-4587-b976-f5a83676d1ee

📥 Commits

Reviewing files that changed from the base of the PR and between 15f4b80 and 09be0b8.

📒 Files selected for processing (5)
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/exceptions.py
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/exceptions_test.py
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc_test.py
  • python/packages/jumpstarter-cli/jumpstarter_cli/shell_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc.py
  • python/packages/jumpstarter-cli-common/jumpstarter_cli_common/oidc_test.py

Comment on lines +261 to +301
def _try_reauth_or_handle(handler, exc, login_func, *, allow_reauth: bool) -> bool:
"""Attempt re-auth via *handler*; return True if re-auth succeeded.

When *allow_reauth* is False the handler is called without catching
``_ReauthSucceeded`` — any re-auth signal propagates as a normal error.
"""
if allow_reauth:
try:
handler(exc, login_func)
except _ReauthSucceeded:
return True
else:
handler(exc, login_func)
return False


def _call_with_exception_handling(func, args, kwargs, login_func, *, allow_reauth: bool):
"""Call *func* and handle exceptions, optionally allowing re-authentication.

Returns ``(result, needs_retry)`` where *needs_retry* is ``True`` when
re-authentication succeeded and the caller should retry the call.
"""
try:
return func(*args, **kwargs), False
except _ReauthSucceeded:
raise ClickExceptionRed("Unexpected re-auth signal") from None
except BaseExceptionGroup as eg:
if _try_reauth_or_handle(_handle_exception_group_with_reauth, eg, login_func, allow_reauth=allow_reauth):
return None, True
except (ConnectionError, JumpstarterException, click.ClickException) as e:
if isinstance(e, ConnectionError) and not allow_reauth:
raise ClickExceptionRed(str(e)) from None
if _try_reauth_or_handle(_handle_single_exception_with_reauth, e, login_func, allow_reauth=allow_reauth):
return None, True
except Exception as e:
_raise_if_mappable(e)
raise
except KeyboardInterrupt as e:
_raise_if_mappable(e)
raise
return None, False # pragma: no cover — handlers above always raise or return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

_ReauthSucceeded can leak out uncaught when the retry hits a BaseExceptionGroup.

_try_reauth_or_handle's allow_reauth=False branch calls handler(exc, login_func) without catching _ReauthSucceeded (Line 272-273). For the single-exception path this is safe only because _call_with_exception_handling short-circuits ConnectionError before allow_reauth=False ever reaches the handler (Line 291-292: if isinstance(e, ConnectionError) and not allow_reauth: raise ClickExceptionRed(...)). No equivalent guard exists for the BaseExceptionGroup branch (Line 287-289): if the retry (allow_reauth=False) raises a BaseExceptionGroup whose leaf is an "expired" ConnectionError, _handle_exception_group_with_reauth_handle_connection_error_with_reauth (Line 218-233) still calls login_func again and raises _ReauthSucceeded(), which now propagates uncaught out of wrapped() as a raw internal exception instead of a click.ClickException. This also breaks the "retries the original call exactly once... surfaced normally" guarantee documented on Line 308-310, since a leaked _ReauthSucceeded is not a normal, user-facing error.

The cleanest fix is to always catch _ReauthSucceeded in _try_reauth_or_handle and convert it to a proper error when re-auth isn't allowed, rather than duplicating the ConnectionError-specific short-circuit for every call site.

🐛 Proposed fix
 def _try_reauth_or_handle(handler, exc, login_func, *, allow_reauth: bool) -> bool:
     """Attempt re-auth via *handler*; return True if re-auth succeeded.
 
-    When *allow_reauth* is False the handler is called without catching
-    ``_ReauthSucceeded`` — any re-auth signal propagates as a normal error.
+    When *allow_reauth* is False, a ``_ReauthSucceeded`` signal raised by the
+    handler is converted into a ``ClickExceptionRed`` instead of retrying
+    again, guaranteeing at most one re-auth attempt regardless of whether the
+    triggering exception arrived standalone or inside a ``BaseExceptionGroup``.
     """
-    if allow_reauth:
-        try:
-            handler(exc, login_func)
-        except _ReauthSucceeded:
-            return True
-    else:
-        handler(exc, login_func)
-    return False
+    try:
+        handler(exc, login_func)
+    except _ReauthSucceeded:
+        if allow_reauth:
+            return True
+        raise ClickExceptionRed(str(exc)) from None
+    return False

Consider adding a test that raises a BaseExceptionGroup wrapping an expired-token ConnectionError on the retried call, to lock in this fix. As per coding guidelines, "Provide comprehensive package test coverage, prioritizing end-to-end tests that start a server and client; use mocks when system tools, services, or platform compatibility make end-to-end testing impractical."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _try_reauth_or_handle(handler, exc, login_func, *, allow_reauth: bool) -> bool:
"""Attempt re-auth via *handler*; return True if re-auth succeeded.
When *allow_reauth* is False the handler is called without catching
``_ReauthSucceeded``any re-auth signal propagates as a normal error.
"""
if allow_reauth:
try:
handler(exc, login_func)
except _ReauthSucceeded:
return True
else:
handler(exc, login_func)
return False
def _call_with_exception_handling(func, args, kwargs, login_func, *, allow_reauth: bool):
"""Call *func* and handle exceptions, optionally allowing re-authentication.
Returns ``(result, needs_retry)`` where *needs_retry* is ``True`` when
re-authentication succeeded and the caller should retry the call.
"""
try:
return func(*args, **kwargs), False
except _ReauthSucceeded:
raise ClickExceptionRed("Unexpected re-auth signal") from None
except BaseExceptionGroup as eg:
if _try_reauth_or_handle(_handle_exception_group_with_reauth, eg, login_func, allow_reauth=allow_reauth):
return None, True
except (ConnectionError, JumpstarterException, click.ClickException) as e:
if isinstance(e, ConnectionError) and not allow_reauth:
raise ClickExceptionRed(str(e)) from None
if _try_reauth_or_handle(_handle_single_exception_with_reauth, e, login_func, allow_reauth=allow_reauth):
return None, True
except Exception as e:
_raise_if_mappable(e)
raise
except KeyboardInterrupt as e:
_raise_if_mappable(e)
raise
return None, False # pragma: no cover — handlers above always raise or return
def _try_reauth_or_handle(handler, exc, login_func, *, allow_reauth: bool) -> bool:
"""Attempt re-auth via *handler*; return True if re-auth succeeded.
When *allow_reauth* is False, a ``_ReauthSucceeded`` signal raised by the
handler is converted into a ``ClickExceptionRed`` instead of retrying
again, guaranteeing at most one re-auth attempt regardless of whether the
triggering exception arrived standalone or inside a ``BaseExceptionGroup``.
"""
try:
handler(exc, login_func)
except _ReauthSucceeded:
if allow_reauth:
return True
raise ClickExceptionRed(str(exc)) from None
return False
def _call_with_exception_handling(func, args, kwargs, login_func, *, allow_reauth: bool):
"""Call *func* and handle exceptions, optionally allowing re-authentication.
Returns ``(result, needs_retry)`` where *needs_retry* is ``True`` when
re-authentication succeeded and the caller should retry the call.
"""
try:
return func(*args, **kwargs), False
except _ReauthSucceeded:
raise ClickExceptionRed("Unexpected re-auth signal") from None
except BaseExceptionGroup as eg:
if _try_reauth_or_handle(_handle_exception_group_with_reauth, eg, login_func, allow_reauth=allow_reauth):
return None, True
except (ConnectionError, JumpstarterException, click.ClickException) as e:
if isinstance(e, ConnectionError) and not allow_reauth:
raise ClickExceptionRed(str(e)) from None
if _try_reauth_or_handle(_handle_single_exception_with_reauth, e, login_func, allow_reauth=allow_reauth):
return None, True
except Exception as e:
_raise_if_mappable(e)
raise
except KeyboardInterrupt as e:
_raise_if_mappable(e)
raise
return None, False # pragma: no cover — handlers above always raise or return
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/packages/jumpstarter-cli-common/jumpstarter_cli_common/exceptions.py`
around lines 261 - 301, Update _try_reauth_or_handle so _ReauthSucceeded is
always caught: return True when allow_reauth is enabled, and raise
ClickExceptionRed("Unexpected re-auth signal") from None when it is disabled.
Preserve the existing handler invocation behavior and ensure BaseExceptionGroup
retries surface the normal ClickExceptionRed instead of leaking the internal
signal.

Source: Coding guidelines

@mangelajo

Copy link
Copy Markdown
Member

@mickume can you have an eye on the python linter?

# import chain (authlib._joserfc_helpers -> authlib.jose). The project already
# uses joserfc directly for JWT operations; authlib is only needed for
# OAuth2Session. The warning provides no actionable information to end users.
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"authlib\.")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍

@mangelajo mangelajo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you @mickume just a couple of nits, mostly the reorg of noqa imports, the other two are mostly nits/being overly protecting with bad server responses I'd say.

Some comments were AI assisted, but corrected, many removed by a human still in the loop :)

Comment on lines +268 to +273
verification_uri = device_data.get("verification_uri")
user_code = device_data.get("user_code")
click.echo(
f"To sign in, open the following URL in your browser:\n\n {verification_uri}\n\n"
f"Then enter the code: {user_code}\n"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not really important, mostly a nit, because this means the server is having some sort of error in the response: If the server omits both verification_uri_complete and verification_uri, the user will see:

To sign in, open the following URL in your browser:

  None

Then enter the code: None

This silently displays garbage instead of failing with a clear error. Consider validating these fields, e.g.:

verification_uri = device_data.get("verification_uri")
user_code = device_data.get("user_code")
if not verification_uri or not user_code:
    raise click.ClickException(
        "Device authorization response is missing required fields "
        "(verification_uri / user_code). The identity provider may not "
        "fully support RFC 8628."
    )

(Same concern applies to device_data["device_code"] on line 259 — a KeyError from a non-compliant server would surface as an unhelpful traceback, though this is lower priority since device_code is a REQUIRED field per RFC 8628 and the pattern is consistent with other grant methods.)

Comment on lines +316 to +318
raise click.ClickException(
"Device authorization timed out waiting for user approval. Please try again."
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This client-side polling timeout path is not covered by tests. The existing test_raises_on_expired_token covers the server responding with {"error": "expired_token"}, which is a different code path (line 301-303).

To cover this, you could add a test with expires_in: 0 (or mock time.monotonic) so the while loop exits immediately and this raise is reached:

@pytest.mark.asyncio
async def test_raises_on_polling_timeout(self) -> None:
    config = Config(issuer="https://auth.example.com", client_id="test")
    discovery = {
        "token_endpoint": "https://auth.example.com/token",
        "device_authorization_endpoint": "https://auth.example.com/device",
    }
    device_response_data = {
        "device_code": "test-device-code",
        "user_code": "ABCD-EFGH",
        "verification_uri": "https://auth.example.com/device",
        "interval": 0.01,
        "expires_in": 0,  # immediate expiry
    }
    # ... mock session that always returns authorization_pending ...
    with pytest.raises(click.ClickException, match="timed out"):
        await config.device_authorization_grant()

Comment on lines +24 to +28
from authlib.integrations.requests_client import OAuth2Session # noqa: E402
from joserfc.jws import extract_compact # noqa: E402
from yarl import URL # noqa: E402

from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT, JMP_OIDC_DEVICE_FLOW # noqa: E402

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Only the authlib import needs to be after the filterwarnings call. The joserfc, yarl, and jumpstarter.config.env imports are unrelated to the authlib warning and can stay in the normal import section, avoiding three unnecessary noqa: E402 markers:

from joserfc.jws import extract_compact
from yarl import URL

from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT, JMP_OIDC_DEVICE_FLOW

# Suppress AuthlibDeprecationWarning emitted during the authlib import chain.
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"authlib\.")
from authlib.integrations.requests_client import OAuth2Session  # noqa: E402

Comment on lines +18 to +28
# Suppress AuthlibDeprecationWarning emitted unconditionally during the authlib
# import chain (authlib._joserfc_helpers -> authlib.jose). The project already
# uses joserfc directly for JWT operations; authlib is only needed for
# OAuth2Session. The warning provides no actionable information to end users.
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"authlib\.")

from authlib.integrations.requests_client import OAuth2Session # noqa: E402
from joserfc.jws import extract_compact # noqa: E402
from yarl import URL # noqa: E402

from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT, JMP_OIDC_DEVICE_FLOW # noqa: E402

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Only the authlib import needs to sit after filterwarnings. Move the unrelated imports back to the normal section to drop three unnecessary noqa: E402 markers:

Suggested change
# Suppress AuthlibDeprecationWarning emitted unconditionally during the authlib
# import chain (authlib._joserfc_helpers -> authlib.jose). The project already
# uses joserfc directly for JWT operations; authlib is only needed for
# OAuth2Session. The warning provides no actionable information to end users.
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"authlib\.")
from authlib.integrations.requests_client import OAuth2Session # noqa: E402
from joserfc.jws import extract_compact # noqa: E402
from yarl import URL # noqa: E402
from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT, JMP_OIDC_DEVICE_FLOW # noqa: E402
from joserfc.jws import extract_compact
from yarl import URL
from jumpstarter.config.env import JMP_OIDC_CALLBACK_PORT, JMP_OIDC_DEVICE_FLOW
# Suppress AuthlibDeprecationWarning emitted unconditionally during the authlib
# import chain (authlib._joserfc_helpers -> authlib.jose). The project already
# uses joserfc directly for JWT operations; authlib is only needed for
# OAuth2Session. The warning provides no actionable information to end users.
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"authlib\.")
from authlib.integrations.requests_client import OAuth2Session # noqa: E402

@raballew

Copy link
Copy Markdown
Member

@mickume needs rebasing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants