Using Azure AD OpenID Connect (OIDC) for Authentication

Maybe there’s a way to get Azure to change how its populating the groups JSON attribute via some tunable? End of the day OIDC spec seems to require whatever json attribute being used for the groups_claim to be an array of strings, each string matching the name of a group.

What Azure is passing right now is technically an array with one very long string in it, which is definitely wrong-ish. It sure feels like Azure is breaking the spec here.


So i removed the groups_prefix and ran it and i see the below as the JWT:

  "exp": 1592295583,
  "jti": "e1425f1bdc04fcbf44623sce4ffa4d95",
  "sub": "",
  "groups": [
  "provider": {
    "provider_id": "AzureAD",
    "provider_type": "oidc",
    "user_id": "6JNSeREDSv1fJODWTf0L4uMrR4s3liNuyC4lP8WW_kY"
  "api_key": false

that then showed the following in Sensu with no permissions being granted

It definitely seems to be passing it incorrectly - i can’t see any tunables to change that, what i thought about doing was just passing a seperate attribute and using that as the group_claim but i couldn’t get it in the right format.

All i can see in the application manifest is to set: “groupMembershipClaims”: “SecurityGroup” unfortunately but I may be missing something (its late in AU at the moment)

Also when i try to use groups.oidc it can’t find the claim

{"message":"GroupsClaim not found","code":0}

Okay I think our friends at HashiCorp have documented the magic.Specifically there is discussion about the scope called “

This maybe the missing magic that Azure requires to set the groups_claim correctly for OIDC.

Take a look at:

Azure Ref:

no the groups.oidc thing was a red herring i added to the discussion.

The groups_claim value should be top level “groups” attribute in the JWT. We just have to find how to have Azure generated to expected array of strings as the value.

For an example of what the expected in the “groups” attribute of the decuded token take a look at this okta documentation:

The okta example shows the token request and the expected decoded JWT with a the “groups” attribute holding an array of strings.

Yeah thats good to reference - will see if i can poke around a bit more to see if its possible to get it to return in the format is expecting without changes required in someones implementation.

Was trying to capture the oidc flow using fiddler earlier to see if i can track down where the extra pieces are coming from. Wasn’t able to find anything that allowed me to pinpoint Azure or Sensu yet (in case it was adding it on accidentally) but will keep digging.


thanks for sticking with it!

I’m not sure how much I can help without taking the plunge into Azure myself.

I’m going to try to write some basic OIDC troubleshooting instructions using curl and cmdline tools to decode the JWT so it can be inspected with jq. This sort of manual troubleshooting might help operators who are having trouble configuring OIDC service providers. I always find that if I manually do the steps involved in an API interaction, I have a better time tracking down the failures and more importantly helps me file useful bug reports.

So having a dig around the config I still came up empty unfortunately but having a look since I am using i am using OIDC v1 tokens on AzureAD. Having a look through some of the AzureAD issues on the hashicorp/vault i was able to find in #6494 which has the comment below:

I think we’ve finally gotten to the bottom of what’s causing this. When logging in via OIDC, the ID token is parsed, and then any claims found at the /userinfo endpoint are merged in. When using the sts endpoint the, groups list in the ID token is fine, but the same claim also exists in the /userinfo response as the stringified list: "groups":["[\"9e58c006-42ba-4406-9b13-f6132b0b29ab\",\"352b6bbe-9f59-4a65-9a7e-2f6cd2f26494\",\"45dd570d-2706-4d0a-bea7-b09fb4ad5691\"]"] . That value is replacing the good groups claim, which is why we’re see this issue only in the OIDC+ //sts... case

This seems to be similar to what I am doing and they seem to suggest moving to OIDC v2 instead which would target the following end point I believe (which is what other people seem to have tried previously):

This though when you look at the openid spec at:

This essentially gives:

{"token_endpoint":"","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"","request_uri_parameter_supported":false,"userinfo_endpoint":"","authorization_endpoint":"","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"OC","cloud_instance_name":"","cloud_graph_host_name":"","msgraph_host":"","rbac_url":""}

the scopes_supported don’t support the groups scope that say OKTA supports (you can pass this as a token in the azure ad app but it makes no difference). If you try and request the following scopes with v2 configured:

type: OIDC
api_version: authentication/v2
  created_by: admin
  labels: sensuctl
  name: AzureAD
  - email
  - profile
  - groups

This results in the following stating:

{"message": "GroupsClaim not found", "code": 0}

I can’t yet see a way around this but its interesting that they were able to get groups working when using v2 (which is essentially the link you provided further up in the chain) doesn’t seem to work given the scope does not exist:

Will have a crack at the permutations again just incase i have missed something, might also have a look a bit later at the consent part of the string as maybe it never gets the access and thus never gets passed…