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