Skip to content

feat: support client_credentials flow for opkssh#520

Open
gautierrog wants to merge 3 commits into
openpubkey:mainfrom
gautierrog:feat/implement-client-credentials-flow
Open

feat: support client_credentials flow for opkssh#520
gautierrog wants to merge 3 commits into
openpubkey:mainfrom
gautierrog:feat/implement-client-credentials-flow

Conversation

@gautierrog
Copy link
Copy Markdown

@gautierrog gautierrog commented Apr 29, 2026

Add the support of client_credentials flow

Adds explicit support for the OAuth2 client_credentials flow for machine-to-machine usage, alongside the existing authorization_code flow.
From a user API perspective, it extends CLI and configuration formats with a new auth_flow field (in --provider, OPKSSH_PROVIDERS, and YAML config)
Runtime behavior is also adjusted: in client_credentials mode, no browser is opened, client_secret becomes mandatory.

@gautierrog
Copy link
Copy Markdown
Author

This is the first draft attempting to support client credentials flow to opkssh as previously discussed here: #373

Corresponding PR on openpubkey here: openpubkey/openpubkey#366

@EthanHeilman
Copy link
Copy Markdown
Member

Add this line to your go.mod so it points to the changes you made in your openpubkey PR. I'll merge the openpubkey PR first and then we can change this back, but this allow us to run tests against your openpubkey PR in this PR

replace github.com/openpubkey/openpubkey v0.23.0 => github.com/gautierrog/openpubkey v0.0.0-20260421123902-e85fbcabf40e

@gautierrog gautierrog force-pushed the feat/implement-client-credentials-flow branch from 1d098dc to cc48949 Compare May 4, 2026 08:22
@gautierrog
Copy link
Copy Markdown
Author

It's done, I also added a more detailed description of the PR on first comment.
It seems that integration test are in timeout, I hardly see that related to the changes (since most of them pass anyway). If you think otherwise I can look more into it.

extraField := strings.TrimSpace(parts[4])
if extraField != "" {
if strings.Contains(extraField, " ") {
// Backward compatibility for strings that accidentally had one extra comma
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.

We should probably fail here and throw an error than attempt to guess as guessing could result in confusing behavior.

strings.HasPrefix(p.Issuer, "https://gitlab.com") ||
p.Issuer == "https://issuer.hello.coop" ||
strings.HasPrefix(p.Issuer, "https://token.actions.githubusercontent.com") {
return nil, fmt.Errorf("client credentials flow is only supported for generic providers (for example keycloak) and not provider shortcuts")
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.

Why is client credentials flow only supported for generic providers? Google for instance has confidential clients that use the client secret to authenticate as the client. Is client credentials here referring to something other than confidential clients?

Comment thread policy/providerloader.go
}

func (p ProvidersRow) ToString() string {
if p.AuthFlow != "" && p.AuthFlow != AuthFlowAuthorizationCode {
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.

Is there a reason this checks for != p.AuthFlow != AuthFlowAuthorizationCode? If p.AuthFlow is AuthFlowAuthorizationCode shouldn't the return in the if block work?

Comment thread policy/providerloader.go
flow := strings.TrimSpace(p.AuthFlow)
if flow == "" ||
strings.EqualFold(flow, AuthFlowAuthorizationCode) ||
strings.EqualFold(flow, AuthFlowClientCredentials) {
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.

Why not have an AuthFlow enum to catch this?

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.

2 participants