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

sessionClient.refreshToken returning generic 400 bad request error #332

Closed
ssawchenko opened this issue Aug 4, 2022 · 14 comments
Closed
Labels
bug Something isn't working

Comments

@ssawchenko
Copy link

Describe the bug?

sessionClient.refreshToken is returning a generic 400 bad request error for some or our users. I have been unable to reproduce this locally, but in our crash reporting we see the following error:

AuthClient.awaitRefreshTokens.onError called 
  with exception: AuthorizationException: {"type":0,"code":0,"errorDescription":"Invalid status code 400 Bad Request"}
	at com.okta.oidc.net.request.TokenRequest.executeRequest(TokenRequest.java:23)
	at com.okta.oidc.clients.sessions.SyncSessionClientImpl.refreshToken(SyncSessionClientImpl.java:5)
	at com.okta.oidc.clients.sessions.SessionClientImpl.lambda$refreshToken$15(SessionClientImpl.java:2)
	at com.okta.oidc.clients.sessions.SessionClientImpl.a(Unknown Source:0)
	at com.okta.oidc.clients.sessions.d.run(Unknown Source:2)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
	at java.lang.Thread.run(Thread.java:920)
Caused by: com.okta.oidc.net.HttpStatusCodeException: Invalid status code 400 Bad Request
	at com.okta.oidc.net.HttpResponse.asJson(HttpResponse.java:10)
	at com.okta.oidc.net.request.TokenRequest.executeRequest(TokenRequest.java:3)

My guess is that the refresh tokens have expired for these users, however, the error does not indicate that this is the problem.

I see that there have been reports in the past for ambiguous error codes (#274) and I am wondering if this is somewhat related.

What is expected to happen?

If this was in fact a valid refresh token expiry, according to this ticket (#274) I would expect an error along the lines of:

AuthorizationException: {"type":2,"code":0,"errorDescription":"Expired refresh token"}

What is the actual behavior?

I am being given the error:

AuthorizationException: {"type":0,"code":0,"errorDescription":"Invalid status code 400 Bad Request"}

which is not very descriptive about what the actual problem is.

Reproduction Steps?

Unable to reproduce this locally - I am guessing I have to wait the refresh token expiry on a device. I have a device sitting right now, but there is still awhile on the expiry before I can test this out.

But we have a bunch of Sentry reports that indicate the same error is being returned to numerous users and that this is not simply a one off case.

Additional Information?

People have been commenting in #165 with similar issues, but since that ticket is closed I am unsure it is getting visibility.

SDK Version

1.3.2

Build Information

No response

@ssawchenko ssawchenko added the bug Something isn't working label Aug 4, 2022
@JayNewstrom
Copy link
Contributor

The response from the server when a refreshToken is invalid is

{
	"error": "invalid_grant",
	"error_description": "The refresh token is invalid or expired."
}

This will result in throwing this error: https://github.com/okta/okta-oidc-android/blob/master/library/src/main/java/com/okta/oidc/net/request/TokenRequest.java#L135

Which should give you the information you're requesting. That being said, 400s could occur outside of that as well.

If you have more information, or ideally could reproduce this, we can get it fixed though!

@ssawchenko
Copy link
Author

ssawchenko commented Aug 8, 2022

@JayNewstrom Thank you for your reply :)

I've never seen any reports with the message "The refresh token is invalid or expired", just numerous instances of "Invalid status code 400 Bad Request". Unfortunately these are coming from our Sentry reporting tool, I have yet to be able to repo this locally. I don't have a user that has an expired refresh token that I can test this on, as we do not have control over the actual Okta tenant for security reasons.

So with that in mind, I have some follow up questions:

  • Is there documentation anywhere that indicates what can cause the "Invalid status code 400 Bad Request" error message to be returned from the refreshToken call?
  • I could possibly setup a dev Okta tenant to try and simulate this - could you point me towards documentation on how I could go about force expiring a user's refresh token, or if refresh tokens can be set to some very low number to allow me to test my theory that expired tokens are causing the issue I am seeing? I have tried digging around online and I can't find anything to help. I am by no means an experienced Okta tenant administrator, I only know enough to test out very basic situations.

@JayNewstrom
Copy link
Contributor

We throw that exception in a few places: notably:

public JSONObject asJson() throws IOException, JSONException {
if (mStatusCode < HttpURLConnection.HTTP_OK ||
mStatusCode >= HttpURLConnection.HTTP_MULT_CHOICE) {
throw new HttpStatusCodeException(mStatusCode, mHttpClient.getResponseMessage());
}
return getJsonObjectFromResponseInputStream(getContent());
}
public JSONObject asJsonWithErrorDescription() throws IOException, JSONException {
if (mStatusCode < HttpURLConnection.HTTP_OK ||
mStatusCode >= HttpURLConnection.HTTP_MULT_CHOICE) {
try {
return getJsonObjectFromResponseInputStream(getContent());
} catch (Exception any) {
throw new HttpStatusCodeException(mStatusCode, mHttpClient.getResponseMessage());
}
}
return getJsonObjectFromResponseInputStream(getContent());
}

This could happen due to introspect/configuration/authorize calls.

The way I forced an invalid token is via a Charles Proxy breakpoint.

It seems like your line numbers aren't matching up in your stack trace. Are you using proguard/minification? This SDK isn't meant to be used with proguard, so you'll need to add a keep rule (which should already be happening automatically https://github.com/okta/okta-oidc-android/blob/master/library/proguard-rules.pro#L1)

@ssawchenko
Copy link
Author

ssawchenko commented Aug 8, 2022

@JayNewstrom

It seems like your line numbers aren't matching up in your stack trace. Are you using proguard/minification?

The project that consumes my library does. I've gone back and added in the proguard rule to my library's consumer proguard file for the future just to be sure.

The way I forced an invalid token is via a Charles Proxy breakpoint.

I've gone through and tested this out in Charles, and when I invalidate the refresh token I do see the "invalid_grant" response, so it looks like the refresh token response is behaving correct when the refresh token is invalid.

So then if this issue is unrelated to refresh tokens being invalid, I'm at a loss for the actual cause. Generally 400 errors occur due to incorrectly typed URL, malformed syntax, or a URL that contains illegal characters, but in this case my requests are going fully through the oidc library call refreshToken and my app has little to no control over how it is being formatted.
There was another person having an issue similar to mine (#165 (comment)) that indicated it was occurring when their access token was invalid. Do you know if there is a way via Charles to invalidate an access token?

@JayNewstrom
Copy link
Contributor

Access tokens aren't used as part of the refresh token process.

Another thing you could do is look through syslog for why the requests are failing.

@ssawchenko
Copy link
Author

ssawchenko commented Aug 8, 2022

@JayNewstrom
Ok I was able to reproduce this, and I again think it is related to the refresh tokens being invalid.

I ran the request in Charles again, invalidated the refresh token and in Charles I see the correct error being returned from the raw Okta call https://[clientname].okta.com/oauth2/v1/token:
Screen Shot 2022-08-08 at 4 39 36 PM

But looking at my console log for my app while this was call was executed shows that the oidc API call refreshToken is actually returning me (the app) an Invalid status code 400 Bad Request error instead:
Screen Shot 2022-08-08 at 4 39 25 PM

I can repo this each time this way.

@JayNewstrom
Copy link
Contributor

The exception is being wrapped. You can access the inner exception via e.getCause()

@ssawchenko
Copy link
Author

@JayNewstrom
Cause looks to also return me the HttpStatusCodeException. Is there somewhere else the invalid grant error message could be found?
image

@JayNewstrom
Copy link
Contributor

It doesn't look like it's exposed. However, if you want to migrate to the new SDK, it's exposed as HttpResponseException.

@ssawchenko
Copy link
Author

ssawchenko commented Aug 9, 2022

I was unaware of a new SDK, I'll open a ticket on our side and look to start migration in the future.

For now though, my main concern is making sure that the Sentry reports we are seeing are not actual issues (I wouldn't call an expired refresh token a problem). So from the above conversation, is the expected behaviour of refreshToken API call to return an "Invalid status code 400 Bad Request" response when given an invalid or expired refresh token? Because this is what I am still seeing locally.

@JayNewstrom
Copy link
Contributor

Yes, this is an expected error.

@ssawchenko
Copy link
Author

@JayNewstrom Excellent, I'll update my docs and Sentry reporting to note this, and we'll look to migrate to the new SDK in the future.

Thank you for your time!

@JayNewstrom
Copy link
Contributor

Please let me know if you have any feedback on the new SDK too! Thanks

@JayNewstrom
Copy link
Contributor

I'm going to close this for now. But if you have more questions, feel free to open a new issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants