Using Azure AD OpenID Connect (OIDC) for Authentication

Does anyone have experience with using Azure AD Open ID Connect for Authentication ?

Following Sensu’s Documentation for Authentication with OIDC we tried to implement OIDC with an Azure AD OIDC Application. We’re using OpenID for other Products in combination with Azure AD and they work, so we thought to give it try with Sensu as we don’t have a classic Active Directory in our Cloud Environment. We used the following config …

{
“type”: “oidc”,
“api_version”: “authentication/v2”,
“metadata”: {
“name”: “azuread”
},
“spec”: {
“additional_scopes”: [
“upn”, “groups”
],
“client_id”: “”,
“client_secret”: “”,
“server”: “https://login.microsoftonline.com//v2.0”,
“redirect_uri”: “https://:8080/api/enterprise/authentication/v2/oidc/callback”,
“username_claim”: “upn”,
“username_prefix”: “aad:”,
“groups_claim”: “groups”,
“groups_prefix”: “aad:”
}
}

Unfortunately this does not work, after clicking on “Sign in with AZUREAD” on Sensu Dashboard we get the following error …

{“message”:"oauth2: cannot fetch token: 400 Bad Request\nResponse: {“error”:“invalid_request”,“error_description”:“AADSTS900144: The request body must contain the following parameter: ‘code’.\r\nTrace ID: e3ba6f08-f35b-4aea-9a39-7b5172754600\r\nCorrelation ID: f90aab99-45ab-40d8-9bf2-c2b1f81f14c1\r\nTimestamp: 2020-05-28 13:52:35Z”,“error_codes”:[900144],“timestamp”:“2020-05-28 13:52:35Z”,“trace_id”:“e3ba6f08-f35b-4aea-9a39-7b5172754600”,“correlation_id”:“f90aab99-45ab-40d8-9bf2-c2b1f81f14c1”,“error_uri”:“https://login.microsoftonline.com/error?code=900144"}","code”:0}

… if we remove the additional scopes, we get the following error message after successfully authenticating to our azure oidc provider …

{“message”:“UsernameClaim not found”,“code”:0}

Any idea where we could check what is wrong ? as we didn’t have any error in our sensu-backend logs that are related to this issue …

Thanks for your help in advance !

Per engineering, “the Azure OIDC implementation wasn’t compliant with the specs” and they referenced this issue. It provides some more information and possibly a workaround.

Do you know if there are any plans to make it compliant with Azures Implementation or if SAML is on the roadmap ?

Not sure of any plans for Azure compliance, but I do know that SAML support is currently being investigated.

I was able to get mine to “work” part of the way by using the below:

Ran sensuctl auth list
type: OIDC
api_version: authentication/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: AzureAD
spec:
  additional_scopes:
  - email
  - upn
  - groups
  - username
  client_id: **REDACTED** (this is the app ID for AZURE)
  client_secret: **REDACTED** (secret used)
  groups_claim: groups
  groups_prefix: 'oidc:'
  redirect_uri: **YOUR_URL_HEREwith https at the front*/api/enterprise/authentication/v2/oidc/callback
  server: sts.windows.net/**TENANTID**/ << the slash at the end is important make sure you put https at the front
  username_claim: upn
  username_prefix: 'oidc:'

I then configured in my auzread the token claims id of upn/groups/email to be passed through, I am able to login successfully. I have two problems that I have not been able to solve yet though

First problem is that even when i add oidc:GUID_OF_MY_GROUP i can’t get role bindings to apply. You can’t just map a user with oidc:UserPrincipalName since special characters are not allowed in a username. I can actually see the GUID is passed when i look at the user i see oidc:[“GUID_HERE”,“ANOTHER_GUID_HERE”]. Passing the groups claim as a role and setting group_claim to target roles it still fails with the group_claim not being passed through

Second problem is that even though I have granted permission for the app to view account information I am still prompted every time I login to authorise the app to view basic user profile information even though I have admin consented in AzureAD.

Hopefully that is of some help although I am not 100% sure if I have even configured it correctly but it seems to be similar to what I have done previously from other configurations.

I had to remove some prefixes at the start given new user limit of posting URL links stopped me - apologies!

Hello @tribble3891,

thanks for sharing this :+1: … could you give some more informations on how you configured azure ad ? For example did you create an Enterprise App or an App Registration ? How are you doing the mapping ? Object id of the user or on user name :face_with_raised_eyebrow:

Thanks in advance

Hey @seizste

Sure - In AzureAD it is set up as a App Registration. The redirect url is: https://your_domain_name_here/api/enterprise/authentication/v2/oidc/callback

I then use the following token configuration:

The API permissions are setto pass email/openid/profile and User.Read as graph api permission (i tried playing around with them to no avail)

I can then login and I am prompted to consent every time for some reason but then I get that I don’t have the right permissions inside sensu to see anything. The configuration does seem to work properly as I see my Azure AD Groups GUID showing up in the profile section in Sensu when my user is logged in

My environment is just a single standalone server since this is for my home lab environment.

I don’t link my on premise AD with my cloud instance but one thing i though of doing was passing the sAMAccountName as a claim and trying to use that as a login ID and provide permissions based on the username rather than the AD groups that I pass from AzureAD. Mapping is all done via GUIDs at the moment since sensu won’t allow an @ sign in the username. I use puppet for my configuration and I have the following as rule bindings (i tried in the below with and without group_prefix setting configured):

sensu::resources::role_bindings:
  role_binding_administrators:
    ensure: present
    role_ref:
      type: Role
      name: role_administrators
    subjects:
      - type: Group
        name: oidc:608d8XXXXXXXXXXXXXX3d322
      - type: Group
        name: 608d8XXXXXXXXXXXXXX3d322

Hope that is helpful (I had more pictures but I am limited by how many I can upload since I am a new poster!)

Hello @tribble3891,

thanks for sharing the details about your configuration. I was also able to configure AzureAD oidc provider to a point where i can log on to the sensu dashboard but unfortunately don’t have any permissions there (i’m also getting prompted about permissions every time) …

{
  "type": "ClusterRoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "gpcms-cluster-admins"
  },
  "spec": {
    "role_ref": {
      "name": "cluster-admin",
      "type": "ClusterRole"
    },
    "subjects": [
      {
        "name": "aad:b2b1bfee-2d5d-55e7-9537-e5b30f1c3456",
        "type": "Group"
      },
      {
        "name": "b2b1bfee-2d5d-55e7-9537-e5b30f1c3456",
        "type": "Group"
      }
    ]
  }
}

… if i go to prefernces of the account i logged on, i can see all the ids including the one i configured. but somehow the mapping does not work. did you get that working or are you struggling at the same point ?

Hey @seizste

I am still struggling with that at the moment still, no combination seems to have worked :frowning:

There is someone else in the sensu-go issues list stating they are having issues also https://github.com/sensu/sensu-go/issues/3058 but seem stuck because maybe they are not passing the token properly through. When I tried some of that configuration I never even got to login at all

@jspaleta - apologies to ping directly! Do you know if there are any known issues with sensu using AzureAD as a auth provider at the moment or can see if we are doing anything obviously wrong?

Hey,

I’m not aware of an issue no.
I’m personally most familiar with interacting with Azure using the addon ldap services and no oidc.

But if you are able to login into the dashboard using the user defined in AzureAD, instead of a sensu locally defined user, then this is probably just a configuration issue with how sensu RBAC role bindings are configured

I would need to see a redacted version of the authentication resource to be sure but my gut tells me this is a misunderstanding on how the groups_prefix and users_prefix settings in the authentication providers resource interact with RBAC.

Assuming for example you have using something like

groups_prefix: 'oidc:'

in your Azure oidc auth provider configuration… then the your ClusterRoleBindings and RoleBindings resources would need to have rules with group names prefix with ‘oidc:’

Following that assumption about your group_prefix setting I would expect to see something like this:

{
  "type": "ClusterRoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "gpcms-cluster-admins"
  },
  "spec": {
    "role_ref": {
      "name": "cluster-admin",
      "type": "ClusterRole"
    },
    "subjects": [
      {
        "name": "oidc:aad:b2b1bfee-2d5d-55e7-9537-e5b30f1c3456",
        "type": "Group"
      },
      {
        "name": "oidc:b2b1bfee-2d5d-55e7-9537-e5b30f1c3456",
        "type": "Group"
      }
    ]
  }
}

Hey

Thanks for taking the time to respond. From a rol-binding perspective I am using the following (tried using both methods):

---
type: RoleBinding
api_version: core/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: role_binding_administrators
  namespace: default
spec:
  role_ref:
    name: role_administrators
    type: Role
  subjects:
  - name: oidc:608dXXXXXXXXXXXXXXb133d322
    type: Group
  - name: 608dXXXXXXXXXXXXXXb133d322
    type: Group

My role then looks like:

type: Role
api_version: core/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: role_administrators
  namespace: default
spec:
  rules:
  - resource_names: null
    resources:
    - assets
    - checks
    - entities
    - events
    - filters
    - handlers
    - hooks
    - mutators
    - rolebindings
    - roles
    - silenced
    verbs:
    - get
    - list
    - create
    - update
    - delete

From an authentication provider:

type: OIDC
api_version: authentication/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: AzureAD
spec:
  additional_scopes:
  - email
  - upn
  - groups
  - username
  client_id: REDACTED
  client_secret: REDACTED
  groups_claim: groups
  groups_prefix: 'oidc:'
  redirect_uri: https://REDACTED/api/enterprise/authentication/v2/oidc/callback
  server: https://sts.windows.net/REDACTED/
  username_claim: upn
  username_prefix: 'oidc:'

That looks too potentially match what I think i should have - most likely have misunderstood how it applies, I can see my group in the GUI with the UPN for the accound:

unless I’m forgetting something else, I expect this to match

- name: oidc:608dXXXXXXXXXXXXXXb133d322
  type: Group

Hey

Those do actually match when not censored out.

When i checked out groups_claims should be an array of strings (https://docs.sensu.io/sensu-go/latest/installation/auth/#oidc-specification). When i add multiple groups to be passed through i can see its an array and it looks to pre-popuate all of them

I just assumed that Sensu put it in the format oidc:[“GROUP 1”, “Group2”] to make it more readable. Wondering if the way azure is passing the token through is not properly formatted for Sensu to parse it ?

I think perhaps we are talking past each other… or I’m just tired. I’m not sure why you are referring to the groups claim when I’m referring to the groups_prefix
I dont know what you mean by censored out. I feel like I’m missing something important about your config.

The groups_claim is part of the oidc request spec.

The groups_prefix and users_prefix are optional strings that sensu adds to the user and auth provider group and user for sensu specifc rbac.

Sorry I haven’t walked through an azure oidc myself yet. But to be clear if you have groups_prefix set your gonna have to make sure your sensu role subject has that prefix at the start… that’s the point I’m trying to get across.

Okay,
I really wish there was an easy way to reconstruct the exact oauth call being done behind the scenes so you could poke at the Azure oauth response with curl. But i would encourage you to try that and look at the json response so you can get a handle on what Azure is doing with regard to populating the groups claim.

Reading up on the Azure documentation for their OIDC implementation backed by AD, it looks like you have some tunables as to what AD attributes get mapped into the array value of the groups_claim associated with the outh2 application you setup in Azure to use with Sensu. It could be that you may have to change some settings to get the array of strings you expect from AD.
I can’t tell you what you expect however, because I don’t have access to your AD which makes it very difficult to walk you through it.
Here’s what I think the best Azure reference doc for the tunable is


From the documentation there are several ways to populate the groups_claim. You are probably just not using the correct option, probably just getting the application level group, not the groups associated with the user.

It would definitely be easier for you to be able to poke at this with some curl commands until you got the Azure settings right. I think its gonna be pretty obvious once you get Azure configured correctly. You’re definitely getting the basics right, Azure is providing a json response with a “groups” attribute… its just not populated with what you expect it sounds like.

I hope this helps.

Hey

Apologies on the above I meant to say group_prefix and not group_claim.

I will have a poke around to see if i can get what is being passed Sensu in the JWT.

thanks

Hey,

Did some digging and found the JWT that is being used. The JWT is below:

{
  "exp": 1592030274,
  "jti": "ec02ea423c21bd431058a9dc929056a2",
  "sub": "oidc:username@domain.com",
  "groups": [
    "oidc:[\"70aed41c-93ef-4088-9fab-196d5d978c9e\",\"8a5d117e-bbbf-4706-ac17-81136e33d7bf\",\"608d8baf-b7d4-4fe1-a5d7-aac3b133d322\"]"
  ],
  "provider": {
    "provider_id": "AzureAD",
    "provider_type": "oidc",
    "user_id": "6JNSeRcDSv1fJOeWTf0L4uMrR4s3liNuyC4lP8cW_kY"
  },
  "api_key": false
}

I pass the group claims properly as groups_claim expects a array of strings, When I look inside sensu for the groups that are seen is correct, this also matches with the AzureAD groups my user has:

image

Groups my user is in AzureAD which show up properly (limited it to show just the sensu ones for brevity but those other groups above I am a member of):

PS /home/admin> Get-AzureADGroup | ? {$_.DisplayName -like "*Sensu*"}

ObjectId                             DisplayName  Description
--------                             -----------  -----------
1fd669b5-2b1a-46f7-bb36-48591caa9131 Sensu Viewer
608d8baf-b7d4-4fe1-a5d7-aac3b133d322 Sensu Admins

I pass my group claims using the below in my Azure AD application token configuration step (I have tried every configuration incl mapping them as roles):


I have a role mapping which seems to be set properly:

Type: Role
api_version: core/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: role_administrators
  namespace: default
spec:
  rules:
  - resource_names: null
    resources:
    - assets
    - checks
    - entities
    - events
    - filters
    - handlers
    - hooks
    - mutators
    - rolebindings
    - roles
    - silenced
    verbs:
    - get
    - list
    - create
    - update
    - delete

There is a corresponding role-binding which maps my Azure AD GUID (with oidc: in front since i have groups_prefix set):

type: RoleBinding
api_version: core/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: role_binding_administrators
  namespace: default
spec:
  role_ref:
    name: role_administrators
    type: Role
  subjects:
  - name: oidc:608d8baf-b7d4-4fe1-a5d7-aac3b133d322
    type: Group

That should properly map the GUID from Azure. AD with the role which grants access but I still see nothing and get the error in the logs (I know the role binding is also correct and does grant information as i created a local user and added a subject of name: test and type: User and i could see everything):

{"component":"apid.graphql","error":"request unauthorized","level":"warning","msg":"couldn't access resource","time":"2020-06-13T18:19:48+10:00"}

Appreciate your help/feedback so far below is also the oidc config

type: OIDC
api_version: authentication/v2
metadata:
  created_by: admin
  labels:
    sensu.io/managed_by: sensuctl
  name: AzureAD
spec:
  additional_scopes:
  - email
  - upn
  - profile
  - groups
  client_id: my_id_here
  client_secret: my_secret_here
  groups_claim: groups
  groups_prefix: 'oidc:'
  redirect_uri: https://sensu.my_url.com/api/enterprise/authentication/v2/oidc/callback
  server: https://sts.windows.net/TENANT/
  username_claim: upn
  username_prefix: 'oidc:'

I must have something configured wrong somewhere i guess since I keep getting prompted constantly to reconsent still for sensu app to have access to information in my tenant.

Hmm…
so yeah according to the JWT info this sure looks right to me and matches how I expect the rule.

“608d8baf-b7d4-4fe1-a5d7-aac3b133d322”

Maybe there’s a bug here. Can you remove the oidc group prefix in your auth resource? Just set it to “”
And then redo your RoleBinding to remove the ‘oidc:’ prefix in the subjects.

Actually…
that JWT token might not be what I expect… why does the groups claim have an oidc: prefix in it?
I think its suppose to just be an array
I’m expecting to see:

  "groups": 
    ["70aed41c-93ef-4088-9fab-196d5d978c9e","8a5d117e-bbbf-4706-ac17-81136e33d7bf","608d8baf-b7d4-4fe1-a5d7-aac3b133d322"]

It looks like Azure is doing is treating ‘groups’ as a keyvalue hash and putting the expected string array inside of the oidc key in the hash. So I think that’s the problem.

So I guess we need to figure out how to configure for that in Sensu’s auth config. maybe the groups_claim value in the Sensu resource needs to be ‘groups.oidc’ instead of ‘groups’

@tribble3891
Ugh… i think Azure has broken the groups_claim array of strings expectation of OIDC in a subtle way
it looks like Azure isnt given us a clean array of strings.
technically its given us an array with a single string in it starting with oidc:[ and ending with ]
its like Azure is trying to wrap an array in an array, which it should be using a json hash.
Azure is doing this

  "groups": [
    "oidc:[\"70aed41c-93ef-4088-9fab-196d5d978c9e\",\"8a5d117e-bbbf-4706-ac17-81136e33d7bf\",\"608d8baf-b7d4-4fe1-a5d7-aac3b133d322\"]"
  ],

I would expect it to do this instead:

  "groups": ["70aed41c-93ef-4088-9fab-196d5d978c9e","8a5d117e-bbbf-4706-ac17-81136e33d7bf","608d8baf-b7d4-4fe1-a5d7-aac3b133d322"],

I’m not sure what to do here, what Azure is putting out right now for the groups attribute seems to break the groups_claim expectation.