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