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