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; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import net.jcip.annotations.Immutable; 029import net.minidev.json.JSONObject; 030 031import com.nimbusds.oauth2.sdk.http.HTTPResponse; 032import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 033import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils; 034 035 036/** 037 * Error object, used to encapsulate OAuth 2.0 and other errors. 038 * 039 * <p>Example error object as HTTP response: 040 * 041 * <pre> 042 * HTTP/1.1 400 Bad Request 043 * Content-Type: application/json;charset=UTF-8 044 * Cache-Control: no-store 045 * Pragma: no-cache 046 * 047 * { 048 * "error" : "invalid_request" 049 * } 050 * </pre> 051 */ 052@Immutable 053public class ErrorObject { 054 055 056 /** 057 * The error code, may not always be defined. 058 */ 059 private final String code; 060 061 062 /** 063 * Optional error description. 064 */ 065 private final String description; 066 067 068 /** 069 * Optional HTTP status code, 0 if not specified. 070 */ 071 private final int httpStatusCode; 072 073 074 /** 075 * Optional URI of a web page that includes additional information 076 * about the error. 077 */ 078 private final URI uri; 079 080 081 /** 082 * Creates a new error with the specified code. 083 * 084 * @param code The error code, {@code null} if not specified. 085 */ 086 public ErrorObject(final String code) { 087 088 this(code, null, 0, null); 089 } 090 091 092 /** 093 * Creates a new error with the specified code and description. 094 * 095 * @param code The error code, {@code null} if not specified. 096 * @param description The error description, {@code null} if not 097 * specified. 098 */ 099 public ErrorObject(final String code, final String description) { 100 101 this(code, description, 0, null); 102 } 103 104 105 /** 106 * Creates a new error with the specified code, description and HTTP 107 * status code. 108 * 109 * @param code The error code, {@code null} if not specified. 110 * @param description The error description, {@code null} if not 111 * specified. 112 * @param httpStatusCode The HTTP status code, zero if not specified. 113 */ 114 public ErrorObject(final String code, final String description, 115 final int httpStatusCode) { 116 117 this(code, description, httpStatusCode, null); 118 } 119 120 121 /** 122 * Creates a new error with the specified code, description, HTTP 123 * status code and page URI. 124 * 125 * @param code The error code, {@code null} if not specified. 126 * @param description The error description, {@code null} if not 127 * specified. 128 * @param httpStatusCode The HTTP status code, zero if not specified. 129 * @param uri The error page URI, {@code null} if not 130 * specified. 131 */ 132 public ErrorObject(final String code, final String description, 133 final int httpStatusCode, final URI uri) { 134 135 this.code = code; 136 this.description = description; 137 this.httpStatusCode = httpStatusCode; 138 this.uri = uri; 139 } 140 141 142 /** 143 * Gets the error code. 144 * 145 * @return The error code, {@code null} if not specified. 146 */ 147 public String getCode() { 148 149 return code; 150 } 151 152 153 /** 154 * Gets the error description. 155 * 156 * @return The error description, {@code null} if not specified. 157 */ 158 public String getDescription() { 159 160 return description; 161 } 162 163 164 /** 165 * Sets the error description. 166 * 167 * @param description The error description, {@code null} if not 168 * specified. 169 * 170 * @return A copy of this error with the specified description. 171 */ 172 public ErrorObject setDescription(final String description) { 173 174 return new ErrorObject(getCode(), description, getHTTPStatusCode(), getURI()); 175 } 176 177 178 /** 179 * Appends the specified text to the error description. 180 * 181 * @param text The text to append to the error description, 182 * {@code null} if not specified. 183 * 184 * @return A copy of this error with the specified appended 185 * description. 186 */ 187 public ErrorObject appendDescription(final String text) { 188 189 String newDescription; 190 191 if (getDescription() != null) 192 newDescription = getDescription() + text; 193 else 194 newDescription = text; 195 196 return new ErrorObject(getCode(), newDescription, getHTTPStatusCode(), getURI()); 197 } 198 199 200 /** 201 * Gets the HTTP status code. 202 * 203 * @return The HTTP status code, zero if not specified. 204 */ 205 public int getHTTPStatusCode() { 206 207 return httpStatusCode; 208 } 209 210 211 /** 212 * Sets the HTTP status code. 213 * 214 * @param httpStatusCode The HTTP status code, zero if not specified. 215 * 216 * @return A copy of this error with the specified HTTP status code. 217 */ 218 public ErrorObject setHTTPStatusCode(final int httpStatusCode) { 219 220 return new ErrorObject(getCode(), getDescription(), httpStatusCode, getURI()); 221 } 222 223 224 /** 225 * Gets the error page URI. 226 * 227 * @return The error page URI, {@code null} if not specified. 228 */ 229 public URI getURI() { 230 231 return uri; 232 } 233 234 235 /** 236 * Sets the error page URI. 237 * 238 * @param uri The error page URI, {@code null} if not specified. 239 * 240 * @return A copy of this error with the specified page URI. 241 */ 242 public ErrorObject setURI(final URI uri) { 243 244 return new ErrorObject(getCode(), getDescription(), getHTTPStatusCode(), uri); 245 } 246 247 248 /** 249 * Returns a JSON object representation of this error object. 250 * 251 * <p>Example: 252 * 253 * <pre> 254 * { 255 * "error" : "invalid_grant", 256 * "error_description" : "Invalid resource owner credentials" 257 * } 258 * </pre> 259 * 260 * @return The JSON object. 261 */ 262 public JSONObject toJSONObject() { 263 264 JSONObject o = new JSONObject(); 265 266 if (code != null) { 267 o.put("error", code); 268 } 269 270 if (description != null) { 271 o.put("error_description", description); 272 } 273 274 if (uri != null) { 275 o.put("error_uri", uri.toString()); 276 } 277 278 return o; 279 } 280 281 282 /** 283 * Returns a parameters representation of this error object. Suitable 284 * for URL-encoded error responses. 285 * 286 * @return The parameters. 287 */ 288 public Map<String, List<String>> toParameters() { 289 290 Map<String,List<String>> params = new HashMap<>(); 291 292 if (getCode() != null) { 293 params.put("error", Collections.singletonList(getCode())); 294 } 295 296 if (getDescription() != null) { 297 params.put("error_description", Collections.singletonList(getDescription())); 298 } 299 300 if (getURI() != null) { 301 params.put("error_uri", Collections.singletonList(getURI().toString())); 302 } 303 304 return params; 305 } 306 307 308 /** 309 * @see #getCode 310 */ 311 @Override 312 public String toString() { 313 314 return code != null ? code : "null"; 315 } 316 317 318 @Override 319 public int hashCode() { 320 321 return code != null ? code.hashCode() : "null".hashCode(); 322 } 323 324 325 @Override 326 public boolean equals(final Object object) { 327 328 return object instanceof ErrorObject && 329 this.toString().equals(object.toString()); 330 } 331 332 333 /** 334 * Parses an error object from the specified JSON object. 335 * 336 * @param jsonObject The JSON object to parse. Must not be 337 * {@code null}. 338 * 339 * @return The error object. 340 */ 341 public static ErrorObject parse(final JSONObject jsonObject) { 342 343 String code = null; 344 try { 345 code = JSONObjectUtils.getString(jsonObject, "error", null); 346 } catch (ParseException e) { 347 // ignore and continue 348 } 349 350 String description = null; 351 try { 352 description = JSONObjectUtils.getString(jsonObject, "error_description", null); 353 } catch (ParseException e) { 354 // ignore and continue 355 } 356 357 URI uri = null; 358 try { 359 uri = JSONObjectUtils.getURI(jsonObject, "error_uri", null); 360 } catch (ParseException e) { 361 // ignore and continue 362 } 363 364 return new ErrorObject(code, description, 0, uri); 365 } 366 367 368 /** 369 * Parses an error object from the specified parameters representation. 370 * Suitable for URL-encoded error responses. 371 * 372 * @param params The parameters. Must not be {@code null}. 373 * 374 * @return The error object. 375 */ 376 public static ErrorObject parse(final Map<String, List<String>> params) { 377 378 String code = MultivaluedMapUtils.getFirstValue(params, "error"); 379 String description = MultivaluedMapUtils.getFirstValue(params, "error_description"); 380 String uriString = MultivaluedMapUtils.getFirstValue(params, "error_uri"); 381 382 URI uri = null; 383 if (uriString != null) { 384 try { 385 uri = new URI(uriString); 386 } catch (URISyntaxException e) { 387 // ignore 388 } 389 } 390 391 return new ErrorObject(code, description, 0, uri); 392 } 393 394 395 /** 396 * Parses an error object from the specified HTTP response. 397 * 398 * @param httpResponse The HTTP response to parse. Must not be 399 * {@code null}. 400 * 401 * @return The error object. 402 */ 403 public static ErrorObject parse(final HTTPResponse httpResponse) { 404 405 JSONObject jsonObject; 406 try { 407 jsonObject = httpResponse.getContentAsJSONObject(); 408 } catch (ParseException e) { 409 return new ErrorObject(null, null, httpResponse.getStatusCode()); 410 } 411 412 ErrorObject intermediary = parse(jsonObject); 413 414 return new ErrorObject( 415 intermediary.getCode(), 416 intermediary.description, 417 httpResponse.getStatusCode(), 418 intermediary.getURI()); 419 } 420}