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
{{ message }}
Repository navigation
bug(cli): client-credentials gateway login stops working when the access token expires #4287
I use OpenShell from automation (scripts and CI-style jobs) that authenticate to an OIDC-protected gateway with a confidential service-account client. I log in once with openshell gateway login <name> using OPENSHELL_OIDC_CLIENT_SECRET, then run a sequence of CLI commands. Once the first access token expires, every CLI command fails until I log in again. This breaks any job that runs longer than the provider's access token lifetime.
Problem Statement
When OPENSHELL_OIDC_CLIENT_SECRET is set, openshell gateway login uses the OAuth 2.0 client credentials grant. Client credentials token responses normally carry no refresh token, so the stored token has only an access token and an expiry.
When that access token nears expiry, the CLI tries to renew it with the refresh token grant only. With no refresh token stored, renewal fails with no refresh token available. Once the token has actually expired, the CLI fails closed and asks the user to run openshell gateway login again, even though OPENSHELL_OIDC_CLIENT_SECRET is still in the environment and a new token could be obtained without user interaction.
Code references (unchanged between 8719fc9 and main at 3fc93e2):
crates/openshell-cli/src/oidc_auth.rs:268oidc_client_credentials_flow obtains the token; the resulting bundle has refresh_token: None (see the doc comment on OidcTokenBundle in crates/openshell-bootstrap/src/oidc_token.rs:21).
crates/openshell-cli/src/oidc_auth.rs:472-481oidc_refresh_token returns no refresh token available when refresh_token is None. There is no client credentials fallback.
crates/openshell-cli/src/main.rs:181-226 command auth setup calls oidc_refresh_token when the token is within 30 seconds of expiry; after real expiry it returns OIDC token refresh failed: ... run openshell gateway login <name>.
The same refresh-only path is used by ensure_valid_oidc_token_bundle (oidc_auth.rs:517-545, called from commands/provider.rs:914) and by shell completion (completers.rs:145).
The SDKs already renew client credentials tokens by repeating the grant (#2803, #2907). The CLI does not.
Impact / Why This Matters
Automation that uses the CLI with a service account has to run openshell gateway login <name> before every command, or wrap commands in retry logic that detects this error and logs in again. Each extra login is an additional token request to the identity provider and an extra step that scripts must get right.
In the 30 seconds before expiry, commands print Warning: OIDC token refresh failed; continuing with the cached token, which looks like a transient problem rather than a missing capability. After expiry, long-running jobs fail partway through even though their credentials are valid. The error text also suggests an interactive re-login, which is misleading for a non-interactive client.
Acceptance Criteria
When the stored token came from a client credentials login and OPENSHELL_OIDC_CLIENT_SECRET is set, the CLI obtains a new access token with the client credentials grant before or at expiry, stores it, and the command succeeds without a manual gateway login.
Renewal reuses the gateway's registered issuer, client ID, audience, and scopes.
The client secret is never written to the token cache, metadata, or logs.
If the token came from client credentials but OPENSHELL_OIDC_CLIENT_SECRET is no longer set, the error says the secret is required to renew, instead of no refresh token available.
Browser and device-code logins keep their current refresh token behavior.
The same renewal applies to every CLI path that renews OIDC tokens (command auth, provider commands, shell completion).
A test covers an expired client credentials bundle being renewed without a refresh token.
Reproduction Steps
Prerequisites: an OpenShell gateway with OIDC authentication enabled, and any OIDC provider that supports the client credentials grant. Configure a confidential client with service accounts enabled and a short access token lifetime (for example 5 minutes) whose tokens the gateway accepts.
Register the gateway with OIDC, using the confidential client ID:
Output includes Authenticated via client credentials.
Run openshell sandbox list. It succeeds.
Wait until the access token lifetime has passed (about 5 minutes in this setup), keeping OPENSHELL_OIDC_CLIENT_SECRET set.
Run openshell sandbox list again.
Expected: the CLI renews the token with client credentials and the command succeeds.
Actual: the command fails with OIDC token refresh failed: no refresh token available and asks for openshell gateway login cc-test.
Suggested UX
No new flags are needed. With the secret in the environment, renewal is silent:
export OPENSHELL_OIDC_CLIENT_SECRET=<client-secret>
openshell gateway login cc-test
# ...more than one token lifetime later...
openshell sandbox list # succeeds, token renewed via client credentials
Without the secret:
Error: OIDC access token for gateway 'cc-test' expired. It was issued via client credentials;
set OPENSHELL_OIDC_CLIENT_SECRET to renew it automatically, or run `openshell gateway login cc-test`.
Environment
OpenShell CLI built from commit 8719fc9 (openshell --version reports 0.1.3-dev). Relevant code is unchanged on main at 3fc93e2.
Gateway with OIDC authentication; identity provider is Keycloak with a confidential service-account client (any client-credentials-capable provider should reproduce).
Access token lifetime: 5 minutes.
Logs
Error: OIDC token refresh failed: no refresh token available — re-authenticate with: openshell gateway login
cached OIDC token has expired; run `openshell gateway login cc-test`
Within 30 seconds before expiry:
Warning: OIDC token refresh failed; continuing with the cached token: no refresh token available — re-authenticate with: openshell gateway login
User Story
I use OpenShell from automation (scripts and CI-style jobs) that authenticate to an OIDC-protected gateway with a confidential service-account client. I log in once with
openshell gateway login <name>usingOPENSHELL_OIDC_CLIENT_SECRET, then run a sequence of CLI commands. Once the first access token expires, every CLI command fails until I log in again. This breaks any job that runs longer than the provider's access token lifetime.Problem Statement
When
OPENSHELL_OIDC_CLIENT_SECRETis set,openshell gateway loginuses the OAuth 2.0 client credentials grant. Client credentials token responses normally carry no refresh token, so the stored token has only an access token and an expiry.When that access token nears expiry, the CLI tries to renew it with the refresh token grant only. With no refresh token stored, renewal fails with
no refresh token available. Once the token has actually expired, the CLI fails closed and asks the user to runopenshell gateway loginagain, even thoughOPENSHELL_OIDC_CLIENT_SECRETis still in the environment and a new token could be obtained without user interaction.Code references (unchanged between 8719fc9 and main at 3fc93e2):
crates/openshell-cli/src/oidc_auth.rs:268oidc_client_credentials_flowobtains the token; the resulting bundle hasrefresh_token: None(see the doc comment onOidcTokenBundleincrates/openshell-bootstrap/src/oidc_token.rs:21).crates/openshell-cli/src/oidc_auth.rs:472-481oidc_refresh_tokenreturnsno refresh token availablewhenrefresh_tokenisNone. There is no client credentials fallback.crates/openshell-cli/src/main.rs:181-226command auth setup callsoidc_refresh_tokenwhen the token is within 30 seconds of expiry; after real expiry it returnsOIDC token refresh failed: ... run openshell gateway login <name>.ensure_valid_oidc_token_bundle(oidc_auth.rs:517-545, called fromcommands/provider.rs:914) and by shell completion (completers.rs:145).The SDKs already renew client credentials tokens by repeating the grant (#2803, #2907). The CLI does not.
Impact / Why This Matters
Automation that uses the CLI with a service account has to run
openshell gateway login <name>before every command, or wrap commands in retry logic that detects this error and logs in again. Each extra login is an additional token request to the identity provider and an extra step that scripts must get right.In the 30 seconds before expiry, commands print
Warning: OIDC token refresh failed; continuing with the cached token, which looks like a transient problem rather than a missing capability. After expiry, long-running jobs fail partway through even though their credentials are valid. The error text also suggests an interactive re-login, which is misleading for a non-interactive client.Acceptance Criteria
OPENSHELL_OIDC_CLIENT_SECRETis set, the CLI obtains a new access token with the client credentials grant before or at expiry, stores it, and the command succeeds without a manualgateway login.OPENSHELL_OIDC_CLIENT_SECRETis no longer set, the error says the secret is required to renew, instead ofno refresh token available.Reproduction Steps
Prerequisites: an OpenShell gateway with OIDC authentication enabled, and any OIDC provider that supports the client credentials grant. Configure a confidential client with service accounts enabled and a short access token lifetime (for example 5 minutes) whose tokens the gateway accepts.
Authenticated via client credentials.openshell sandbox list. It succeeds.OPENSHELL_OIDC_CLIENT_SECRETset.openshell sandbox listagain.Expected: the CLI renews the token with client credentials and the command succeeds.
Actual: the command fails with
OIDC token refresh failed: no refresh token availableand asks foropenshell gateway login cc-test.Suggested UX
No new flags are needed. With the secret in the environment, renewal is silent:
Without the secret:
Environment
openshell --versionreports 0.1.3-dev). Relevant code is unchanged on main at 3fc93e2.Logs
Within 30 seconds before expiry: