|
33 | 33 |
|
34 | 34 | import net.openid.appauth.AuthorizationException.GeneralErrors;
|
35 | 35 | import net.openid.appauth.AuthorizationException.RegistrationRequestErrors;
|
| 36 | +import net.openid.appauth.AuthorizationException.RevokeTokenRequestErrors; |
36 | 37 | import net.openid.appauth.AuthorizationException.TokenRequestErrors;
|
37 | 38 | import net.openid.appauth.IdToken.IdTokenException;
|
38 | 39 | import net.openid.appauth.browser.BrowserDescriptor;
|
@@ -511,6 +512,34 @@ public void performRegistrationRequest(
|
511 | 512 | .execute();
|
512 | 513 | }
|
513 | 514 |
|
| 515 | + /** |
| 516 | + * Sends a request to the authorization service to revoke a token. |
| 517 | + * The result of this request will be sent to the provided callback handler. |
| 518 | + */ |
| 519 | + public void performRevokeToken( |
| 520 | + @NonNull RevokeTokenRequest request, |
| 521 | + @NonNull RevokeTokenResponseCallback callback) { |
| 522 | + performRevokeToken(request, NoClientAuthentication.INSTANCE, callback); |
| 523 | + } |
| 524 | + |
| 525 | + /** |
| 526 | + * Sends a request to the authorization service to revoke a token. |
| 527 | + * The result of this request will be sent to the provided callback handler. |
| 528 | + */ |
| 529 | + public void performRevokeToken( |
| 530 | + @NonNull RevokeTokenRequest request, |
| 531 | + @NonNull ClientAuthentication clientAuthentication, |
| 532 | + @NonNull RevokeTokenResponseCallback callback) { |
| 533 | + checkNotDisposed(); |
| 534 | + Logger.debug("Initiating token revocation"); |
| 535 | + new RevokeTokenRequestTask( |
| 536 | + request, |
| 537 | + clientAuthentication, |
| 538 | + mClientConfiguration.getConnectionBuilder(), |
| 539 | + callback) |
| 540 | + .execute(); |
| 541 | + } |
| 542 | + |
514 | 543 | /**
|
515 | 544 | * Disposes state that will not normally be handled by garbage collection. This should be
|
516 | 545 | * called when the authorization service is no longer required, including when any owning
|
@@ -859,4 +888,131 @@ public interface RegistrationResponseCallback {
|
859 | 888 | void onRegistrationRequestCompleted(@Nullable RegistrationResponse response,
|
860 | 889 | @Nullable AuthorizationException ex);
|
861 | 890 | }
|
| 891 | + |
| 892 | + private static class RevokeTokenRequestTask |
| 893 | + extends AsyncTask<Void, Void, JSONObject> { |
| 894 | + private RevokeTokenRequest mRequest; |
| 895 | + private final ClientAuthentication mClientAuthentication; |
| 896 | + private final ConnectionBuilder mConnectionBuilder; |
| 897 | + private RevokeTokenResponseCallback mCallback; |
| 898 | + |
| 899 | + private AuthorizationException mException; |
| 900 | + |
| 901 | + RevokeTokenRequestTask(RevokeTokenRequest request, |
| 902 | + @NonNull ClientAuthentication clientAuthentication, |
| 903 | + @NonNull ConnectionBuilder connectionBuilder, |
| 904 | + RevokeTokenResponseCallback callback) { |
| 905 | + mRequest = request; |
| 906 | + mClientAuthentication = clientAuthentication; |
| 907 | + mConnectionBuilder = connectionBuilder; |
| 908 | + mCallback = callback; |
| 909 | + } |
| 910 | + |
| 911 | + |
| 912 | + @Override |
| 913 | + protected JSONObject doInBackground(Void... voids) { |
| 914 | + InputStream is = null; |
| 915 | + try { |
| 916 | + HttpURLConnection conn = mConnectionBuilder.openConnection( |
| 917 | + mRequest.configuration.revocationEndpoint); |
| 918 | + conn.setRequestMethod("POST"); |
| 919 | + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
| 920 | + conn.setDoOutput(true); |
| 921 | + |
| 922 | + Map<String, String> headers = mClientAuthentication |
| 923 | + .getRequestHeaders(mRequest.clientId); |
| 924 | + if (headers != null) { |
| 925 | + for (Map.Entry<String,String> header : headers.entrySet()) { |
| 926 | + conn.setRequestProperty(header.getKey(), header.getValue()); |
| 927 | + } |
| 928 | + } |
| 929 | + |
| 930 | + Map<String, String> parameters = mRequest.getRequestParameters(); |
| 931 | + Map<String, String> clientAuthParams = mClientAuthentication |
| 932 | + .getRequestParameters(mRequest.clientId); |
| 933 | + if (clientAuthParams != null) { |
| 934 | + parameters.putAll(clientAuthParams); |
| 935 | + } |
| 936 | + |
| 937 | + String queryData = UriUtil.formUrlEncode(parameters); |
| 938 | + conn.setRequestProperty("Content-Length", String.valueOf(queryData.length())); |
| 939 | + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); |
| 940 | + |
| 941 | + wr.write(queryData); |
| 942 | + wr.flush(); |
| 943 | + |
| 944 | + if (conn.getResponseCode() >= HttpURLConnection.HTTP_MULT_CHOICE) { |
| 945 | + is = conn.getErrorStream(); |
| 946 | + String response = Utils.readInputStream(is); |
| 947 | + return new JSONObject(response); |
| 948 | + } |
| 949 | + return null; |
| 950 | + } catch (IOException ex) { |
| 951 | + Logger.debugWithStack(ex, "Failed to complete revocation request"); |
| 952 | + mException = AuthorizationException.fromTemplate( |
| 953 | + GeneralErrors.NETWORK_ERROR, ex); |
| 954 | + } catch (JSONException ex) { |
| 955 | + Logger.debugWithStack(ex, "Failed to complete revocation request"); |
| 956 | + mException = AuthorizationException.fromTemplate( |
| 957 | + GeneralErrors.JSON_DESERIALIZATION_ERROR, ex); |
| 958 | + } finally { |
| 959 | + Utils.closeQuietly(is); |
| 960 | + } |
| 961 | + return null; |
| 962 | + } |
| 963 | + |
| 964 | + @Override |
| 965 | + protected void onPostExecute(JSONObject json) { |
| 966 | + if (mException != null) { |
| 967 | + mCallback.onRevokeTokenRequestCompleted(null, mException); |
| 968 | + return; |
| 969 | + } |
| 970 | + |
| 971 | + if (json != null && json.has(AuthorizationException.PARAM_ERROR)) { |
| 972 | + AuthorizationException ex; |
| 973 | + try { |
| 974 | + String error = json.getString(AuthorizationException.PARAM_ERROR); |
| 975 | + ex = AuthorizationException.fromOAuthTemplate( |
| 976 | + RevokeTokenRequestErrors.byString(error), |
| 977 | + error, |
| 978 | + json.optString(AuthorizationException.PARAM_ERROR_DESCRIPTION, null), |
| 979 | + UriUtil.parseUriIfAvailable( |
| 980 | + json.optString(AuthorizationException.PARAM_ERROR_URI))); |
| 981 | + } catch (JSONException jsonEx) { |
| 982 | + ex = AuthorizationException.fromTemplate( |
| 983 | + GeneralErrors.JSON_DESERIALIZATION_ERROR, |
| 984 | + jsonEx); |
| 985 | + } |
| 986 | + mCallback.onRevokeTokenRequestCompleted(null, ex); |
| 987 | + return; |
| 988 | + } |
| 989 | + |
| 990 | + RevokeTokenResponse response = new RevokeTokenResponse.Builder(mRequest).build(); |
| 991 | + Logger.debug("Token revocation with %s completed", |
| 992 | + mRequest.configuration.revocationEndpoint); |
| 993 | + mCallback.onRevokeTokenRequestCompleted(response, null); |
| 994 | + } |
| 995 | + } |
| 996 | + |
| 997 | + /** |
| 998 | + * Callback interface for token revocation requests. |
| 999 | + * |
| 1000 | + * @see AuthorizationService#performRevokeToken |
| 1001 | + */ |
| 1002 | + public interface RevokeTokenResponseCallback { |
| 1003 | + /** |
| 1004 | + * Invoked when the request completes successfully or fails. |
| 1005 | + * |
| 1006 | + * Exactly one of `response` or `ex` will be non-null. If `response` is `null`, a failure |
| 1007 | + * occurred during the request. This can happen if an invalid URI was provided, no |
| 1008 | + * connection to the server could be established, or the response JSON was incomplete or |
| 1009 | + * incorrectly formatted. |
| 1010 | + * |
| 1011 | + * @param response the retrieved token revocation response, if successful; `null` otherwise. |
| 1012 | + * @param ex a description of the failure, if one occurred: `null` otherwise. |
| 1013 | + * @see AuthorizationException.RevokeTokenRequestErrors |
| 1014 | + */ |
| 1015 | + void onRevokeTokenRequestCompleted(@Nullable RevokeTokenResponse response, |
| 1016 | + @Nullable AuthorizationException ex); |
| 1017 | + } |
862 | 1018 | }
|
0 commit comments