spring-projects / spring-projects/spring-security

Support nested attributes for userNameAttributeName in OAuth2 UserInfo response

Open
#16,950 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

in: oauth2 type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Issue Description:

Currently, the DefaultOAuth2UserService seems to expect the userNameAttributeName configured in the ClientRegistration to be a top-level attribute in the JSON response from the UserInfo endpoint. However, some OAuth2 providers return user information nested within a structured object (e.g., under a data key).

Example UserInfo Response:

Some providers, like Feishu/Lark, return a UserInfo response structured like this (https://open.feishu.cn/document/server-docs/authentication-management/login-state-management/get):

{
    "code": 0,
    "msg": "success",
    "data": {
        "name": "zhangsan",
        "en_name": "zhangsan",
        "avatar_url": "www.feishu.cn/avatar/icon",
        "avatar_thumb": "www.feishu.cn/avatar/icon_thumb",
        "avatar_middle": "www.feishu.cn/avatar/icon_middle",
        "avatar_big": "www.feishu.cn/avatar/icon_big",
        "open_id": "ou-caecc734c2e3328a62489fe0648c4b98779515d3",
        "union_id": "on-d89jhsdhjsajkda7828enjdj328ydhhw3u43yjhdj",
        "email": "zhangsan@feishu.cn",
        "enterprise_email": "demo@mail.com",
        "user_id": "5d9bdxxx",
        "mobile": "+86130002883xx",
        "tenant_key": "736588c92lxf175d",
		"employee_no": "111222333"
    }
}

In this case, the desired unique identifier for the user might be open_id, which is located at data.open_id.

Expected Behavior

It should be possible to configure the userNameAttributeName to specify a path to a nested attribute. For instance, configuring userNameAttributeName as data.open_id (using dot notation or a similar standard like JSON Pointer /data/open_id) should instruct the OAuth2UserService to extract the value "ou-caecc734c2e3328a62489fe0648c4b98779515d3" from the example JSON and use it as the principal name (i.e., the value returned by OAuth2User.getName()).

Current Behavior

Based on the code in DefaultOAuth2UserService (and how DefaultOAuth2User uses the userNameAttributeName), it appears the framework expects userNameAttributeName to be a direct key in the top-level userAttributes map.

// Relevant snippet from DefaultOAuth2UserService
// ...
String userNameAttributeName = userRequest.getClientRegistration()
    .getProviderDetails()
    .getUserInfoEndpoint()
    .getUserNameAttributeName();
// ...
Map<String, Object> userAttributes = response.getBody();
// ...
return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);

// DefaultOAuth2User likely uses userAttributes.get(userNameAttributeName) internally

Trying to set userNameAttributeName to data.open_id likely results in the principal name being null or an error, because there is no top-level key named "data.open_id" in the userAttributes map. The framework does not seem to parse nested paths for this attribute.

Context

  • How has this issue affected you? This limitation prevents straightforward integration with OAuth2 providers whose UserInfo endpoints return essential user identifiers within nested JSON objects. We are trying to integrate with a provider (like Feishu/Lark) that uses this nested structure.
  • What are you trying to accomplish? We need to reliably extract the open_id from the nested data object in the UserInfo response and use it as the primary user identifier within our Spring Security context.
  • What other alternatives have you considered?
    • Implementing a custom OAuth2UserService to manually parse the response and extract the nested attribute. This works but adds boilerplate code for a relatively common scenario.
  • Are you aware of any workarounds? The primary workaround is creating a custom OAuth2UserService, as mentioned above. However, native support in the framework would be cleaner and more convenient.

Suggestion:

Consider enhancing the DefaultOAuth2UserService (or related components) to support a path expression (e.g., dot notation data.open_id or JSON Pointer /data/open_id) for the userNameAttributeName configuration, allowing developers to easily specify nested attributes as the user identifier.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading DefaultOAuth2UserService and DefaultOAuth2User, focusing on how userNameAttributeName is passed through and resolved against the UserInfo response map. Decide on the supported nested-path format and ensure a value such as data.open_id becomes the principal name while preserving existing top-level attributes; verify the behavior with focused tests in the relevant Spring Security OAuth2 user-service test area.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
authentication
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.