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