Skip to content

Conversation

@xiaobao520123
Copy link

@xiaobao520123 xiaobao520123 commented Jun 26, 2025

Description

Using configuration '--allowed-role' to support Microsoft Entra ID app role authorization.

Motivation and Context

Hi, I'm from Microsoft.

My daily workload typically involves doing authorization via Azure RBAC.
Due to our Azure tenant policy settings, neither Microsoft.GraphAPI nor 'groups' is available for user authentication by oauth2-proxy. Setting app role registration to users and then implementing authorization by looking at the 'roles' claim of each access token is the only one possible solution. After looking through oauth2-proxy implementation, I added authorization by app roles to provider ms-entra-id, which was built on top of current '--allowed-role' setting.
To test this feature, I created my own Application in my Azure Cloud. And it was able to see and check user's roles available in every JWT token, then made sure the user had one of authorized roles.

Authorization through app role is part of Azure RBAC ecosystem. You can learn more about app role here: Add app roles to your application and receive them in the token

How Has This Been Tested?

  1. I wrote a unit-test in ms_entra_id_test.go file.
  2. I created my own Enterprise Application in my Azure Cloud. After assigning one of app roles to my account, oauth2-proxy could successfully extract roles I had, and checked the roles in the authorization process as a whole.
  3. I also updated the documentation.

Checklist:

  • My change requires a change to the documentation or CHANGELOG.
  • I have updated the documentation/CHANGELOG accordingly.
  • I have created a feature (non-master) branch for my PR.
  • I have written tests for my code changes.

@xiaobao520123 xiaobao520123 requested a review from a team as a code owner June 26, 2025 12:54
@xiaobao520123 xiaobao520123 force-pushed the feature/support_entra_id_app_roles branch from ad85975 to e54d18d Compare June 28, 2025 08:56
payload, err := extractAccessTokenPayload(s)
if err != nil {
return nil, fmt.Errorf("malformed access token, couldn't extract jwt payload: %v", err)
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap error messages

Suggested change
return nil, err
return nil, fmt.Errorf("couldn't extract access token payload: %w", err)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tuunit done.

Comment on lines 347 to 352
return err
}

var claim rolesClaim
if err := json.Unmarshal(payload, &claim); err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap all returned errors. Otherwise tracing things gets messy really quickly

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap all returned errors. Otherwise tracing things gets messy really quickly

@tuunit Done.

Comment on lines 83 to 93
parts := strings.Split(s.AccessToken, ".")
if len(parts) < 2 {
return nil, fmt.Errorf("malformed access token, expected 3 parts got %d", len(parts))
}

payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("malformed access token, couldn't extract jwt payload: %v", err)
}

return payload, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you we use the claimExtractor provided in provider_data.go->getClaimExtractor()?

Do you need the roles claim from the AccessToken instead of the roles claim in the IdToken?

What exactly is the difference here?

https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference

https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiaobao520123 thanks for contributing. I'm not an Entra user so my experience with it is limited to some testing environments. Could you clarify the differences?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiaobao520123 thanks for contributing. I'm not an Entra user so my experience with it is limited to some testing environments. Could you clarify the differences?

@tuunit ID token and Access Token are two different concepts in MS Entra system. ID token tells the client/server who the user is, mainly for authentication with basic profile information of the user in it. Access Token tells the server what the user can access within a certain range of cloud resources or applications, and it is more about doing authorization.

In our case, we register a group of users on one single Enterprise Application with a custom role assigned onto the group. After the client calls MS Identity Platform for an Access Token of that application, the payload within the token allows the server to check if the user has the permission to access that Application. Precisely, by the roles claim. Often Access Tokens are related to one or a small range of cloud resource, while ID token is more like a global info in the whole system.

I think provider_data.go->getClaimExtractor() is designed for ID tokens, so I prefer to add a new function to extract the payload of Access Token.

@tuunit
Copy link
Member

tuunit commented Oct 28, 2025

@xiaobao520123 please add a changelog entry as well :)

Comment on lines +179 to +181

// Role enables to restrict login to users with app role
Roles []string `json:"roles,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run make generate to update the alpha docs

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tuunit Done.

@xiaobao520123 xiaobao520123 force-pushed the feature/support_entra_id_app_roles branch 2 times, most recently from aa6b1a8 to 601e505 Compare October 30, 2025 13:05
@xiaobao520123 xiaobao520123 force-pushed the feature/support_entra_id_app_roles branch from 601e505 to f5a32a3 Compare October 30, 2025 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants