You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR adds authentication parity between CyteTypeR and the Python client. It introduces browser-based PKCE authentication, shared credential storage, the cytetyper terminal command, authenticated API requests, development URL overrides, and updated documentation and tests.
Summary
Add SetupCyteTypeR(), LoginCyteTypeR(), and LogoutCyteTypeR().
Add dashboard and job report helpers.
Add cytetyper commands for setup, login, logout, dashboard, and job viewing.
Share credentials with the Python client through the same local credentials file.
Add PKCE, callback state validation, atomic credential writes, and restrictive permissions.
Support production defaults and explicit development URLs.
Apply authentication to submission, upload, polling, and result retrieval.
Add Unix and Windows CLI launchers.
Update README, vignettes, rendered guides, and function references.
Update package version to 0.4.3.
Authentication flow
sequenceDiagram
actor User
participant Client as CyteTypeR or cytetyper
participant Callback as Local callback server
participant Browser
participant API as CyteType API
participant Store as Shared credentials file
User->>Client: Start setup
Client->>Client: Generate PKCE verifier, challenge, and state
Client->>Callback: Listen on a random 127.0.0.1 port
Client->>Browser: Open authorization URL
Browser->>API: Email OTP sign-in and consent
API-->>Callback: Redirect with authorization code and state
Callback->>Callback: Validate state
Callback->>API: Exchange code and PKCE verifier
API-->>Callback: Return API token and account metadata
Callback-->>Client: Complete authentication
Client->>Store: Save credentials atomically
Loading
Client architecture
flowchart LR
CLI[cytetyper CLI] --> Functions[CyteTypeR authentication functions]
RClient[CyteTypeR submission functions] --> Resolver[API URL and token resolver]
Functions --> Resolver
Resolver <--> Credentials[Shared credentials.json]
Python[Python CyteType client] <--> Credentials
Resolver --> API[CyteType API]
API --> Reports[Dashboard and job reports]
R/api.R now disables httr2 HTTP error conditions for all non-2xx responses, so
.stop_if_rate_limited() can no longer access e$resp and will not emit the dedicated
RATE_LIMIT_EXCEEDED guidance. Users will instead get a generic HTTP error string for 429 responses,
reducing debuggability and changing prior behavior.
The new req_error(is_error = function(resp) FALSE) prevents httr2 from emitting httr2_http
errors for HTTP 429, but .stop_if_rate_limited() only triggers when it receives an httr2_http
with e$resp; therefore the specialized RATE_LIMIT_EXCEEDED message can no longer be produced for
.api_response_helper() calls.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
### Issue description
`R/api.R:.api_response_helper()` now uses `httr2::req_error(is_error = function(resp) FALSE)`, preventing `httr2` from raising `httr2_http` conditions on HTTP failures (including 429). The existing rate-limit handler `.stop_if_rate_limited()` only triggers when it catches an `httr2_http` with `e$resp`, so throttling responses lose the custom “RATE_LIMIT_EXCEEDED” guidance.
### Issue Context
- `.api_response_helper()` still calls `.stop_if_rate_limited(e)` in its `tryCatch` error handler, but for HTTP 429 there is no thrown `httr2_http` anymore—only a normal response object followed by `stop("HTTP ...")`.
- The repo already has a dedicated rate-limit UX in `R/errors.R:.stop_if_rate_limited()`; the goal is to preserve that behavior.
### Fix Focus Areas
- R/api.R[42-76]
- R/errors.R[36-47]
### Suggested implementation direction
Pick one:
1) **Call the rate-limit logic before `stop()`** inside the non-2xx branch in `.api_response_helper()`:
- After `parsed <- .parse_server_error(response)`, if `parsed$error_code == "RATE_LIMIT_EXCEEDED"`, raise the same message currently produced by `.stop_if_rate_limited()` (including the “Use your own LLM API key…” guidance).
2) **Stop disabling httr2 errors for 429**:
- Change `req_error(is_error=...)` to return `TRUE` for statuses you want httr2 to throw (e.g., 429), but keep returning `FALSE` for statuses you intentionally handle as normal responses (401/403/404). Then update `.api_response_helper()` to catch `httr2_http` and inspect `e$resp` similarly to the previous behavior.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper
• Introduces browser-based PKCE sign-in and shared credential storage with the Python client.
• Adds the cytetyper terminal CLI plus Unix/Windows launchers and an installer function.
• Applies authenticated requests across submission, uploads, polling, and result retrieval.
• Adds secure, atomic credential writes with strict permissions and state validation.
• Updates defaults (CYTETYPE_API_URL support), docs/vignettes, and expands test coverage.
Diagram
graph TD
CLI["cytetyper CLI"] --> AuthMod["Auth functions"] --> Callback["Callback server"] --> API{{"CyteType API"}}
AuthMod --> Creds[("credentials.json")]
Client["CyteTypeR client"] --> Resolver["Token/URL resolver"] --> Creds --> API
Python{{"Python client"}} --> Creds
subgraph Legend
direction LR
_mod["Module"] ~~~ _db[("File")] ~~~ _ext{{"External"}}
end
Loading
High-Level Assessment
Using a loopback redirect + PKCE is a standard, secure pattern for CLI/desktop auth, and a shared credentials file is the simplest way to achieve parity with the Python client without introducing OS-specific keychain integrations or a background credential service. Device-code flow or platform keychains were plausible alternatives but would increase platform complexity or require additional server-side support.
Files changed (33) +3819 / -242
Enhancement (8) +1696 / -24
NAMESPACEExport authentication and CLI entrypoints+6/-0
Export authentication and CLI entrypoints
• Exports SetupCyteTypeR/LoginCyteTypeR/LogoutCyteTypeR, dashboard/job helpers, and InstallCyteTypeRCli so users can authenticate and install/use the CLI.
api.RPreserve 401/403 as typed authentication errors+11/-3
Preserve 401/403 as typed authentication errors
• Disables httr2 default error raising for API calls and maps 401/403 responses into .stop_authentication() errors (cytetype_auth_error). Improves error propagation for auth failures.
auth.RImplement PKCE browser auth and secure credential storage+1039/-0
Implement PKCE browser auth and secure credential storage
• Adds the full auth subsystem: API URL validation, shared credentials path resolution, atomic credential writes with strict permissions, PKCE + state generation/validation, a local httpuv callback app, and helper URLs for dashboard/job viewing. Introduces exported user functions for setup/login/logout and opening dashboard/job URLs.
cli.RAdd cytetyper CLI dispatcher plus installer+351/-0
Add cytetyper CLI dispatcher plus installer
• Implements cytetyper command parsing and dispatch (setup/get-key/login/logout/dashboard/view and --version). Adds InstallCyteTypeRCli() to install an OS-appropriate launcher alongside the active Rscript.
cytetype.RResolve auth token/URL and strengthen GetResults origin handling+78/-21
Resolve auth token/URL and strengthen GetResults origin handling
• Uses .resolve_api_url() for consistent origin handling and .resolve_auth_token() to require credentials by default for authenticated endpoints. Extends GetResults() with api_url support and enforces origin matching when an explicit auth_token is supplied for stored jobs.
cli_callback.htmlAdd callback HTML template for browser auth completion+180/-0
Add callback HTML template for browser auth completion
• Provides the local callback landing page with placeholders for status/message/email/dashboard URL and a timed redirect. Designed to avoid token/code disclosure and to apply strict security headers.
• Documents browser sign-in, manual API-key login, cytetyper installation/usage, and development URL overrides. Updates example report URLs to the production domain.
configurations.mdDocument API URL precedence and auth workflows+93/-23
Document API URL precedence and auth workflows
• Restructures the configuration guide to cover API origin resolution, development environment guidance, authentication functions/CLI commands, and LLM credential hygiene.
DESCRIPTIONBump version and add auth dependencies+5/-2
Bump version and add auth dependencies
• Adds askpass, httpuv, and openssl imports required for browser login, local callback serving, and PKCE. Bumps package version to 0.4.3 and updates roxygen config metadata.
zzz.RUpdate default API URL and support CYTETYPE_API_URL overrides+14/-7
Update default API URL and support CYTETYPE_API_URL overrides
• Stops overwriting user-supplied options on load, changes the default origin to https://cytetype.nygen.io, and adds precedence for the CYTETYPE_API_URL environment variable.
test-cytetype-build-upload.RCover auth behavior in build/upload/submission workflow+145/-1
Cover auth behavior in build/upload/submission workflow
• Updates fixtures and adds tests ensuring missing credentials fail early, stored credentials are used when auth_token is NULL, explicit tokens override stored credentials, and completed workflows store results retrievable via GetResults().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds authentication parity between CyteTypeR and the Python client. It introduces browser-based PKCE authentication, shared credential storage, the
cytetyperterminal command, authenticated API requests, development URL overrides, and updated documentation and tests.Summary
SetupCyteTypeR(),LoginCyteTypeR(), andLogoutCyteTypeR().cytetypercommands for setup, login, logout, dashboard, and job viewing.0.4.3.Authentication flow
sequenceDiagram actor User participant Client as CyteTypeR or cytetyper participant Callback as Local callback server participant Browser participant API as CyteType API participant Store as Shared credentials file User->>Client: Start setup Client->>Client: Generate PKCE verifier, challenge, and state Client->>Callback: Listen on a random 127.0.0.1 port Client->>Browser: Open authorization URL Browser->>API: Email OTP sign-in and consent API-->>Callback: Redirect with authorization code and state Callback->>Callback: Validate state Callback->>API: Exchange code and PKCE verifier API-->>Callback: Return API token and account metadata Callback-->>Client: Complete authentication Client->>Store: Save credentials atomicallyClient architecture
flowchart LR CLI[cytetyper CLI] --> Functions[CyteTypeR authentication functions] RClient[CyteTypeR submission functions] --> Resolver[API URL and token resolver] Functions --> Resolver Resolver <--> Credentials[Shared credentials.json] Python[Python CyteType client] <--> Credentials Resolver --> API[CyteType API] API --> Reports[Dashboard and job reports]User-facing commands
SetupCyteTypeR() LoginCyteTypeR() LogoutCyteTypeR() OpenCyteTypeDashboard() ViewCyteTypeJob("<JOB_ID>")Development environments can provide their assigned URL without embedding internal URLs:
cytetyper setup --api-url "<URL>"