NUWCDIVNPT / stig-manager

An API and client for managing STIG assessments
Other
113 stars 29 forks source link

Last Claims field does not update when using ADFS IdP #1399

Open jb0000001 opened 3 days ago

jb0000001 commented 3 days ago

Is there an existing issue for this?

Are you using the latest version of STIG Manager?

Where are you experiencing the issue?

Current Behavior

I have configured ADFS to act as the IdP for STIG Manager. Login and role mapping works correctly. The "Last Access" timestamp updates on each login.

With the above working AD FS implementation, changes in the IdP database (i.e. ActiveDirectory) are not reflected in the user record in STIG Manager. Restarting STIG Manager, closing the web browser, and logging in again have no effect. Once the initial user information is populated into the STIG Manager database at first login, no further updates are made.

Expected Behavior

Changes in the IdP database should be reflected in the user record in STIG Manager after the user's next logon.

Steps To Reproduce

  1. Deploy STIG Manager in a docker container using standard documentation.
  2. Configure STIG Manager to use AD FS as its IdP Relevant excerpt from Docker compose file
    environment:
      - STIGMAN_OIDC_PROVIDER=https://adfs.example.com/adfs
      - STIGMAN_CLIENT_OIDC_PROVIDER=https://adfs.example.com/adfs
      - STIGMAN_CLIENT_ID=your_client_id
      - STIGMAN_DB_HOST=mysql
      - STIGMAN_JWT_SCOPE_CLAIM=scp
      - STIGMAN_CLIENT_EXTRA_SCOPES=https://stigs.example.com/openid
      - STIGMAN_JWT_PRIVILEGES_CLAIM=roles
  3. Configure AD FS to provide the required claims (preferred_username, name, email, roles) to STIG Manager.
    NOTE: This step required a fair bit of trial and error to get working
  4. Once ADFS is configured and login to STIG Manager is successful (with admin role), navigate to the User Grants section under Application Management
  5. Double click on your user account
  6. Note the Email and/or Name values
  7. Change either of those values for your user account in the IdP database
  8. Log in again
  9. Navigate to the User Grants section under Application Management
  10. Double click on your user account
  11. Note that the Email and/or Name values have not been updated

Can you provide screenshots, logs, or other useful artifacts?

I have traced the problem to the following line: https://github.com/NUWCDIVNPT/stig-manager/blob/f584df55cc775e229080b7148101a875877661d0/api/source/utils/auth.js#L79

The conditional here means the lastClaims field will only be updated if a) it doesn't already exist in the DB, or b) the jti attribute value has changed.

As best I can tell (my ADFS expertise is somewhat limited), AD FS and Azure AD FS do not include the jti attribute in the id token and can't be easily configured to do so.

Reference: https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference https://techcommunity.microsoft.com/t5/microsoft-entra/azure-ad-token-missing-jti-claim/m-p/2210776 https://learn.microsoft.com/en-us/answers/questions/908497/how-do-i-get-azure-ad-to-send-jti-in-jwt https://github.com/nextauthjs/next-auth/issues/6125 (example ID token)

Since the attribute is not present, my assumption is that the comparison becomes NULL !== NULL (which will always fail). Hence, lastClaims will only update at first login for any IdP that does not include the jti attribute in the ID token.

One possible fix would be to make this a configurable option so that another attribute (i.e. nonce, iat, exp, etc.) can be used to trigger updates to the user information in the DB (instead of jti).

Describe your Environment

- Hosting: STIG Manager running in a Podman (Docker) container.
- Browser: MS Edge v129.0.2792.79
- OS (host): RHEL 8
- OS (container): Alpine LTS (https://github.com/NUWCDIVNPT/stig-manager/blob/f584df55cc775e229080b7148101a875877661d0/Dockerfile)
cd-rite commented 3 days ago

Hi @jb0000001 Thanks for bringing this to our attention, and the references! We will investigate making this configurable, or perhaps falling back to nonce if we don't find a jti.

csmig commented 2 days ago

I appreciate the effort made to present this issue to us. I've opened a PR meant to resolve the issue by implementing a new envvar STIGMAN_JWT_ASSERTION_CLAIM.

@jb0000001 are you able to checkout the branch jwt-assertion-claim in this repo and give it a spin before we merge? I have tested the PR using Keycloak and the nonce claim and all went well. Although I noticed Keycloak retains the original nonce in any refreshed JWT derived from the original (it does change the jti value in the refreshed tokens). Not sure how ADFS handles that, since we do not have access to ADFS.

Also, I've taken note of the comment that it was challenging to work out some of the token and claim details when using ADFS. I think our Documentation needs a rework on this topic, it is very Keycloak-centric and has some imprecise terminology.

jb0000001 commented 2 days ago

@csmig thanks!!! The code change looks like it should work. I'm tied up with other projects at the moment, so it may be a few days before I can try it out.

When I force a refresh of the token from the client application using oidcProvider.updateToken(-1);, AD FS does not include a nonce claim in either the ID or the access token. That shouldn't be an issue since the nonce sent to ADFS (and by extension the matching nonce returned in the ID token) changes every time I reload the STIG Manager application page in my web browser.

cd-rite commented 1 day ago

Hi @jb0000001 Just to clarify, the API only receives an access token, not the ID token. According to the MS reference here, you'll want to specify uti ("Token identifier claim, equivalent to jti in the JWT specification. Unique, per-token identifier that is case-sensitive.") rather than nonce. In the above branch or the next numbered release, you'll be able to specify that with the STIGMAN_JWT_ASSERTION_CLAIM envvar.