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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.oauth2.sdk.ErrorObject;
023import com.nimbusds.oauth2.sdk.ErrorResponse;
024import com.nimbusds.oauth2.sdk.ParseException;
025import com.nimbusds.oauth2.sdk.http.HTTPResponse;
026import com.nimbusds.oauth2.sdk.token.BearerTokenError;
027import com.nimbusds.oauth2.sdk.util.StringUtils;
028import net.jcip.annotations.Immutable;
029import net.minidev.json.JSONObject;
030
031import java.util.Collections;
032import java.util.HashSet;
033import java.util.Objects;
034import java.util.Set;
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)
077 *     <li>OAuth 2.0 Bearer Token Usage (RFC 6750)
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                this.error = Objects.requireNonNull(error);
123        }
124
125
126        @Override
127        public boolean indicatesSuccess() {
128
129                return false;
130        }
131
132
133        @Override
134        public ErrorObject getErrorObject() {
135
136                return error;
137        }
138
139
140        /**
141         * Returns the HTTP response for this client registration error 
142         * response.
143         *
144         * <p>Example HTTP response:
145         *
146         * <pre>
147         * HTTP/1.1 400 Bad Request
148         * Content-Type: application/json
149         * Cache-Control: no-store
150         * Pragma: no-cache
151         *
152         * {
153         *  "error":"invalid_redirect_uri",
154         *  "error_description":"The redirection URI of https://sketchy.example.com is not allowed for this server."
155         * }
156         * </pre>
157         *
158         * @return The HTTP response.
159         */
160        @Override
161        public HTTPResponse toHTTPResponse() {
162
163                HTTPResponse httpResponse;
164
165                if (error.getHTTPStatusCode() > 0) {
166                        httpResponse = new HTTPResponse(error.getHTTPStatusCode());
167                } else {
168                        httpResponse = new HTTPResponse(HTTPResponse.SC_BAD_REQUEST);
169                }
170
171                // Add the WWW-Authenticate header
172                if (error instanceof BearerTokenError) {
173
174                        BearerTokenError bte = (BearerTokenError)error;
175
176                        httpResponse.setWWWAuthenticate(bte.toWWWAuthenticateHeader());
177
178                } else {
179                        JSONObject jsonObject = new JSONObject();
180
181                        if (error.getCode() != null)
182                                jsonObject.put("error", error.getCode());
183
184                        if (error.getDescription() != null)
185                                jsonObject.put("error_description", error.getDescription());
186
187                        httpResponse.setEntityContentType(ContentType.APPLICATION_JSON);
188
189                        httpResponse.setBody(jsonObject.toString());
190                }
191                
192                httpResponse.setCacheControl("no-store");
193                httpResponse.setPragma("no-cache");
194
195                return httpResponse;
196        }
197
198
199        /**
200         * Parses a client registration error response from the specified HTTP 
201         * response.
202         *
203         * <p>Note: The HTTP status code is not checked for matching the error
204         * code semantics.
205         *
206         * @param httpResponse The HTTP response to parse. Its status code must
207         *                     not be 200 (OK). Must not be {@code null}.
208         *
209         * @return The client registration error response.
210         *
211         * @throws ParseException If the HTTP response couldn't be parsed to a
212         *                        client registration error response.
213         */
214        public static ClientRegistrationErrorResponse parse(final HTTPResponse httpResponse)
215                throws ParseException {
216                
217                httpResponse.ensureStatusCodeNotOK();
218
219                ErrorObject error;
220
221                String wwwAuth = httpResponse.getWWWAuthenticate();
222                
223                if (StringUtils.isNotBlank(wwwAuth)) {
224                        error = BearerTokenError.parse(wwwAuth);
225                } else {
226                        error = ErrorObject.parse(httpResponse);
227                }
228
229                return new ClientRegistrationErrorResponse(error);
230        }
231}