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.client;
019
020
021import java.util.Collections;
022import java.util.HashSet;
023import java.util.Set;
024
025import com.nimbusds.oauth2.sdk.ErrorObject;
026import com.nimbusds.oauth2.sdk.ErrorResponse;
027import com.nimbusds.oauth2.sdk.ParseException;
028import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
029import com.nimbusds.oauth2.sdk.http.HTTPResponse;
030import com.nimbusds.oauth2.sdk.token.BearerTokenError;
031import net.jcip.annotations.Immutable;
032import net.minidev.json.JSONObject;
033import org.apache.commons.lang3.StringUtils;
034
035
036/**
037 * Client registration error response.
038 *
039 * <p>Standard errors:
040 *
041 * <ul>
042 *     <li>OAuth 2.0 Bearer Token errors:
043 *         <ul>
044 *             <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#MISSING_TOKEN}
045 *             <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INVALID_REQUEST}
046 *             <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INVALID_TOKEN}
047 *             <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INSUFFICIENT_SCOPE}
048 *          </ul>
049 *     <li>OpenID Connect specific errors:
050 *         <ul>
051 *             <li>{@link RegistrationError#INVALID_REDIRECT_URI}
052 *             <li>{@link RegistrationError#INVALID_CLIENT_METADATA}
053 *             <li>{@link RegistrationError#INVALID_SOFTWARE_STATEMENT}
054 *             <li>{@link RegistrationError#UNAPPROVED_SOFTWARE_STATEMENT}
055 *         </ul>
056 * </ul>
057 *
058 * <p>Example HTTP response:
059 *
060 * <pre>
061 * HTTP/1.1 400 Bad Request
062 * Content-Type: application/json
063 * Cache-Control: no-store
064 * Pragma: no-cache
065 *
066 * {
067 *  "error":"invalid_redirect_uri",
068 *  "error_description":"The redirection URI of http://sketchy.example.com is not allowed for this server."
069 * }
070 * </pre>
071 *
072 * <p>Related specifications:
073 *
074 * <ul>
075 *     <li>OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591), section
076 *         3.2.2.
077 *     <li>OAuth 2.0 Bearer Token Usage (RFC 6750), section 3.1.
078 * </ul>
079 */
080@Immutable
081public class ClientRegistrationErrorResponse 
082        extends ClientRegistrationResponse
083        implements ErrorResponse {
084
085
086        /**
087         * Gets the standard errors for a client registration error response.
088         *
089         * @return The standard errors, as a read-only set.
090         */
091        public static Set<ErrorObject> getStandardErrors() {
092                
093                Set<ErrorObject> stdErrors = new HashSet<>();
094                stdErrors.add(BearerTokenError.MISSING_TOKEN);
095                stdErrors.add(BearerTokenError.INVALID_REQUEST);
096                stdErrors.add(BearerTokenError.INVALID_TOKEN);
097                stdErrors.add(BearerTokenError.INSUFFICIENT_SCOPE);
098                stdErrors.add(RegistrationError.INVALID_REDIRECT_URI);
099                stdErrors.add(RegistrationError.INVALID_CLIENT_METADATA);
100                stdErrors.add(RegistrationError.INVALID_SOFTWARE_STATEMENT);
101                stdErrors.add(RegistrationError.UNAPPROVED_SOFTWARE_STATEMENT);
102
103                return Collections.unmodifiableSet(stdErrors);
104        }
105
106
107        /**
108         * The underlying error.
109         */
110        private final ErrorObject error;
111
112
113        /**
114         * Creates a new client registration error response.
115         *
116         * @param error The error. Should match one of the 
117         *              {@link #getStandardErrors standard errors} for a client
118         *              registration error response. Must not be {@code null}.
119         */
120        public ClientRegistrationErrorResponse(final ErrorObject error) {
121
122                if (error == null)
123                        throw new IllegalArgumentException("The error must not be null");
124
125                this.error = error;
126        }
127
128
129        @Override
130        public boolean indicatesSuccess() {
131
132                return false;
133        }
134
135
136        @Override
137        public ErrorObject getErrorObject() {
138
139                return error;
140        }
141
142
143        /**
144         * Returns the HTTP response for this client registration error 
145         * response.
146         *
147         * <p>Example HTTP response:
148         *
149         * <pre>
150         * HTTP/1.1 400 Bad Request
151         * Content-Type: application/json
152         * Cache-Control: no-store
153         * Pragma: no-cache
154         *
155         * {
156         *  "error":"invalid_redirect_uri",
157         *  "error_description":"The redirection URI of http://sketchy.example.com is not allowed for this server."
158         * }
159         * </pre>
160         *
161         * @return The HTTP response.
162         */
163        @Override
164        public HTTPResponse toHTTPResponse() {
165
166                HTTPResponse httpResponse;
167
168                if (error.getHTTPStatusCode() > 0) {
169                        httpResponse = new HTTPResponse(error.getHTTPStatusCode());
170                } else {
171                        httpResponse = new HTTPResponse(HTTPResponse.SC_BAD_REQUEST);
172                }
173
174                // Add the WWW-Authenticate header
175                if (error instanceof BearerTokenError) {
176
177                        BearerTokenError bte = (BearerTokenError)error;
178
179                        httpResponse.setWWWAuthenticate(bte.toWWWAuthenticateHeader());
180
181                } else {
182                        JSONObject jsonObject = new JSONObject();
183
184                        if (error.getCode() != null)
185                                jsonObject.put("error", error.getCode());
186
187                        if (error.getDescription() != null)
188                                jsonObject.put("error_description", error.getDescription());
189
190                        httpResponse.setContentType(CommonContentTypes.APPLICATION_JSON);
191
192                        httpResponse.setContent(jsonObject.toString());
193                }
194                
195                httpResponse.setCacheControl("no-store");
196                httpResponse.setPragma("no-cache");
197
198                return httpResponse;
199        }
200
201
202        /**
203         * Parses a client registration error response from the specified HTTP 
204         * response.
205         *
206         * <p>Note: The HTTP status code is not checked for matching the error
207         * code semantics.
208         *
209         * @param httpResponse The HTTP response to parse. Its status code must
210         *                     not be 200 (OK). Must not be {@code null}.
211         *
212         * @throws ParseException If the HTTP response couldn't be parsed to a
213         *                        client registration error response.
214         */
215        public static ClientRegistrationErrorResponse parse(final HTTPResponse httpResponse)
216                throws ParseException {
217                
218                httpResponse.ensureStatusCodeNotOK();
219
220                ErrorObject error;
221
222                String wwwAuth = httpResponse.getWWWAuthenticate();
223                
224                if (StringUtils.isNotBlank(wwwAuth)) {
225                        error = BearerTokenError.parse(wwwAuth);
226                } else {
227                        error = ErrorObject.parse(httpResponse);
228                }
229
230                return new ClientRegistrationErrorResponse(error);
231        }
232}