Documentation
¶
Overview ¶
Package credentials stores and resolves the OAuth token of one profile: access token, refresh token, expiry and type.
Resolution order:
- GITLAB_MCP_REFRESH_TOKEN in the environment (CI, automation).
- The OS keyring — Secret Service on Linux, Keychain on macOS, Credential Manager on Windows — keyed by the profile name.
- A file under the profile directory, restricted to the current account (0600, or an ACL on Windows), written only when the keyring was unavailable, and warned about on every use.
A missing keyring entry falls through quietly. A broken keyring (no session bus, no secret service) also falls through, so a headless machine still works, and its error is reported if nothing else is found.
GitLab rotates the refresh token on every refresh and revokes the old one at once (§2.3), so a refresh token from the environment works exactly once. The pair it rotates into is saved to the keyring or the file stamped with a hash of the variable's value, and while the variable still holds that value the stored descendant is used in its place. Without that, the second start after a refresh would present a spent token.
Index ¶
Constants ¶
const EnvVar = "GITLAB_MCP_REFRESH_TOKEN"
EnvVar is the environment override.
const ServiceName = "gitlab-mcp"
ServiceName is the keyring service identifier.
Variables ¶
var ErrKeyringSilent = errors.New("credentials: the profile records a token in the OS keyring and the " +
"keyring returned nothing. It is more likely locked or unreachable than empty: unlock it and try again. " +
"`gitlab-mcp login` writes a fresh token, which works around this rather than fixing it")
ErrKeyringSilent is what a keyring that answers "nothing" looks like when the profile says a token was stored in it.
The two are not the same and the advice differs. A missing token is fixed by logging in. A keyring that has the token and will not hand it over — locked, or a session bus that cannot reach the daemon — is fixed by unlocking it, and logging in again writes a second token beside the first without addressing the cause.
var ErrNotFound = errors.New("credentials: no token found; run `gitlab-mcp login`")
ErrNotFound means no token is stored anywhere.
Functions ¶
This section is empty.
Types ¶
type Backend ¶
type Backend interface {
Get(service, account string) (string, error)
Set(service, account, secret string) error
Delete(service, account string) error
}
Backend is the keyring contract. Tests substitute an in-memory one.
type Store ¶
type Store struct {
Profile string
Keyring Backend
FilePath string
Env func(string) string
// Warn receives human-readable warnings; the plaintext fallback is
// announced through it on every use rather than once at login.
Warn func(string)
// ExpectKeyring says the profile records that a token was saved to
// the keyring. It changes only the error: with it, a keyring that
// answers "nothing" is reported as a keyring that will not answer
// rather than as a token that was never there.
ExpectKeyring bool
// contains filtered or unexported fields
}
Store resolves and saves the token for one profile.
func (*Store) Delete ¶
Delete removes the token from every store and reports every failure. Missing entries are fine. The environment is not a store.
func (*Store) Resolve ¶
Resolve returns the token and where it came from.
The environment wins, except over a stored pair rotated from the very value it holds: that pair is the environment token's live successor. A token from the environment carries only the refresh token, so the first call refreshes it.
func (*Store) ResolveStored ¶
ResolveStored returns the token from the keyring or the file, ignoring the environment override: it is the token logout can revoke and delete.
func (*Store) Save ¶
Save stores the token in the keyring, or in the file when the keyring fails, and reports where it landed. A successful keyring save removes a stale plaintext copy, so there is one source of truth.
While the environment override is set, the saved pair is stamped as its successor (see Resolve); a pair saved without it is not.