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.oauth2.sdk.token;
019
020
021import com.nimbusds.oauth2.sdk.ParseException;
022import com.nimbusds.oauth2.sdk.Scope;
023import com.nimbusds.oauth2.sdk.http.HTTPResponse;
024import net.jcip.annotations.Immutable;
025
026import java.net.URI;
027
028
029/**
030 * OAuth 2.0 bearer token error. Used to indicate that access to a resource 
031 * protected by a Bearer access token is denied, due to the request or token 
032 * being invalid, or due to the access token having insufficient scope.
033 *
034 * <p>Standard bearer access token errors:
035 *
036 * <ul>
037 *     <li>{@link #MISSING_TOKEN}
038 *     <li>{@link #INVALID_REQUEST}
039 *     <li>{@link #INVALID_TOKEN}
040 *     <li>{@link #INSUFFICIENT_SCOPE}
041 * </ul>
042 *
043 * <p>Example HTTP response:
044 *
045 * <pre>
046 * HTTP/1.1 401 Unauthorized
047 * WWW-Authenticate: Bearer realm="example.com",
048 *                   error="invalid_token",
049 *                   error_description="The access token expired"
050 * </pre>
051 *
052 * <p>Related specifications:
053 *
054 * <ul>
055 *     <li>OAuth 2.0 Bearer Token Usage (RFC 6750)
056 *     <li>Hypertext Transfer Protocol (HTTP/1.1): Authentication (RFC 7235)
057 * </ul>
058 */
059@Immutable
060public class BearerTokenError extends TokenSchemeError {
061        
062        
063        private static final long serialVersionUID = -5209789923955060584L;
064        
065        /**
066         * The request does not contain an access token. No error code or
067         * description is specified for this error, just the HTTP status code
068         * is set to 401 (Unauthorized).
069         *
070         * <p>Example:
071         *
072         * <pre>
073         * HTTP/1.1 401 Unauthorized
074         * WWW-Authenticate: Bearer
075         * </pre>
076         */
077        public static final BearerTokenError MISSING_TOKEN =
078                new BearerTokenError(null, null, HTTPResponse.SC_UNAUTHORIZED);
079        
080        
081        /**
082         * The request is missing a required parameter, includes an unsupported
083         * parameter or parameter value, repeats the same parameter, uses more
084         * than one method for including an access token, or is otherwise
085         * malformed. The HTTP status code is set to 400 (Bad Request).
086         */
087        public static final BearerTokenError INVALID_REQUEST =
088                new BearerTokenError("invalid_request", "Invalid request",
089                                     HTTPResponse.SC_BAD_REQUEST);
090        
091        
092        /**
093         * The access token provided is expired, revoked, malformed, or invalid
094         * for other reasons.  The HTTP status code is set to 401
095         * (Unauthorized).
096         */
097        public static final BearerTokenError INVALID_TOKEN =
098                new BearerTokenError("invalid_token", "Invalid access token",
099                                     HTTPResponse.SC_UNAUTHORIZED);
100        
101        
102        /**
103         * The request requires higher privileges than provided by the access
104         * token. The HTTP status code is set to 403 (Forbidden).
105         */
106        public static final BearerTokenError INSUFFICIENT_SCOPE =
107                new BearerTokenError("insufficient_scope", "Insufficient scope",
108                                     HTTPResponse.SC_FORBIDDEN);
109        
110        
111        /**
112         * Creates a new OAuth 2.0 bearer token error with the specified code
113         * and description.
114         *
115         * @param code        The error code, {@code null} if not specified.
116         * @param description The error description, {@code null} if not
117         *                    specified.
118         */
119        public BearerTokenError(final String code, final String description) {
120        
121                this(code, description, 0, null, null, null);
122        }
123
124
125        /**
126         * Creates a new OAuth 2.0 bearer token error with the specified code,
127         * description and HTTP status code.
128         *
129         * @param code           The error code, {@code null} if not specified.
130         * @param description    The error description, {@code null} if not
131         *                       specified.
132         * @param httpStatusCode The HTTP status code, zero if not specified.
133         */
134        public BearerTokenError(final String code, final String description, final int httpStatusCode) {
135        
136                this(code, description, httpStatusCode, null, null, null);
137        }
138
139
140        /**
141         * Creates a new OAuth 2.0 bearer token error with the specified code,
142         * description, HTTP status code, page URI, realm and scope.
143         *
144         * @param code           The error code, {@code null} if not specified.
145         * @param description    The error description, {@code null} if not
146         *                       specified.
147         * @param httpStatusCode The HTTP status code, zero if not specified.
148         * @param uri            The error page URI, {@code null} if not
149         *                       specified.
150         * @param realm          The realm, {@code null} if not specified.
151         * @param scope          The required scope, {@code null} if not 
152         *                       specified.
153         */
154        public BearerTokenError(final String code, 
155                                final String description, 
156                                final int httpStatusCode, 
157                                final URI uri,
158                                final String realm,
159                                final Scope scope) {
160        
161                super(AccessTokenType.BEARER, code, description, httpStatusCode, uri, realm, scope);
162        }
163
164
165        @Override
166        public BearerTokenError setDescription(final String description) {
167
168                return new BearerTokenError(super.getCode(), description, super.getHTTPStatusCode(), super.getURI(), getRealm(), getScope());
169        }
170
171
172        @Override
173        public BearerTokenError appendDescription(final String text) {
174
175                String newDescription;
176
177                if (getDescription() != null)
178                        newDescription = getDescription() + text;
179                else
180                        newDescription = text;
181
182                return new BearerTokenError(super.getCode(), newDescription, super.getHTTPStatusCode(), super.getURI(), getRealm(), getScope());
183        }
184
185
186        @Override
187        public BearerTokenError setHTTPStatusCode(final int httpStatusCode) {
188
189                return new BearerTokenError(super.getCode(), super.getDescription(), httpStatusCode, super.getURI(), getRealm(), getScope());
190        }
191
192
193        @Override
194        public BearerTokenError setURI(final URI uri) {
195
196                return new BearerTokenError(super.getCode(), super.getDescription(), super.getHTTPStatusCode(), uri, getRealm(), getScope());
197        }
198
199
200        @Override
201        public BearerTokenError setRealm(final String realm) {
202
203                return new BearerTokenError(getCode(), 
204                                            getDescription(), 
205                                            getHTTPStatusCode(), 
206                                            getURI(), 
207                                            realm, 
208                                            getScope());
209        }
210
211
212        @Override
213        public BearerTokenError setScope(final Scope scope) {
214
215                return new BearerTokenError(getCode(),
216                                            getDescription(),
217                                            getHTTPStatusCode(),
218                                            getURI(),
219                                            getRealm(),
220                                            scope);
221        }
222
223
224        /**
225         * Parses an OAuth 2.0 bearer token error from the specified HTTP
226         * response {@code WWW-Authenticate} header.
227         *
228         * @param wwwAuth The {@code WWW-Authenticate} header value to parse. 
229         *                Must not be {@code null}.
230         *
231         * @return The bearer token error.
232         *
233         * @throws ParseException If the {@code WWW-Authenticate} header value 
234         *                        couldn't be parsed to a Bearer token error.
235         */
236        public static BearerTokenError parse(final String wwwAuth)
237                throws ParseException {
238                
239                TokenSchemeError genericError = TokenSchemeError.parse(wwwAuth, AccessTokenType.BEARER);
240                
241                return new BearerTokenError(
242                        genericError.getCode(),
243                        genericError.getDescription(),
244                        genericError.getHTTPStatusCode(),
245                        genericError.getURI(),
246                        genericError.getRealm(),
247                        genericError.getScope()
248                );
249        }
250}