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}