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