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