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<BearerTokenError>(); 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 ErrorObject getErrorObject() { 104 105 return error; 106 } 107 108 109 /** 110 * Returns the HTTP response for this UserInfo error response. 111 * 112 * <p>Example HTTP response: 113 * 114 * <pre> 115 * HTTP/1.1 401 Unauthorized 116 * WWW-Authenticate: Bearer realm="example.com", 117 * error="invalid_token", 118 * error_description="The access token expired" 119 * </pre> 120 * 121 * @return The HTTP response matching this UserInfo error response. 122 */ 123 @Override 124 public HTTPResponse toHTTPResponse() { 125 126 HTTPResponse httpResponse; 127 128 if (error.getHTTPStatusCode() > 0) 129 httpResponse = new HTTPResponse(error.getHTTPStatusCode()); 130 else 131 httpResponse = new HTTPResponse(HTTPResponse.SC_BAD_REQUEST); 132 133 // Add the WWW-Authenticate header 134 if (error != null) 135 httpResponse.setWWWAuthenticate(error.toWWWAuthenticateHeader()); 136 137 return httpResponse; 138 } 139 140 141 /** 142 * Parses a UserInfo error response from the specified HTTP response 143 * {@code WWW-Authenticate} header. 144 * 145 * @param wwwAuth The {@code WWW-Authenticate} header value to parse. 146 * Must not be {@code null}. 147 * 148 * @throws ParseException If the {@code WWW-Authenticate} header value 149 * couldn't be parsed to a UserInfo error 150 * response. 151 */ 152 public static UserInfoErrorResponse parse(final String wwwAuth) 153 throws ParseException { 154 155 BearerTokenError error = BearerTokenError.parse(wwwAuth); 156 157 return new UserInfoErrorResponse(error); 158 } 159 160 161 /** 162 * Parses a UserInfo error response from the specified HTTP response. 163 * 164 * <p>Note: The HTTP status code is not checked for matching the error 165 * code semantics. 166 * 167 * @param httpResponse The HTTP response to parse. Its status code must 168 * not be 200 (OK). Must not be {@code null}. 169 * 170 * @throws ParseException If the HTTP response couldn't be parsed to a 171 * UserInfo error response. 172 */ 173 public static UserInfoErrorResponse parse(final HTTPResponse httpResponse) 174 throws ParseException { 175 176 httpResponse.ensureStatusCodeNotOK(); 177 178 String wwwAuth = httpResponse.getWWWAuthenticate(); 179 180 if (StringUtils.isNotBlank(wwwAuth)) 181 parse(wwwAuth); 182 183 return new UserInfoErrorResponse(); 184 } 185}