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.oauth2.sdk.http.HTTPResponse;
023import net.jcip.annotations.Immutable;
024import net.minidev.json.JSONObject;
025
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.Objects;
029import java.util.Set;
030
031
032/**
033 * OAuth 2.0 Token error response.
034 *
035 * <p>Standard token errors:
036 *
037 * <ul>
038 *     <li>{@link OAuth2Error#INVALID_REQUEST}
039 *     <li>{@link OAuth2Error#INVALID_CLIENT}
040 *     <li>{@link OAuth2Error#INVALID_GRANT}
041 *     <li>{@link OAuth2Error#UNAUTHORIZED_CLIENT}
042 *     <li>{@link OAuth2Error#UNSUPPORTED_GRANT_TYPE}
043 *     <li>{@link OAuth2Error#INVALID_SCOPE}
044 * </ul>
045 *
046 * <p>Example HTTP response:
047 *
048 * <pre>
049 * HTTP/1.1 400 Bad Request
050 * Content-Type: application/json
051 * Cache-Control: no-store
052 * Pragma: no-cache
053 * 
054 * {
055 *  "error": "invalid_request"
056 * }
057 * </pre>
058 *
059 * <p>Related specifications:
060 *
061 * <ul>
062 *     <li>OAuth 2.0 (RFC 6749)
063 * </ul>
064 */
065@Immutable
066public class TokenErrorResponse extends TokenResponse implements ErrorResponse {
067
068
069        /**
070         * The standard OAuth 2.0 errors for an Access Token error response.
071         */
072        private static final Set<ErrorObject> STANDARD_ERRORS;
073        
074        
075        static {
076                Set<ErrorObject> errors = new HashSet<>();
077                errors.add(OAuth2Error.INVALID_REQUEST);
078                errors.add(OAuth2Error.INVALID_CLIENT);
079                errors.add(OAuth2Error.INVALID_GRANT);
080                errors.add(OAuth2Error.UNAUTHORIZED_CLIENT);
081                errors.add(OAuth2Error.UNSUPPORTED_GRANT_TYPE);
082                errors.add(OAuth2Error.INVALID_SCOPE);
083                STANDARD_ERRORS = Collections.unmodifiableSet(errors);
084        }
085        
086        
087        /**
088         * Gets the standard OAuth 2.0 errors for an Access Token error 
089         * response.
090         *
091         * @return The standard errors, as a read-only set.
092         */
093        public static Set<ErrorObject> getStandardErrors() {
094        
095                return STANDARD_ERRORS;
096        }
097        
098        
099        /**
100         * The error.
101         */
102        private final ErrorObject error;
103
104
105        /**
106         * Creates a new OAuth 2.0 Access Token error response. No OAuth 2.0
107         * error is specified.
108         */
109        protected TokenErrorResponse() {
110
111                error = null;
112        }
113        
114        
115        /**
116         * Creates a new OAuth 2.0 Access Token error response.
117         *
118         * @param error The error. Should match one of the 
119         *              {@link #getStandardErrors standard errors} for a token 
120         *              error response. Must not be {@code null}.
121         */
122        public TokenErrorResponse(final ErrorObject error) {
123                this.error = Objects.requireNonNull(error);
124        }
125
126
127        @Override
128        public boolean indicatesSuccess() {
129
130                return false;
131        }
132        
133
134        @Override
135        public ErrorObject getErrorObject() {
136        
137                return error;
138        }
139        
140        
141        /**
142         * Returns the JSON object for this token error response.
143         *
144         * @return The JSON object for this token error response.
145         */
146        public JSONObject toJSONObject() {
147        
148                JSONObject o = new JSONObject();
149
150                // No error?
151                if (error == null)
152                        return o;
153                
154                o.putAll(error.toJSONObject());
155                
156                return o;
157        }
158        
159        
160        @Override
161        public HTTPResponse toHTTPResponse() {
162
163                int statusCode = (error != null && error.getHTTPStatusCode() > 0) ?
164                        error.getHTTPStatusCode() : HTTPResponse.SC_BAD_REQUEST;
165
166                HTTPResponse httpResponse = new HTTPResponse(statusCode);
167
168                if (error == null)
169                        return httpResponse;
170                
171                httpResponse.setEntityContentType(ContentType.APPLICATION_JSON);
172                httpResponse.setCacheControl("no-store");
173                httpResponse.setPragma("no-cache");
174                httpResponse.setBody(toJSONObject().toString());
175                return httpResponse;
176        }
177
178
179        /**
180         * Parses an OAuth 2.0 Token Error response from the specified JSON
181         * object.
182         *
183         * @param jsonObject The JSON object to parse. Its status code must not
184         *                   be 200 (OK). Must not be {@code null}.
185         *
186         * @return The token error response.
187         *
188         * @throws ParseException If the JSON object couldn't be parsed to an 
189         *                        OAuth 2.0 Token Error response.
190         */
191        public static TokenErrorResponse parse(final JSONObject jsonObject)
192                throws ParseException {
193
194                // No error code?
195                if (! jsonObject.containsKey("error"))
196                        return new TokenErrorResponse();
197                
198                ErrorObject error = ErrorObject.parse(jsonObject).setHTTPStatusCode(HTTPResponse.SC_BAD_REQUEST);
199                
200                return new TokenErrorResponse(error);
201        }
202        
203        
204        /**
205         * Parses an OAuth 2.0 Token Error response from the specified HTTP
206         * response.
207         *
208         * @param httpResponse The HTTP response to parse. Its status code must
209         *                     not be 200 (OK). Must not be {@code null}.
210         *
211         * @return The token error response.
212         *
213         * @throws ParseException If the HTTP response couldn't be parsed to an 
214         *                        OAuth 2.0 Token Error response.
215         */
216        public static TokenErrorResponse parse(final HTTPResponse httpResponse)
217                throws ParseException {
218                
219                httpResponse.ensureStatusCodeNotOK();
220                return new TokenErrorResponse(ErrorObject.parse(httpResponse));
221        }
222}