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        /**
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         * The request is missing a required parameter, includes an unsupported
082         * parameter or parameter value, repeats the same parameter, uses more
083         * than one method for including an access token, or is otherwise 
084         * malformed. The HTTP status code is set to 400 (Bad Request).
085         */
086        public static final BearerTokenError INVALID_REQUEST = 
087                new BearerTokenError("invalid_request", "Invalid request", 
088                                     HTTPResponse.SC_BAD_REQUEST);
089
090
091        /**
092         * The access token provided is expired, revoked, malformed, or invalid
093         * for other reasons.  The HTTP status code is set to 401 
094         * (Unauthorized).
095         */
096        public static final BearerTokenError INVALID_TOKEN =
097                new BearerTokenError("invalid_token", "Invalid access token", 
098                                     HTTPResponse.SC_UNAUTHORIZED);
099        
100        
101        /**
102         * The request requires higher privileges than provided by the access 
103         * token. The HTTP status code is set to 403 (Forbidden).
104         */
105        public static final BearerTokenError INSUFFICIENT_SCOPE =
106                new BearerTokenError("insufficient_scope", "Insufficient scope", 
107                                     HTTPResponse.SC_FORBIDDEN);
108        
109        
110        /**
111         * Creates a new OAuth 2.0 bearer token error with the specified code
112         * and description.
113         *
114         * @param code        The error code, {@code null} if not specified.
115         * @param description The error description, {@code null} if not
116         *                    specified.
117         */
118        public BearerTokenError(final String code, final String description) {
119        
120                this(code, description, 0, null, null, null);
121        }
122
123
124        /**
125         * Creates a new OAuth 2.0 bearer token error with the specified code,
126         * description and HTTP status code.
127         *
128         * @param code           The error code, {@code null} if not specified.
129         * @param description    The error description, {@code null} if not
130         *                       specified.
131         * @param httpStatusCode The HTTP status code, zero if not specified.
132         */
133        public BearerTokenError(final String code, final String description, final int httpStatusCode) {
134        
135                this(code, description, httpStatusCode, null, null, null);
136        }
137
138
139        /**
140         * Creates a new OAuth 2.0 bearer token error with the specified code,
141         * description, HTTP status code, page URI, realm and scope.
142         *
143         * @param code           The error code, {@code null} if not specified.
144         * @param description    The error description, {@code null} if not
145         *                       specified.
146         * @param httpStatusCode The HTTP status code, zero if not specified.
147         * @param uri            The error page URI, {@code null} if not
148         *                       specified.
149         * @param realm          The realm, {@code null} if not specified.
150         * @param scope          The required scope, {@code null} if not 
151         *                       specified.
152         */
153        public BearerTokenError(final String code, 
154                                final String description, 
155                                final int httpStatusCode, 
156                                final URI uri,
157                                final String realm,
158                                final Scope scope) {
159        
160                super(AccessTokenType.BEARER, code, description, httpStatusCode, uri, realm, scope);
161        }
162
163
164        @Override
165        public BearerTokenError setDescription(final String description) {
166
167                return new BearerTokenError(super.getCode(), description, super.getHTTPStatusCode(), super.getURI(), getRealm(), getScope());
168        }
169
170
171        @Override
172        public BearerTokenError appendDescription(final String text) {
173
174                String newDescription;
175
176                if (getDescription() != null)
177                        newDescription = getDescription() + text;
178                else
179                        newDescription = text;
180
181                return new BearerTokenError(super.getCode(), newDescription, super.getHTTPStatusCode(), super.getURI(), getRealm(), getScope());
182        }
183
184
185        @Override
186        public BearerTokenError setHTTPStatusCode(final int httpStatusCode) {
187
188                return new BearerTokenError(super.getCode(), super.getDescription(), httpStatusCode, super.getURI(), getRealm(), getScope());
189        }
190
191
192        @Override
193        public BearerTokenError setURI(final URI uri) {
194
195                return new BearerTokenError(super.getCode(), super.getDescription(), super.getHTTPStatusCode(), uri, getRealm(), getScope());
196        }
197
198
199        @Override
200        public BearerTokenError setRealm(final String realm) {
201
202                return new BearerTokenError(getCode(), 
203                                            getDescription(), 
204                                            getHTTPStatusCode(), 
205                                            getURI(), 
206                                            realm, 
207                                            getScope());
208        }
209
210
211        @Override
212        public BearerTokenError setScope(final Scope scope) {
213
214                return new BearerTokenError(getCode(),
215                                            getDescription(),
216                                            getHTTPStatusCode(),
217                                            getURI(),
218                                            getRealm(),
219                                            scope);
220        }
221
222
223        /**
224         * Parses an OAuth 2.0 bearer token error from the specified HTTP
225         * response {@code WWW-Authenticate} header.
226         *
227         * @param wwwAuth The {@code WWW-Authenticate} header value to parse. 
228         *                Must not be {@code null}.
229         *
230         * @return The bearer token error.
231         *
232         * @throws ParseException If the {@code WWW-Authenticate} header value 
233         *                        couldn't be parsed to a Bearer token error.
234         */
235        public static BearerTokenError parse(final String wwwAuth)
236                throws ParseException {
237                
238                TokenSchemeError genericError = TokenSchemeError.parse(wwwAuth, AccessTokenType.BEARER);
239                
240                return new BearerTokenError(
241                        genericError.getCode(),
242                        genericError.getDescription(),
243                        genericError.getHTTPStatusCode(),
244                        genericError.getURI(),
245                        genericError.getRealm(),
246                        genericError.getScope()
247                );
248        }
249}