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