[GH-ISSUE #5658] JWT Group Sync fails with AWS Cognito (Embedded IdP stringifies array claims) #11840

Open
opened 2026-08-05 01:31:16 -04:00 by saavagebueno · 0 comments
Owner

Originally created by @gerardpujol8 on GitHub (Mar 23, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5658

Description

When using AWS Cognito as a Custom OIDC provider in the self-hosted Combined Setup, JWT group synchronization fails.

Cognito sends the cognito:groups claim as a valid JSON array of strings (e.g., ["Administrators"]). However, the Embedded IdP (Dex), which cannot be disabled in the combined setup, seems to stringify this custom, non-standard OIDC claim during token translation into a plain string: "[\"Administrators\"]".

As a result, the ToGroups function in shared/auth/jwt/extractor.go fails the type assertion claim.([]interface{}), silently drops the groups, and logs a debug error.

To Reproduce

Steps to reproduce the behavior:

Deploy a self-hosted NetBird instance using the Combined Setup (config.yaml).

Go to 'Settings' -> 'Authentication' and add AWS Cognito as a Custom OIDC provider.

Go to 'Settings' -> 'Groups', enable JWT Group Sync, and set the JWT claim to cognito:groups.

Log in using a Cognito user that belongs to a Cognito group.

The user successfully logs in, but the group is not created/assigned in NetBird.

Enable debug log errors in the config.yaml file (logLevel: "debug")

Check the management container logs, and see the error: DEBG shared/auth/jwt/extractor.go:159: JWT claim "cognito:groups" is not a string array.

Expected behavior

The NetBird management server should correctly parse the cognito:groups claim as an array of strings and automatically sync/assign the user to the corresponding NetBird group.

Are you using NetBird Cloud?

No, I am using a self-hosted NetBird.

NetBird version

  • Management: v0.67.0
  • Dashboard: v2.35.0

Additional context

Based on debugging, the issue happens because AWS Cognito does not explicitly declare a groups scope in its .well-known/openid-configuration. Because cognito:groups is treated as a custom claim, it gets serialized/stringified by Dex.

When extractor.go processes the token, the following logic fails:

// extractor.go:159
if claimGroups, ok := claim.([]interface{}); ok { 
    // This fails because 'claim' is now a stringified array: "[\"Administrators\"]"
} else {
    log.Debugf("JWT claim %q is not a string array", claimName)
}

Proposed Solutions:

Feature: Expose a "Claim Mapping" or "Custom Scopes" configuration in the Dashboard UI for Custom OIDC providers, allowing administrators to map cognito:groups to the standard groups claim before the embedded IdP processes it.

Fallback logic: Update extractor.go so that if the initial []interface{} type assertion fails, it attempts to json.Unmarshal the string back into a string slice as a fallback for stringified arrays.

Originally created by @gerardpujol8 on GitHub (Mar 23, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5658 **Description** When using AWS Cognito as a Custom OIDC provider in the self-hosted Combined Setup, JWT group synchronization fails. Cognito sends the `cognito:groups` claim as a valid JSON array of strings (e.g., `["Administrators"]`). However, the Embedded IdP (Dex), which cannot be disabled in the combined setup, seems to stringify this custom, non-standard OIDC claim during token translation into a plain string: `"[\"Administrators\"]"`. As a result, the ToGroups function in `shared/auth/jwt/extractor.go` fails the type assertion `claim.([]interface{})`, silently drops the groups, and logs a debug error. **To Reproduce** Steps to reproduce the behavior: Deploy a self-hosted NetBird instance using the Combined Setup (config.yaml). Go to 'Settings' -> 'Authentication' and add AWS Cognito as a Custom OIDC provider. Go to 'Settings' -> 'Groups', enable JWT Group Sync, and set the JWT claim to `cognito:groups`. Log in using a Cognito user that belongs to a Cognito group. The user successfully logs in, but the group is not created/assigned in NetBird. Enable debug log errors in the `config.yaml` file (`logLevel: "debug"`) Check the management container logs, and see the error: `DEBG shared/auth/jwt/extractor.go:159: JWT claim "cognito:groups" is not a string array`. **Expected behavior** The NetBird management server should correctly parse the `cognito:groups` claim as an array of strings and automatically sync/assign the user to the corresponding NetBird group. **Are you using NetBird Cloud?** No, I am using a self-hosted NetBird. **NetBird version** - Management: `v0.67.0` - Dashboard: `v2.35.0` **Additional context** Based on debugging, the issue happens because AWS Cognito does not explicitly declare a groups scope in its `.well-known/openid-configuration`. Because `cognito:groups` is treated as a custom claim, it gets serialized/stringified by Dex. When `extractor.go` processes the token, the following logic fails: ``` // extractor.go:159 if claimGroups, ok := claim.([]interface{}); ok { // This fails because 'claim' is now a stringified array: "[\"Administrators\"]" } else { log.Debugf("JWT claim %q is not a string array", claimName) } ``` **Proposed Solutions:** Feature: Expose a "Claim Mapping" or "Custom Scopes" configuration in the Dashboard UI for Custom OIDC providers, allowing administrators to map `cognito:groups` to the standard `groups` claim before the embedded IdP processes it. Fallback logic: Update `extractor.go` so that if the initial `[]interface{}` type assertion fails, it attempts to `json.Unmarshal` the string back into a string slice as a fallback for stringified arrays.
saavagebueno added the triage-needed label 2026-08-05 01:31:16 -04:00
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#11840