Repository navigation
Device authorization flow never sends client_secret, so netbird ssh / netbird login fail against Google #8113
Unanswered
Donemmanuelo
asked this question in
Issue Triage
Replies: 1 comment 3 replies
|
Hello @Donemmanuelo we recommend migrating to integrated DEX mode. Check our docs on that below: https://docs.netbird.io/selfhosted/migration/external-to-embedded-idp Sending client secret will be deprecated and direct google idp will be marked as not recommended anymore. |
3 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Describe the problem
With a self-hosted (split) deployment whose
DeviceAuthorizationFlowuses Googledirectly (
Provider: "hosted", a TVs and Limited Input devices OAuth client,ClientSecretset), the device flow can never complete. The device code is issued, butthe very first token poll is rejected by Google, before the user has approved anything:
DeviceAuthorizationFlow.requestToken(client/internal/auth/device_flow.go) builds thetoken form with only
client_id,grant_typeanddevice_code. The secret isconfigured in
management.json, sent by management inGetDeviceAuthorizationFlow, andstored by the client in
DeviceAuthProviderConfig.ClientSecret— but never put on thewire. Google documents
client_secretas a parameter of the device token poll(https://developers.google.com/identity/protocols/oauth2/limited-input-device#step-4:-poll-googles-authorization-server).
The PKCE flow is not affected: it passes the secret through
oauth2.Config, which is whythe same deployment works from macOS/Windows or a Linux desktop session. It only bites
where
shouldUseDeviceFlowpicks the device flow — headless Linux/FreeBSD, i.e. servers.To reproduce
DeviceAuthorizationFlow:Provider: "hosted",ClientID/Audience= a GoogleTVs and Limited Input devices client,
ClientSecret= its secret.netbird ssh root@<peer> whoami.Expected behavior
When the device-flow provider config carries a client secret, the token poll includes
it; when it does not, nothing changes for public clients (Dex, Keycloak, Zitadel, ...).
Proposed fix
Three lines in
requestToken, guarded so public clients are unaffected:I have this with a unit test that fails without the change, and verified it end-to-end
against Google with a patched v0.80.0 client:
netbird sshcompleted withAuthentication successful!and ran the remote command (for that test I also widenedthe JWT wait locally — see the 30-second point below). Happy to open the PR once the
direction is agreed.
I am aware
ProviderConfig.client_secretis deprecated in the proto and the docs nowrecommend the embedded IdP with Google as a connector. If the intended answer is
"migrate to the embedded IdP", it would help to say so in the legacy Google docs and to
stop advertising a device flow that cannot work — today the secret is accepted, sent to
the client, and silently dropped.
Smaller, related: Google's device-code response uses
verification_url(not RFC 8628'sverification_uri), soAuthFlowInfounmarshals an empty URL and the CLI prints a blanklogin link. The user code still works at
https://www.google.com/device. I have aseparate small fix for that too (RFC names keep precedence), also tested live.
Also related, and IdP-independent:
dialWithJWT(client/ssh/client/client.go) derivesthe JWT context from the SSH dial timeout (
ssh.ClientConfig.Timeout, 30 s), so thewhole device flow — including the human approving the code on another device — must
finish within 30 seconds or the CLI fails with
DeadlineExceededwhile the daemon isstill polling. The device code itself is valid for 30 minutes.
Environment
netbirdio/netbirdimage), LinuxmainAll reactions