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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.oauth2.sdk.ErrorObject;
023import com.nimbusds.oauth2.sdk.ErrorResponse;
024import com.nimbusds.oauth2.sdk.OAuth2Error;
025import com.nimbusds.oauth2.sdk.ParseException;
026import com.nimbusds.oauth2.sdk.http.HTTPResponse;
027import net.jcip.annotations.Immutable;
028import net.minidev.json.JSONObject;
029
030import java.util.Collections;
031import java.util.HashSet;
032import java.util.Objects;
033import java.util.Set;
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
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                this.error = Objects.requireNonNull(error);
132        }
133
134        
135        @Override
136        public boolean indicatesSuccess() {
137
138                return false;
139        }
140
141        
142        @Override
143        public ErrorObject getErrorObject() {
144
145                return error;
146        }
147
148        
149        /**
150         * Returns the JSON object for this CIBA error response.
151         *
152         * @return The JSON object for this CIBA error response.
153         */
154        public JSONObject toJSONObject() {
155
156                if (error != null) {
157                        return error.toJSONObject();
158                } else {
159                        return new JSONObject();
160                }
161        }
162        
163
164        @Override
165        public HTTPResponse toHTTPResponse() {
166
167                int statusCode = (error != null && error.getHTTPStatusCode() > 0) ?
168                        error.getHTTPStatusCode() : HTTPResponse.SC_BAD_REQUEST;
169
170                HTTPResponse httpResponse = new HTTPResponse(statusCode);
171
172                if (error == null)
173                        return httpResponse;
174
175                httpResponse.setEntityContentType(ContentType.APPLICATION_JSON);
176                httpResponse.setCacheControl("no-store");
177                httpResponse.setPragma("no-cache");
178                httpResponse.setBody(toJSONObject().toString());
179
180                return httpResponse;
181        }
182
183        /**
184         * Parses a CIBA error response from the specified JSON object.
185         *
186         * @param jsonObject The JSON object to parse. Its status code must not
187         *                   be 200 (OK). Must not be {@code null}.
188         *
189         * @return The CIBA error response.
190         *
191         * @throws ParseException If parsing failed.
192         */
193        public static CIBAErrorResponse parse(final JSONObject jsonObject)
194                throws ParseException {
195
196                // No error code?
197                if (! jsonObject.containsKey("error"))
198                        return new CIBAErrorResponse();
199
200                return new CIBAErrorResponse(ErrorObject.parse(jsonObject));
201        }
202        
203
204        /**
205         * Parses a CIBA error response from the specified HTTP response.
206         *
207         * @param httpResponse The HTTP response to parse. Its status code must
208         *                     not be 200 (OK). Must not be {@code null}.
209         *
210         * @return The CIBA error response.
211         *
212         * @throws ParseException If parsing failed.
213         */
214        public static CIBAErrorResponse parse(final HTTPResponse httpResponse)
215                throws ParseException {
216
217                httpResponse.ensureStatusCodeNotOK();
218                return new CIBAErrorResponse(ErrorObject.parse(httpResponse));
219        }
220}