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