Skip to content

Slack provider with rotating user tokens - #1

Merged
n1hility merged 5 commits into
forge/v0.0.116-rhaiv.0from
slack-rotation/jg
Sep 26, 2026
Merged

n1hility merged 5 commits into
forge/v0.0.116-rhaiv.0from
slack-rotation/jg

Conversation

@n1hility

@n1hility n1hility commented Sep 25, 2026 •

Copy link
Copy Markdown

Summary

Carried patch for Slack token rotation on the Forge OpenShell image. Mirrors upstream NVIDIA#3730 (issue NVIDIA#3729); the fork keeps its built-in profile catalog, so the profile lands as a built-in here rather than an example.

Commits: fix(server) classify OAuth token errors returned with HTTP 2xx (Slack's oauth.v2.access reports failures as 200 + error; previously a permanent oauth_invalid_success_response loop, now parked as reauthorization_required with the Slack name as the subtype; issuer table gated to 2xx envelopes so 4xx classification is unchanged) · feat(providers) built-in slack profile (43200 s cap, 21600 s lead, client_id/client_secret/refresh_token material) + tighter adc_credential · fix(providers) profile is read-only (Slack reads accept GET) with corrected docs.

Verification

Live on saw-jasongreene with a gateway built from this branch (tag v0.0.116-rhaiv.0-forge.2 was cut at 8037354; the later commits are the 2xx gate, a clippy fix and the read-only profile, so re-cut forge.3 after merge): connect-time, manual and worker-driven rotations succeeded with the read proxy serving throughout; after uninstalling the Slack app a forced refresh parked as reauthorization_required / oauth_invalid_grant / invalid_refresh_token.

Tests: provider_refresh 57, profile handlers 118, openshell-providers 131, CLI --from-gcloud-adc tests; fmt and clippy clean on the touched crates.

RFC 6749 section 5.2 puts token endpoint errors on 4xx responses, but
some issuers answer HTTP 200 with an `error` body. Slack's oauth.v2.access
does this for every failure, so a rotated-out or revoked refresh token
left the refresh state in a permanent `oauth_invalid_success_response`
loop: retried every minute, never parked, and never surfaced to the
operator as a reauthorization requirement.

When a 2xx body does not parse as a token response but does parse as an
OAuth error response, route it through the existing error classifier.
Bodies that carry a token are handled exactly as before, and bodies with
neither a token nor an `error` field still report
`oauth_invalid_success_response`.

Fold the Slack token endpoint error vocabulary onto the closest RFC
failure codes so status consumers keep a stable vocabulary, preserving
the issuer's own name as the provider error subtype in the same way the
Google `invalid_rapt` and `admin_policy_enforced` cases already do.
Terminal outcomes stay gated on the grant kind: user refresh grants park
for reauthorization, non-interactive grants fall back to configuration
repair.

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
Add a built-in `slack` provider profile for Slack apps that have token
rotation enabled. Slack issues user access tokens that expire after
43200 seconds together with a single-use refresh token, and requires the
app client ID and client secret on every refresh, so the profile
declares an `oauth2_refresh_token` credential against oauth.v2.access
with a matching 43200-second lifetime cap, a 21600-second refresh lead,
and client_id / client_secret / refresh_token material.

Slack Web API methods are POST even for reads, so the endpoint uses the
read-write method preset; deployers add `rules` for the methods their
workloads need.

Tighten `ProviderTypeProfile::adc_credential` to require the Google
token endpoint in addition to the three ADC material keys, so that
`--from-gcloud-adc` keeps refusing profiles whose refresh material
merely has the same shape.

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
Consult the issuer-specific error table only when the error arrived in a
2xx envelope, so RFC 6749 error responses keep the RFC-only
classification they had before and the change stays confined to the
branch that previously reported `oauth_invalid_success_response`. Add a
regression test for an issuer-named error on a 400 response.

Drop the manual `refresh rotate` step from the Slack provider docs: the
worker may mint concurrently and Slack refresh tokens are single use.
State that a used refresh token is invalidated without asserting a
grace period.

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
Slack's read methods accept GET with query-string arguments, and the
token travels in the injected authorization header rather than as a
query parameter, so the read-only method preset covers reads. Grant
read-only access in the built-in profile and state that write methods
such as chat.postMessage need an explicit rule or a read-write copy.
Drop the incorrect claim that Slack reads require POST. Describe where
the Slack error name surfaces (the provider_error_subtype field of the
refresh status API, echoed in LAST_ERROR).

Mirrors the follow-up on the upstream submission (NVIDIA#3730).

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
@n1hility
n1hility marked this pull request as ready for review September 26, 2026 04:21
@n1hility
n1hility merged commit b65f116 into forge/v0.0.116-rhaiv.0 Sep 26, 2026
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.

1 participant