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.openid.connect.sdk; 019 020 021import java.util.Collections; 022import java.util.HashSet; 023import java.util.Set; 024 025import com.nimbusds.oauth2.sdk.ErrorObject; 026import com.nimbusds.oauth2.sdk.ErrorResponse; 027import com.nimbusds.oauth2.sdk.ParseException; 028import com.nimbusds.oauth2.sdk.http.CommonContentTypes; 029import com.nimbusds.oauth2.sdk.http.HTTPResponse; 030import com.nimbusds.oauth2.sdk.token.BearerTokenError; 031import com.nimbusds.oauth2.sdk.util.StringUtils; 032import net.jcip.annotations.Immutable; 033 034 035/** 036 * UserInfo error response. 037 * 038 * <p>Standard OAuth 2.0 Bearer Token errors: 039 * 040 * <ul> 041 * <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#MISSING_TOKEN} 042 * <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INVALID_REQUEST} 043 * <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INVALID_TOKEN} 044 * <li>{@link com.nimbusds.oauth2.sdk.token.BearerTokenError#INSUFFICIENT_SCOPE} 045 * </ul> 046 * 047 * <p>Example HTTP response: 048 * 049 * <pre> 050 * HTTP/1.1 401 Unauthorized 051 * WWW-Authenticate: Bearer realm="example.com", 052 * error="invalid_token", 053 * error_description="The access token expired" 054 * </pre> 055 * 056 * <p>Related specifications: 057 * 058 * <ul> 059 * <li>OpenID Connect Core 1.0, section 5.3.3. 060 * <li>OAuth 2.0 Bearer Token Usage (RFC 6750), section 3.1. 061 * </ul> 062 */ 063@Immutable 064public class UserInfoErrorResponse 065 extends UserInfoResponse 066 implements ErrorResponse { 067 068 069 /** 070 * Gets the standard errors for a UserInfo error response. 071 * 072 * @return The standard errors, as a read-only set. 073 */ 074 public static Set<BearerTokenError> getStandardErrors() { 075 076 Set<BearerTokenError> stdErrors = new HashSet<>(); 077 stdErrors.add(BearerTokenError.MISSING_TOKEN); 078 stdErrors.add(BearerTokenError.INVALID_REQUEST); 079 stdErrors.add(BearerTokenError.INVALID_TOKEN); 080 stdErrors.add(BearerTokenError.INSUFFICIENT_SCOPE); 081 082 return Collections.unmodifiableSet(stdErrors); 083 } 084 085 086 /** 087 * The underlying error. 088 */ 089 private final ErrorObject error; 090 091 092 /** 093 * Creates a new UserInfo error response. No OAuth 2.0 bearer token 094 * error / general error object is specified. 095 */ 096 private UserInfoErrorResponse() { 097 098 error = null; 099 } 100 101 102 /** 103 * Creates a new UserInfo error response indicating a bearer token 104 * error. 105 * 106 * @param error The OAuth 2.0 bearer token error. Should match one of 107 * the {@link #getStandardErrors standard errors} for a 108 * UserInfo error response. Must not be {@code null}. 109 */ 110 public UserInfoErrorResponse(final BearerTokenError error) { 111 112 this((ErrorObject) error); 113 } 114 115 116 /** 117 * Creates a new UserInfo error response indicating a general error. 118 * 119 * @param error The error. Must not be {@code null}. 120 */ 121 public UserInfoErrorResponse(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 UserInfo error response. 146 * 147 * <p>Example HTTP response: 148 * 149 * <pre> 150 * HTTP/1.1 401 Unauthorized 151 * WWW-Authenticate: Bearer realm="example.com", 152 * error="invalid_token", 153 * error_description="The access token expired" 154 * </pre> 155 * 156 * @return The HTTP response matching this UserInfo error response. 157 */ 158 @Override 159 public HTTPResponse toHTTPResponse() { 160 161 HTTPResponse httpResponse; 162 163 if (error != null && error.getHTTPStatusCode() > 0) { 164 httpResponse = new HTTPResponse(error.getHTTPStatusCode()); 165 } else { 166 httpResponse = new HTTPResponse(HTTPResponse.SC_BAD_REQUEST); 167 } 168 169 // Add the WWW-Authenticate header 170 if (error instanceof BearerTokenError) { 171 httpResponse.setWWWAuthenticate(((BearerTokenError) error).toWWWAuthenticateHeader()); 172 } else if (error != null){ 173 httpResponse.setContentType(CommonContentTypes.APPLICATION_JSON); 174 httpResponse.setContent(error.toJSONObject().toJSONString()); 175 } 176 177 return httpResponse; 178 } 179 180 181 /** 182 * Parses a UserInfo error response from the specified HTTP response 183 * {@code WWW-Authenticate} header. 184 * 185 * @param wwwAuth The {@code WWW-Authenticate} header value to parse. 186 * Must not be {@code null}. 187 * 188 * @return The UserInfo error response. 189 * 190 * @throws ParseException If the {@code WWW-Authenticate} header value 191 * couldn't be parsed to a UserInfo error 192 * response. 193 */ 194 public static UserInfoErrorResponse parse(final String wwwAuth) 195 throws ParseException { 196 197 BearerTokenError error = BearerTokenError.parse(wwwAuth); 198 199 return new UserInfoErrorResponse(error); 200 } 201 202 203 /** 204 * Parses a UserInfo error response from the specified HTTP response. 205 * 206 * <p>Note: The HTTP status code is not checked for matching the error 207 * code semantics. 208 * 209 * @param httpResponse The HTTP response to parse. Its status code must 210 * not be 200 (OK). Must not be {@code null}. 211 * 212 * @return The UserInfo error response. 213 * 214 * @throws ParseException If the HTTP response couldn't be parsed to a 215 * UserInfo error response. 216 */ 217 public static UserInfoErrorResponse parse(final HTTPResponse httpResponse) 218 throws ParseException { 219 220 httpResponse.ensureStatusCodeNotOK(); 221 222 String wwwAuth = httpResponse.getWWWAuthenticate(); 223 224 if (StringUtils.isNotBlank(wwwAuth)) { 225 // Bearer token error? 226 return parse(wwwAuth); 227 } 228 229 // Other error? 230 return new UserInfoErrorResponse(ErrorObject.parse(httpResponse)); 231 } 232}