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}