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