001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.auth;
018
019import org.apache.juneau.http.header.*;
020import org.apache.juneau.http.response.*;
021
022/**
023 * Specialization of {@link Unauthorized} thrown by {@link BearerTokenGuard}, {@link ApiKeyGuard}, and
024 * user-supplied {@link TokenValidator} / {@link ApiKeyStore} implementations to signal AuthN failure.
025 *
026 * <p>
027 * Behaves like a plain {@code 401 Unauthorized} from the wire's perspective &mdash; same status code,
028 * same reason phrase, same JSON / problem-details body shape &mdash; but adds a typed
029 * {@link #wwwAuthenticate(String)} fluent setter that returns {@code AuthenticationException} (instead
030 * of the generic {@link BasicHttpException} returned by the inherited {@code setHeader(...)} chain) so
031 * authoring AuthN guards stays terse:
032 *
033 * <p class='bjava'>
034 *    <jk>throw new</jk> AuthenticationException(<js>"Bearer token missing"</js>)
035 *       .wwwAuthenticate(<js>"Bearer realm=\"api\""</js>);
036 * </p>
037 *
038 * <p>
039 * The {@code WWW-Authenticate} response header is mandated by RFC 7235 &sect;4.1 for every {@code 401}
040 * response &mdash; clients use the challenge string to discover which authentication schemes the server
041 * supports and to format their next request. AuthN guards in this package set it on every rejection.
042 *
043 * <h5 class='section'>See Also:</h5><ul>
044 *    <li class='jc'>{@link Unauthorized}
045 *    <li class='jc'>{@link BearerTokenGuard}
046 *    <li class='jc'>{@link ApiKeyGuard}
047 *    <li class='link'><a class="doclink" href="https://datatracker.ietf.org/doc/html/rfc7235#section-4.1">RFC 7235 &sect;4.1 &mdash; WWW-Authenticate</a>
048 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerAuthGuards">AuthN Guards — Bearer / API-Key / JWT</a>
049 * </ul>
050 *
051 * @since 10.0.0
052 */
053@SuppressWarnings({
054   "java:S110" // Deep inheritance inherent to the exception hierarchy
055})
056public class AuthenticationException extends Unauthorized {
057
058   private static final long serialVersionUID = 1L;
059
060   /**
061    * Constructor with no message.
062    */
063   public AuthenticationException() {
064      super();
065   }
066
067   /**
068    * Constructor with a {@link String#format(String, Object...) String.format}-style message.
069    *
070    * @param msg The detail message. May be <jk>null</jk>.
071    *    Treated as a format pattern when {@code args} is non-empty.
072    * @param args Optional message arguments.
073    */
074   public AuthenticationException(String msg, Object...args) {
075      super(msg, args);
076   }
077
078   /**
079    * Constructor with a cause.
080    *
081    * @param cause The cause. May be <jk>null</jk>.
082    */
083   public AuthenticationException(Throwable cause) {
084      super(cause);
085   }
086
087   /**
088    * Constructor with a cause and a {@link String#format(String, Object...) String.format}-style message.
089    *
090    * @param cause The cause. May be <jk>null</jk>.
091    * @param msg The detail message. May be <jk>null</jk>.
092    *    Treated as a format pattern when {@code args} is non-empty.
093    * @param args Optional message arguments.
094    */
095   public AuthenticationException(Throwable cause, String msg, Object...args) {
096      super(cause, msg, args);
097   }
098
099   /**
100    * Sets the {@code WWW-Authenticate} response header to the supplied challenge string.
101    *
102    * <p>
103    * Per RFC 7235 &sect;4.1, the challenge identifies the authentication scheme(s) the server accepts
104    * and any scheme-specific parameters (e.g. {@code realm}). Examples:
105    *
106    * <ul>
107    *    <li>{@code Bearer realm="api"}
108    *    <li>{@code Bearer realm="api", error="invalid_token", error_description="The access token expired"}
109    *    <li>{@code ApiKey realm="api"}
110    * </ul>
111    *
112    * @param value The {@code WWW-Authenticate} challenge value. Must not be <jk>null</jk>.
113    * @return This object.
114    */
115   public AuthenticationException wwwAuthenticate(String value) {
116      setHeader(WwwAuthenticate.of(value));
117      return this;
118   }
119}