001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk;
019
020
021import com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.jose.util.Base64URL;
023import com.nimbusds.jwt.util.DateUtils;
024import com.nimbusds.oauth2.sdk.auth.X509CertificateConfirmation;
025import com.nimbusds.oauth2.sdk.dpop.JWKThumbprintConfirmation;
026import com.nimbusds.oauth2.sdk.http.HTTPResponse;
027import com.nimbusds.oauth2.sdk.id.*;
028import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail;
029import com.nimbusds.oauth2.sdk.token.AccessTokenType;
030import com.nimbusds.oauth2.sdk.util.JSONArrayUtils;
031import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
032import net.jcip.annotations.Immutable;
033import net.minidev.json.JSONArray;
034import net.minidev.json.JSONObject;
035
036import java.util.Date;
037import java.util.List;
038import java.util.Map;
039
040
041/**
042 * Token introspection success response.
043 *
044 * <p>Related specifications:
045 *
046 * <ul>
047 *     <li>OAuth 2.0 Token Introspection (RFC 7662).
048 *     <li>OAuth 2.0 Rich Authorization Requests (RFC 9396), section 9.2.
049 *     <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound
050 *         Access Tokens (RFC 8705).
051 *     <li>OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer
052 *         (DPoP) (RFC 9449)
053 * </ul>
054 */
055@Immutable
056public class TokenIntrospectionSuccessResponse extends TokenIntrospectionResponse implements SuccessResponse {
057
058
059        /**
060         * Builder for constructing token introspection success responses.
061         */
062        public static class Builder {
063                
064                
065                /**
066                 * The parameters.
067                 */
068                private final JSONObject params = new JSONObject();
069
070
071                /**
072                 * Creates a new token introspection success response builder.
073                 *
074                 * @param active {@code true} if the token is active, else
075                 *               {@code false}.
076                 */
077                public Builder(final boolean active) {
078                        
079                        params.put("active", active);
080                }
081                
082                
083                /**
084                 * Creates a new token introspection success response builder
085                 * with the parameters of the specified response.
086                 *
087                 * @param response The response which parameters to use. Not
088                 *                 {@code null}.
089                 */
090                public Builder(final TokenIntrospectionSuccessResponse response) {
091                        
092                        params.putAll(response.params);
093                }
094
095
096                /**
097                 * Sets the token scope. Corresponds to the {@code scope}
098                 * parameter.
099                 *
100                 * @param scope The token scope, {@code null} if not specified.
101                 *
102                 * @return This builder.
103                 */
104                public Builder scope(final Scope scope) {
105                        if (scope != null) params.put("scope", scope.toString());
106                        else params.remove("scope");
107                        return this;
108                }
109
110
111                /**
112                 * Sets the identifier for the OAuth 2.0 client that requested
113                 * the token. Corresponds to the {@code client_id} parameter.
114                 *
115                 * @param clientID The client identifier, {@code null} if not
116                 *                 specified.
117                 *
118                 * @return This builder.
119                 */
120                public Builder clientID(final ClientID clientID) {
121                        if (clientID != null) params.put("client_id", clientID.getValue());
122                        else params.remove("client_id");
123                        return this;
124                }
125
126
127                /**
128                 * Sets the username of the resource owner who authorised the
129                 * token. Corresponds to the {@code username} parameter.
130                 *
131                 * @param username The username, {@code null} if not specified.
132                 *
133                 * @return This builder.
134                 */
135                public Builder username(final String username) {
136                        if (username != null) params.put("username", username);
137                        else params.remove("username");
138                        return this;
139                }
140
141
142                /**
143                 * Sets the token type. Corresponds to the {@code token_type}
144                 * parameter.
145                 *
146                 * @param tokenType The token type, {@code null} if not
147                 *                  specified.
148                 *
149                 * @return This builder.
150                 */
151                public Builder tokenType(final AccessTokenType tokenType) {
152                        if (tokenType != null) params.put("token_type", tokenType.getValue());
153                        else params.remove("token_type");
154                        return this;
155                }
156
157
158                /**
159                 * Sets the token expiration time. Corresponds to the
160                 * {@code exp} parameter.
161                 *
162                 * @param exp The token expiration time, {@code null} if not
163                 *            specified.
164                 *
165                 * @return This builder.
166                 */
167                public Builder expirationTime(final Date exp) {
168                        if (exp != null) params.put("exp", DateUtils.toSecondsSinceEpoch(exp));
169                        else params.remove("exp");
170                        return this;
171                }
172
173
174                /**
175                 * Sets the token issue time. Corresponds to the {@code iat}
176                 * parameter.
177                 *
178                 * @param iat The token issue time, {@code null} if not
179                 *            specified.
180                 *
181                 * @return This builder.
182                 */
183                public Builder issueTime(final Date iat) {
184                        if (iat != null) params.put("iat", DateUtils.toSecondsSinceEpoch(iat));
185                        else params.remove("iat");
186                        return this;
187                }
188
189
190                /**
191                 * Sets the token not-before time. Corresponds to the
192                 * {@code nbf} parameter.
193                 *
194                 * @param nbf The token not-before time, {@code null} if not
195                 *            specified.
196                 *
197                 * @return This builder.
198                 */
199                public Builder notBeforeTime(final Date nbf) {
200                        if (nbf != null) params.put("nbf", DateUtils.toSecondsSinceEpoch(nbf));
201                        else params.remove("nbf");
202                        return this;
203                }
204
205
206                /**
207                 * Sets the token subject. Corresponds to the {@code sub}
208                 * parameter.
209                 *
210                 * @param sub The token subject, {@code null} if not specified.
211                 *
212                 * @return This builder.
213                 */
214                public Builder subject(final Subject sub) {
215                        if (sub != null) params.put("sub", sub.getValue());
216                        else params.remove("sub");
217                        return this;
218                }
219
220
221                /**
222                 * Sets the token audience. Corresponds to the {@code aud}
223                 * parameter.
224                 *
225                 * @param audList The token audience, {@code null} if not
226                 *                specified.
227                 *
228                 * @return This builder.
229                 */
230                public Builder audience(final List<Audience> audList) {
231                        if (audList != null) params.put("aud", Audience.toStringList(audList));
232                        else params.remove("aud");
233                        return this;
234                }
235
236
237                /**
238                 * Sets the token issuer. Corresponds to the {@code iss}
239                 * parameter.
240                 *
241                 * @param iss The token issuer, {@code null} if not specified.
242                 *
243                 * @return This builder.
244                 */
245                public Builder issuer(final Issuer iss) {
246                        if (iss != null) params.put("iss", iss.getValue());
247                        else params.remove("iss");
248                        return this;
249                }
250
251
252                /**
253                 * Sets the token identifier. Corresponds to the {@code jti}
254                 * parameter.
255                 *
256                 * @param jti The token identifier, {@code null} if not
257                 *            specified.
258                 *
259                 * @return This builder.
260                 */
261                public Builder jwtID(final JWTID jti) {
262                        if (jti != null) params.put("jti", jti.getValue());
263                        else params.remove("jti");
264                        return this;
265                }
266                
267                
268                /**
269                 * Sets the client X.509 certificate SHA-256 thumbprint, for a
270                 * mutual TLS client certificate bound access token.
271                 * Corresponds to the {@code cnf.x5t#S256} parameter.
272                 *
273                 * @param x5t The client X.509 certificate SHA-256 thumbprint,
274                 *            {@code null} if not specified.
275                 *
276                 * @return This builder.
277                 */
278                @Deprecated
279                public Builder x509CertificateSHA256Thumbprint(final Base64URL x5t) {
280                        
281                        if (x5t != null) {
282                                JSONObject cnf;
283                                if (params.containsKey("cnf")) {
284                                        cnf = (JSONObject)params.get("cnf");
285                                } else {
286                                        cnf = new JSONObject();
287                                        params.put("cnf", cnf);
288                                }
289                                cnf.put("x5t#S256", x5t.toString());
290                        } else if (params.containsKey("cnf")) {
291                                JSONObject cnf = (JSONObject) params.get("cnf");
292                                cnf.remove("x5t#S256");
293                                if (cnf.isEmpty()) {
294                                        params.remove("cnf");
295                                }
296                        }
297                        
298                        return this;
299                }
300                
301                
302                /**
303                 * Sets the client X.509 certificate confirmation, for a mutual
304                 * TLS client certificate bound access token. Corresponds to
305                 * the {@code cnf.x5t#S256} parameter.
306                 *
307                 * @param cnf The client X.509 certificate confirmation,
308                 *            {@code null} if not specified.
309                 *
310                 * @return This builder.
311                 */
312                public Builder x509CertificateConfirmation(final X509CertificateConfirmation cnf) {
313                        
314                        if (cnf != null) {
315                                Map.Entry<String, JSONObject> param = cnf.toJWTClaim();
316                                params.put(param.getKey(), param.getValue());
317                        } else {
318                                params.remove("cnf");
319                        }
320                        return this;
321                }
322                
323                
324                /**
325                 * Sets the JSON Web Key (JWK) SHA-256 thumbprint confirmation,
326                 * for OAuth 2.0 DPoP. Corresponds to the {@code cnf.jkt}
327                 * parameter.
328                 *
329                 * @param cnf The JWK SHA-256 thumbprint confirmation,
330                 *            {@code null} if not specified.
331                 *
332                 * @return This builder.
333                 */
334                public Builder jwkThumbprintConfirmation(final JWKThumbprintConfirmation cnf) {
335                        
336                        if (cnf != null) {
337                                Map.Entry<String, JSONObject> param = cnf.toJWTClaim();
338                                params.put(param.getKey(), param.getValue());
339                        } else {
340                                params.remove("cnf");
341                        }
342                        return this;
343                }
344
345
346                /**
347                 * Sets the Rich Authorisation Request (RAR) details.
348                 * Corresponds to the {@code authorization_details} parameter.
349                 *
350                 * @param authorizationDetails The authorisation details,
351                 *                             {@code null} if not specified.
352                 *
353                 * @return This builder.
354                 */
355                public Builder authorizationDetails(final List<AuthorizationDetail> authorizationDetails) {
356
357                        if (authorizationDetails != null) {
358                                JSONArray jsonArray = new JSONArray();
359                                for (AuthorizationDetail detail: authorizationDetails) {
360                                        jsonArray.add(detail.toJSONObject());
361                                }
362                                params.put("authorization_details", jsonArray);
363                        } else {
364                                params.put("authorization_details", null);
365                        }
366                        return this;
367                }
368
369
370                /**
371                 * Sets a custom parameter.
372                 *
373                 * @param name  The parameter name. Must not be {@code null}.
374                 * @param value The parameter value. Should map to a JSON type.
375                 *              If {@code null} not specified.
376                 *
377                 * @return This builder.
378                 */
379                public Builder parameter(final String name, final Object value) {
380                        if (value != null) params.put(name, value);
381                        else params.remove(name);
382                        return this;
383                }
384
385
386                /**
387                 * Builds a new token introspection success response.
388                 *
389                 * @return The token introspection success response.
390                 */
391                public TokenIntrospectionSuccessResponse build() {
392
393                        return new TokenIntrospectionSuccessResponse(params);
394                }
395        }
396
397
398        /**
399         * The parameters.
400         */
401        private final JSONObject params;
402
403
404        /**
405         * Creates a new token introspection success response.
406         *
407         * @param params The response parameters. Must contain at least the
408         *               required {@code active} parameter and not be
409         *               {@code null}.
410         */
411        public TokenIntrospectionSuccessResponse(final JSONObject params) {
412
413                if (! (params.get("active") instanceof Boolean)) {
414                        throw new IllegalArgumentException("Missing / invalid boolean active parameter");
415                }
416
417                this.params = params;
418        }
419
420
421        /**
422         * Returns the active status for the token. Corresponds to the
423         * {@code active} parameter.
424         *
425         * @return {@code true} if the token is active, else {@code false}.
426         */
427        public boolean isActive() {
428
429                try {
430                        return JSONObjectUtils.getBoolean(params, "active", false);
431                } catch (ParseException e) {
432                        return false; // always false on error
433                }
434        }
435
436
437        /**
438         * Returns the scope of the token. Corresponds to the {@code scope}
439         * parameter.
440         *
441         * @return The token scope, {@code null} if not specified.
442         */
443        public Scope getScope() {
444
445                try {
446                        return Scope.parse(JSONObjectUtils.getString(params, "scope"));
447                } catch (ParseException e) {
448                        return null;
449                }
450        }
451
452
453        /**
454         * Returns the identifier of the OAuth 2.0 client that requested the
455         * token. Corresponds to the {@code client_id} parameter.
456         *
457         * @return The client identifier, {@code null} if not specified.
458         */
459        public ClientID getClientID() {
460
461                try {
462                        return new ClientID(JSONObjectUtils.getString(params, "client_id"));
463                } catch (ParseException e) {
464                        return null;
465                }
466        }
467
468
469        /**
470         * Returns the username of the resource owner who authorised the token.
471         * Corresponds to the {@code username} parameter.
472         *
473         * @return The username, {@code null} if not specified.
474         */
475        public String getUsername() {
476
477                try {
478                        return JSONObjectUtils.getString(params, "username", null);
479                } catch (ParseException e) {
480                        return null;
481                }
482        }
483
484
485        /**
486         * Returns the access token type. Corresponds to the {@code token_type}
487         * parameter.
488         *
489         * @return The token type, {@code null} if not specified.
490         */
491        public AccessTokenType getTokenType() {
492
493                try {
494                        return new AccessTokenType(JSONObjectUtils.getString(params, "token_type"));
495                } catch (ParseException e) {
496                        return null;
497                }
498        }
499
500
501        /**
502         * Returns the token expiration time. Corresponds to the {@code exp}
503         * parameter.
504         *
505         * @return The token expiration time, {@code null} if not specified.
506         */
507        public Date getExpirationTime() {
508
509                try {
510                        return DateUtils.fromSecondsSinceEpoch(JSONObjectUtils.getLong(params, "exp"));
511                } catch (ParseException e) {
512                        return null;
513                }
514        }
515
516
517        /**
518         * Returns the token issue time. Corresponds to the {@code iat}
519         * parameter.
520         *
521         * @return The token issue time, {@code null} if not specified.
522         */
523        public Date getIssueTime() {
524
525                try {
526                        return DateUtils.fromSecondsSinceEpoch(JSONObjectUtils.getLong(params, "iat"));
527                } catch (ParseException e) {
528                        return null;
529                }
530        }
531
532
533        /**
534         * Returns the token not-before time. Corresponds to the {@code nbf}
535         * parameter.
536         *
537         * @return The token not-before time, {@code null} if not specified.
538         */
539        public Date getNotBeforeTime() {
540
541                try {
542                        return DateUtils.fromSecondsSinceEpoch(JSONObjectUtils.getLong(params, "nbf"));
543                } catch (ParseException e) {
544                        return null;
545                }
546        }
547
548
549        /**
550         * Returns the subject of the token, usually a machine-readable
551         * identifier of the resource owner who authorised the token.
552         * Corresponds to the {@code sub} parameter.
553         *
554         * @return The token subject, {@code null} if not specified.
555         */
556        public Subject getSubject() {
557
558                try {
559                        return new Subject(JSONObjectUtils.getString(params, "sub"));
560                } catch (ParseException e) {
561                        return null;
562                }
563        }
564
565
566        /**
567         * Returns the intended audience for the token. Corresponds to the
568         * {@code aud} parameter.
569         *
570         * @return The token audience, {@code null} if not specified.
571         */
572        public List<Audience> getAudience() {
573                // Try string array first, then string
574                try {
575                        return Audience.create(JSONObjectUtils.getStringList(params, "aud"));
576                } catch (ParseException e) {
577                        try {
578                                return new Audience(JSONObjectUtils.getString(params, "aud")).toSingleAudienceList();
579                        } catch (ParseException e2) {
580                                return null;
581                        }
582                }
583        }
584
585
586        /**
587         * Returns the token issuer. Corresponds to the {@code iss} parameter.
588         *
589         * @return The token issuer, {@code null} if not specified.
590         */
591        public Issuer getIssuer() {
592
593                try {
594                        return new Issuer(JSONObjectUtils.getString(params, "iss"));
595                } catch (ParseException e) {
596                        return null;
597                }
598        }
599
600
601        /**
602         * Returns the token identifier. Corresponds to the {@code jti}
603         * parameter.
604         *
605         * @return The token identifier, {@code null} if not specified.
606         */
607        public JWTID getJWTID() {
608
609                try {
610                        return new JWTID(JSONObjectUtils.getString(params, "jti"));
611                } catch (ParseException e) {
612                        return null;
613                }
614        }
615        
616        
617        /**
618         * Returns the client X.509 certificate SHA-256 thumbprint, for a
619         * mutual TLS client certificate bound access token. Corresponds to the
620         * {@code cnf.x5t#S256} parameter.
621         *
622         * @return The client X.509 certificate SHA-256 thumbprint,
623         *         {@code null} if not specified.
624         */
625        @Deprecated
626        public Base64URL getX509CertificateSHA256Thumbprint() {
627        
628                try {
629                        JSONObject cnf = JSONObjectUtils.getJSONObject(params, "cnf", null);
630                        
631                        if (cnf == null) return null;
632                        
633                        String x5t = JSONObjectUtils.getString(cnf, "x5t#S256", null);
634                        
635                        if (x5t == null) return null;
636                        
637                        return new Base64URL(x5t);
638                        
639                } catch (ParseException e) {
640                        return null;
641                }
642        }
643        
644        
645        /**
646         * Returns the client X.509 certificate confirmation, for a mutual TLS
647         * client certificate bound access token. Corresponds to the
648         * {@code cnf.x5t#S256} parameter.
649         *
650         * @return The client X.509 certificate confirmation, {@code null} if
651         *         not specified.
652         */
653        public X509CertificateConfirmation getX509CertificateConfirmation() {
654                
655                return X509CertificateConfirmation.parse(params);
656        }
657        
658        
659        /**
660         * Returns the JSON Web Key (JWK) SHA-256 thumbprint confirmation, for
661         * OAuth 2.0 DPoP. Corresponds to the {@code cnf.jkt} parameter.
662         *
663         * @return The JWK SHA-256 thumbprint confirmation, {@code null} if not
664         *         specified.
665         */
666        public JWKThumbprintConfirmation getJWKThumbprintConfirmation() {
667                
668                return JWKThumbprintConfirmation.parse(params);
669        }
670
671
672        /**
673         * Returns the Rich Authorisation Request (RAR) details. Corresponds to
674         * the {@code authorization_details} parameter.
675         *
676         * @return The authorisation details, {@code null} if not specified.
677         */
678        public List<AuthorizationDetail> getAuthorizationDetails() {
679
680                JSONArray jsonArray = getJSONArrayParameter("authorization_details");
681
682                if (jsonArray == null) return null;
683
684                try {
685                        return AuthorizationDetail.parseList(JSONArrayUtils.toJSONObjectList(jsonArray));
686                } catch (ParseException e) {
687                        return null;
688                }
689        }
690        
691        
692        /**
693         * Returns the string parameter with the specified name.
694         *
695         * @param name The parameter name. Must not be {@code null}.
696         *
697         * @return The parameter value, {@code null} if not specified or if
698         *         parsing failed.
699         */
700        public String getStringParameter(final String name) {
701                
702                try {
703                        return JSONObjectUtils.getString(params, name, null);
704                } catch (ParseException e) {
705                        return null;
706                }
707        }
708        
709        
710        /**
711         * Returns the boolean parameter with the specified name.
712         *
713         * @param name The parameter name. Must not be {@code null}.
714         *
715         * @return The parameter value.
716         *
717         * @throws ParseException If the parameter isn't specified or parsing
718         *                        failed.
719         */
720        public boolean getBooleanParameter(final String name)
721                throws ParseException {
722                
723                return JSONObjectUtils.getBoolean(params, name);
724        }
725        
726        
727        /**
728         * Returns the number parameter with the specified name.
729         *
730         * @param name The parameter name. Must not be {@code null}.
731         *
732         * @return The parameter value, {@code null} if not specified or
733         *         parsing failed.
734         */
735        public Number getNumberParameter(final String name) {
736                
737                try {
738                        return JSONObjectUtils.getNumber(params, name, null);
739                } catch (ParseException e) {
740                        return null;
741                }
742        }
743        
744        
745        /**
746         * Returns the string list parameter with the specified name.
747         *
748         * @param name The parameter name. Must not be {@code null}.
749         *
750         * @return The parameter value, {@code null} if not specified or if
751         *         parsing failed.
752         */
753        public List<String> getStringListParameter(final String name) {
754                
755                try {
756                        return JSONObjectUtils.getStringList(params, name, null);
757                } catch (ParseException e) {
758                        return null;
759                }
760        }
761        
762        
763        /**
764         * Returns the JSON object parameter with the specified name.
765         *
766         * @param name The parameter name. Must not be {@code null}.
767         *
768         * @return The parameter value, {@code null} if not specified or if
769         *         parsing failed.
770         */
771        public JSONObject getJSONObjectParameter(final String name) {
772                
773                try {
774                        return JSONObjectUtils.getJSONObject(params, name, null);
775                } catch (ParseException e) {
776                        return null;
777                }
778        }
779
780
781        /**
782         * Returns the JSON array parameter with the specified name.
783         *
784         * @param name The parameter name. Must not be {@code null}.
785         *
786         * @return The parameter value, {@code null} if not specified or if
787         *         parsing failed.
788         */
789        public JSONArray getJSONArrayParameter(final String name) {
790
791                try {
792                        return JSONObjectUtils.getJSONArray(params, name, null);
793                } catch (ParseException e) {
794                        return null;
795                }
796        }
797        
798        
799        /**
800         * Returns the underlying parameters.
801         *
802         * @return The parameters, as JSON object.
803         */
804        public JSONObject getParameters() {
805                
806                return params;
807        }
808
809
810        /**
811         * Returns a JSON object representation of this token introspection
812         * success response.
813         *
814         * <p>Example JSON object:
815         *
816         * <pre>
817         * {
818         *  "active"          : true,
819         *  "client_id"       : "l238j323ds-23ij4",
820         *  "username"        : "jdoe",
821         *  "scope"           : "read write dolphin",
822         *  "sub"             : "Z5O3upPC88QrAjx00dis",
823         *  "aud"             : "https://protected.example.net/resource",
824         *  "iss"             : "https://server.example.com/",
825         *  "exp"             : 1419356238,
826         *  "iat"             : 1419350238,
827         *  "extension_field" : "twenty-seven"
828         * }
829         * </pre>
830         *
831         * @return The JSON object.
832         */
833        public JSONObject toJSONObject() {
834
835                return new JSONObject(params);
836        }
837        
838
839        @Override
840        public boolean indicatesSuccess() {
841
842                return true;
843        }
844
845
846        @Override
847        public HTTPResponse toHTTPResponse() {
848
849                HTTPResponse httpResponse = new HTTPResponse(HTTPResponse.SC_OK);
850                httpResponse.setEntityContentType(ContentType.APPLICATION_JSON);
851                httpResponse.setContent(params.toJSONString());
852                return httpResponse;
853        }
854
855
856        /**
857         * Parses a token introspection success response from the specified
858         * JSON object.
859         *
860         * @param jsonObject The JSON object to parse. Must not be {@code null}.
861         *
862         * @return The token introspection success response.
863         *
864         * @throws ParseException If the JSON object couldn't be parsed to a
865         *                        token introspection success response.
866         */
867        public static TokenIntrospectionSuccessResponse parse(final JSONObject jsonObject)
868                throws ParseException {
869
870                try {
871                        return new TokenIntrospectionSuccessResponse(jsonObject);
872                } catch (IllegalArgumentException e) {
873                        throw new ParseException(e.getMessage(), e);
874                }
875        }
876
877
878        /**
879         * Parses a token introspection success response from the specified
880         * HTTP response.
881         *
882         * @param httpResponse The HTTP response. Must not be {@code null}.
883         *
884         * @return The token introspection success response.
885         *
886         * @throws ParseException If the HTTP response couldn't be parsed to a
887         *                        token introspection success response.
888         */
889        public static TokenIntrospectionSuccessResponse parse(final HTTPResponse httpResponse)
890                throws ParseException {
891
892                httpResponse.ensureStatusCode(HTTPResponse.SC_OK);
893                JSONObject jsonObject = httpResponse.getContentAsJSONObject();
894                return parse(jsonObject);
895        }
896}