Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NPE occurs if account has no localAccountId #669

Closed
GregDThomas opened this issue Jun 30, 2023 · 12 comments
Closed

NPE occurs if account has no localAccountId #669

GregDThomas opened this issue Jun 30, 2023 · 12 comments
Assignees
Labels
Bug Something isn't working, needs an investigation and a fix public-client For questions/issues related to public client apps Requires more info More information is needed, from either the person who opened the issue or another team

Comments

@GregDThomas
Copy link

Steps to reproduce

Expected results

  • New token is generated, and then re-used as it's in the cache

Actual results

  • New token is generated, then an NPE occurs as there is no localAccountId and the following statement throws an NPE;
    • if (accCached.homeAccountId().contains(accCached.localAccountId())) {
==No accounts in cache
17:46:47.494 [ForkJoinPool.commonPool-worker-3] DEBUG com.microsoft.aad.msal4j.PublicClientApplication - [Correlation ID: cd8843c4-5fc5-4d42-9d65-e6e7675f042b] Execution of class com.microsoft.aad.msal4j.AcquireTokenSilentSupplier failed.
com.microsoft.aad.msal4j.MsalClientException: Token not found in the cache
	at com.microsoft.aad.msal4j.AcquireTokenSilentSupplier.execute(AcquireTokenSilentSupplier.java:75)
	at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:69)
	at com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:18)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
==acquireTokenSilently call failed: com.microsoft.aad.msal4j.MsalClientException: Token not found in the cache
17:46:47.565 [ForkJoinPool.commonPool-worker-3] DEBUG com.microsoft.aad.msal4j.TokenRequestExecutor - Sending token request to: https://adfs.mbtest.bt.com/adfs/
17:46:48.867 [ForkJoinPool.commonPool-worker-3] DEBUG com.microsoft.aad.msal4j.PublicClientApplication - [Correlation ID: 48f8b765-57e0-44b6-8325-20d9f97b2272] Access Token and Refresh Token were returned
==username/password flow succeeded
Account username: **REDACTED** 
Access token:     **REDACTED** 
Id token:         **REDACTED** 

17:46:48.882 [ForkJoinPool.commonPool-worker-3] ERROR com.microsoft.aad.msal4j.PublicClientApplication - [Correlation ID: 865566e9-a1d5-444f-b364-976f46a9b953] Execution of class com.microsoft.aad.msal4j.AccountsSupplier failed.
java.lang.NullPointerException: null
	at java.base/java.lang.String.contains(String.java:2036)
	at com.microsoft.aad.msal4j.TokenCache.getAccounts(TokenCache.java:349)
	at com.microsoft.aad.msal4j.AccountsSupplier.get(AccountsSupplier.java:26)
	at com.microsoft.aad.msal4j.AccountsSupplier.get(AccountsSupplier.java:11)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

@Avery-Dunn
Copy link
Collaborator

Hello @GregDThomas : Sorry for the delayed response on this. Just to be clear, you're seeing this issue after making a silent call, right? (i.e., you get a token without a localAccountId, then call the silent flow and get the NPE)

If so, the reason seems to be that when checking for cached accounts we check if a certain string contains the localAccountId, and when that's null it throws the exception.

Could you describe your use case a bit more? As long as your getting ID tokens I'd assume they'd have either a sub or oid claim (which is where we get localAccountId), but I know some guest account/ADFS/B2C scenarios can get a bit weird with local ID's.

I'll confirm this behavior is a bug (and not something we should be flagging) and get a fix in the next release (no ETA yet, but it should be soon)

@Avery-Dunn Avery-Dunn self-assigned this Jul 7, 2023
@Avery-Dunn Avery-Dunn added Bug Something isn't working, needs an investigation and a fix Requires more info More information is needed, from either the person who opened the issue or another team labels Jul 7, 2023
@GregDThomas
Copy link
Author

Hi - that's right. It's essentially a straight lift of the code from the sample referenced, but it;

  • Attempts to fetch token silently. This fails, as there is nothing in the cache ("Token not found in the cache" from the logs above).
  • Then succeeds in fetching the token with username/password ("username/password flow succeeded" from the logs above).
  • Then attempts again to fetch the token silently - triggering the NPE ("java.lang.NullPointerException: null" from the logs above.

@Avery-Dunn
Copy link
Collaborator

Avery-Dunn commented Jul 7, 2023

Are you using ADFS, B2C, or some sort of guest account? Just trying to get an idea of what scenarios would lead to that localAccountId being null so we can better handle it.

@GregDThomas
Copy link
Author

Ah, sorry missed that. It's just "my account" on a local domain. This particular domain isn't really used for logging in - it's more for providing a directory of users for testing applications.

@GregDThomas
Copy link
Author

The "ultimate" use case is for an REST API exposed by a Java application. Some of the clients will be "humans", but there will be some M2M clients which will use credentials of "service accounts" - i.e. not authorised directly with AD, but can access the credentials of an account in AD that it will use to gain a token.

@GregDThomas
Copy link
Author

Aside; I've just tried with another AD account - that of the one I actually use to login to Windows. That too has no localAccountId and ends up with a NPE.

@GregDThomas
Copy link
Author

And yes, we're using an ADFS end-point to get the token

@Avery-Dunn
Copy link
Collaborator

Avery-Dunn commented Jul 10, 2023

In your console output you list ID token as *REDACTED , but you are getting an ID token, right? If so, could you decode the token (at a site like this one) and confirm whether or not there is a 'sub' and/or 'oid' section?

If you have those, then you shouldn't be getting an NPE (the localAccountId is populated using them). If you don't have those sections, that'd be very weird and I'm not sure why they're missing.

I can't recreate the issue using our test ADFS accounts, so my only other guess is that it's an issue with that sample rather than in the code. You said it was basically a straight lift, but did you make any changes to the code (particularly around the "getAccounts" or the PublicClientApplication creation logic)?

@GregDThomas
Copy link
Author

GregDThomas commented Jul 11, 2023

Below is the full decoded token (albeit details redacted again in places) - as you suspect there is no sub or oid value.

{
  "aud": "(uuid)",
  "iss": "(url)",
  "iat": 1688750275,
  "nbf": 1688750275,
  "exp": 1688753875,
  "auth_time": 1688750275,
  "sub": "(opaque string)",
  "upn": "(principle name)",
  "unique_name": "(domain)\\(username)",
  "sid": "(uid)",
  "email": "(email)",
  "given_name": "Greg",
  "family_name": "Thomas",
  "group": [
    "list",
    "of",
    "groups"
  ],
  "winaccountname": "(account name)",
  "apptype": "Public",
  "appid": "(uuid)",
  "authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
  "ver": "1.0",
  "scp": "openid"
}

With regards to the sample, the only change I made was to import a CA certificate in to the JVM at the very beginning - without which I get TLS errors.

@GregDThomas
Copy link
Author

I doubt it is much help, but for completeness the header is;

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "(opaque string)",
  "kid": "(same opaque string)"
}

@Avery-Dunn
Copy link
Collaborator

This was fixed as part of #684, which is now in the latest 1.13.10 release.

@GregDThomas
Copy link
Author

Than ks - I can confirm I'm no longer getting an NPE.

@bgavrilMS bgavrilMS added the public-client For questions/issues related to public client apps label Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working, needs an investigation and a fix public-client For questions/issues related to public client apps Requires more info More information is needed, from either the person who opened the issue or another team
Projects
None yet
Development

No branches or pull requests

3 participants