netbirdio / netbird

Connect your devices into a secure WireGuard®-based overlay network with SSO, MFA and granular access controls.
https://netbird.io
BSD 3-Clause "New" or "Revised" License
10.72k stars 482 forks source link

Cannot log in on mobile device when setting `AuthUserIDClaim=preferred_username`, potentially invalid OIDC usage #2057

Open oddlama opened 4 months ago

oddlama commented 4 months ago

Describe the problem

I want to use preferred_username as the AuthUserIDClaim so that I can identify the accounts in my instance. Currently I'm using kanidm as my IDP and the sub value leads to randomly generated UUIDs, which makes it impossible to easily distinguish accounts:

image

Correct me if I am wrong, but it looks to me like the AuthUserIDClaim determines the name for accounts. I would have expected it to be named AuthUsernameClaim or something like that since the sub claim from the OIDC spec should always be used for the ID.

Now there are two issues:

  1. I'm not sure why the email address is not populated, since the requested oauth scopes are openid profile email, so the information must be available
  2. When trying to login from a mobile device, it starts the oauth flow and after successfully logging in the app stays in Connecting... for around 30 seconds and finally fails with an error. The server log then shows unable to fetch account with claims, err: user ID is empty:

    May 26 16:15:11 netbird netbird-mgmt[837]: 2024-05-26T16:15:11+02:00 WARN management/server/grpcserver.go:348: failed validating JWT token sent from peer y1ParZkbzVMQGeU/KMycYl75v90i2O6EwgO1YQZnSFs= with error rpc error: code = Internal desc = unable to fetch account with claims, err: user ID is empty. Trying again as it may be due to the IdP cache iss>

    This message repeats several times. As soon as I remove AuthUserIDClaim from my config (or set it to sub) it will start working as expected without errors.

Logging in on the webinterface still works fine, showing the correct user name:

image

But I noticed that this will count as a new account, which is weird because the sub claim didn't change. Section 5.7 of the core OIDC spec states: "The sub (subject) and iss (issuer) Claims, used together, are the only Claims that an RP can rely upon as a stable identifier for the End-User, [...] other Claims such as email, phone_number, and preferred_username MUST NOT be used as unique identifiers for the End-User."

I might be misunderstanding what netbird does internally, but it seems to me as if it uses AuthUserIDClaim as both the identifier and username. This would essentially force users to always set this attribute to sub because all other claims would not be stable.

To Reproduce

Steps to reproduce the behavior:

  1. Set AuthUserIDClaim to preferred_username
  2. Login in web interface (username is correct, login works)
  3. Try to login on android device using the same account
  4. Errors with unable to fetch account with claims, err: user ID is empty
  5. Also notice that the email is missing.

Expected behavior

  1. The login should succeed on mobile too
  2. The login should lead to the same account (same sub claim), but the username should reflect the setting
  3. The email should be set / updated to the primary email from the email oidc scope

Are you using NetBird Cloud?

No, using self-hosted.

NetBird version

0.27.7

Additional context

My `management.json` file ```json { "DataStoreEncryptionKey": "---REDACTED---", "Datadir": "/var/lib/netbird-mgmt/data", "DeviceAuthorizationFlow": { "Provider": "none", "ProviderConfig": { "Audience": "netbird", "ClientID": "netbird", "DeviceAuthEndpoint": "", "Domain": null, "Scope": "openid profile email", "TokenEndpoint": null, "UseIDToken": false } }, "HttpConfig": { "Address": "127.0.0.1:8011", "AuthAudience": "netbird", "AuthUserIDClaim": "preferred_username", "IdpSignKeyRefreshEnabled": true, "OIDCConfigEndpoint": "https://mykanidminstance.example.com/oauth2/openid/netbird/.well-known/openid-configuration" }, "IdpManagerConfig": { "Auth0ClientCredentials": null, "AzureClientCredentials": null, "ClientConfig": { "ClientID": "netbird", "ClientSecret": "", "GrantType": "client_credentials", "Issuer": "", "TokenEndpoint": "" }, "ExtraConfig": {}, "KeycloakClientCredentials": null, "ManagerType": "none", "ZitadelClientCredentials": null }, "PKCEAuthorizationFlow": { "ProviderConfig": { "Audience": "netbird", "AuthorizationEndpoint": "", "ClientID": "netbird", "ClientSecret": "", "RedirectURLs": [ "http://localhost:53000" ], "Scope": "openid profile email", "TokenEndpoint": "", "UseIDToken": false } }, "ReverseProxy": { "TrustedHTTPProxies": [], "TrustedHTTPProxiesCount": 0, "TrustedPeers": [ "0.0.0.0/0" ] }, "Signal": { "Password": null, "Proto": "https", "URI": "mynetbirdinstance.example.com:443", "Username": "" }, "StoreConfig": { "Engine": "sqlite" }, "Stuns": [ { "Password": null, "Proto": "udp", "URI": "stun:mycoturn.example.com:3478", "Username": "" } ], "TURNConfig": { "CredentialsTTL": "12h", "Secret": "--REDACTED--", "TimeBasedCredentials": false, "Turns": [ { "Password": "--REDACTED--", "Proto": "udp", "URI": "turn:mycoturn.example.com:5349", "Username": "netbird" } ] } } ```
bcmmbaga commented 4 months ago

Hello @oddlama,

  1. I'm not sure why the email address is not populated, since the requested oauth scopes are openid profile email, so the information must be available

NetBird does not store detailed user information except for the UserID. To populate user data such as the username and email address, NetBird retrieves this information from the Identity Provider (IdP) via REST API calls, as OIDC does not support fetching a list of user accounts directly.

Currently, NetBird supports fetching user data from IdPs like Auth0, Azure, Okta, Google Workspace, JumpCloud, Zitadel, Authentik, and Keycloak. Unfortunately, your IdP, Kanidm, is not yet supported. However, you can bridge your account with any of the supported IdPs mentioned above to ensure that user information, including email addresses, is correctly populated.

  1. When trying to login from a mobile device, it starts the oauth flow and after successfully logging in the app stays in Connecting... for around 30 seconds and finally fails with an error. The server log then shows unable to fetch account with claims, err: user ID is empty:

NetBird relies on the AuthUserIDClaim internally to extract the userID from the access token to identify the user account. This means that the AuthUserIDClaim must be set to the correct claim that contains the unique identifier of the user in your IdP. By default the AuthUserIDClaim will be set to sub.

In your case, it appears that the AuthUserIDClaim is either empty or incorrectly set, which is why the server log shows "unable to fetch account with claims, err: user ID is empty." Please ensure that the AuthUserIDClaim is configured to the correct claim containing the userID in your IdP settings.

oddlama commented 4 months ago

Currently, NetBird supports fetching user data from IdPs like Auth0, Azure, Okta, Google Workspace, JumpCloud, Zitadel, Authentik, and Keycloak. Unfortunately, your IdP, Kanidm, is not yet supported. However, you can bridge your account with any of the supported IdPs mentioned above to ensure that user information, including email addresses, is correctly populated.

Interesting, so netbird basically wants to pre-populate the list of all users? Sounds like a lot of work to maintain a specific implementation for each IDP. Is there a chance to get a agnostic OIDC-compatible mode, which populates user data on first login instead? That would have the downside of not being able to see all users immediately but at least their information would be available regardelss of which IDP is in use, without requiring netbird to write any more integrations.

NetBird relies on the AuthUserIDClaim internally to extract the userID from the access token to identify the user account. This means that the AuthUserIDClaim must be set to the correct claim that contains the unique identifier of the user in your IdP. By default the AuthUserIDClaim will be set to sub.

I see, this is kind of what I assumed. But then I don't really understand why this setting exists, because the OIDC spec quoted above basically requires netbird to use the sub claim as an identifier. Anything else is not stable and could change. So from my limited understanding, changing this setting sounds like making netbird violate the OIDC spec. Am I missing something essential here? When is that option used in a real world scenario?

In your case, it appears that the AuthUserIDClaim is either empty or incorrectly set, which is why the server log shows "unable to fetch account with claims, err: user ID is empty." Please ensure that the AuthUserIDClaim is configured to the correct claim containing the userID in your IdP settings.

So this means that the preferred_username is not present the oauth response by the flow initiated by the mobile device. But the IDP doesn't care whether a mobile device or web browser makes the request, so that leads me to believe that there is something different about the flow initiated on the mobile device, because it only fails there. Do you have any leads on what could be different?


A final question: How does netbird link user accounts? Is there a separate concept of usernames and user ids, or is the username the same as the user id? I'd like netbird to use sub as the id for identifying accounts but still get a humanly readable name for it from the preferred_username claim on first login.

bcmmbaga commented 4 months ago

Interesting, so netbird basically wants to pre-populate the list of all users? Sounds like a lot of work to maintain a specific implementation for each IDP. Is there a chance to get a agnostic OIDC-compatible mode, which populates user data on first login instead? That would have the downside of not being able to see all users immediately but at least their information would be available regardelss of which IDP is in use, without requiring netbird to write any more integrations.

Yes, for self-hosted, it will populate the list of all users allowed to have access in IdP. At the moment, you can open a feature request for support of OIDC-compatible mode.

I see, this is kind of what I assumed. But then I don't really understand why this setting exists, because the OIDC spec quoted above basically requires netbird to use the sub claim as an identifier. Anything else is not stable and could change. So from my limited understanding, changing this setting sounds like making netbird violate the OIDC spec. Am I missing something essential here? When is that option used in a real world scenario?

This is because different IdPs have varying behaviors and support for access tokens and ID tokens. For instance, to ensure Netbird works correctly with Azure AD (Microsoft Entra ID), the AuthUserIDClaim must be set to oid, which maps the correct user ID from the ID token

So this means that the preferred_username is not present the oauth response by the flow initiated by the mobile device. But the IDP doesn't care whether a mobile device or web browser makes the request, so that leads me to believe that there is something different about the flow initiated on the mobile device, because it only fails there. Do you have any leads on what could be different?

There is a possibility that this is not set, as NetBird extracts these claims from the token generated by your IdP. Unfortunately, at the moment, I don't have any specific leads on what might be causing this difference

A final question: How does netbird link user accounts? Is there a separate concept of usernames and user ids, or is the username the same as the user id? I'd like netbird to use sub as the id for identifying accounts but still get a humanly readable name for it from the preferred_username claim on first login.

Netbird relies on the user ID to link accounts from your IdP. The username and user ID could be the same or different, again, this depends on the specific IdP implementation. However, in the majority of cases, the username represents the user's full name, while the user ID serves as the unique identifier for the user within your IdP

NexZhu commented 4 months ago

Netbird is searching for AuthUserIDClaim (e.g., name, email) in access tokens, but these fields only present in ID tokens, Netbird should extract them from ID tokens.

bcmmbaga commented 4 months ago

Netbird is searching for AuthUserIDClaim (e.g., name, email) in access tokens, but these fields only present in ID tokens, Netbird should extract them from ID tokens.

NetBird currently extracts only the userID from the token. Other user attributes, such as name and email, are not extracted from the token. Instead, these attributes are retrieved directly from the Identity Provider (IdP).

bcmmbaga commented 4 months ago

@oddlama You can switch to the ID Token by configuring NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN=true and NETBIRD_TOKEN_SOURCE=idToken in your setup.env file. However, it's essential to note that this change will require recreating the DB

oddlama commented 4 months ago

@bcmmbaga Thanks, thats great to know! I'll test that later