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