1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2014 Red Hat, Inc., and individual contributors
4 * as indicated by the @author tags.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package io.undertow.security.api;
20
21 import io.undertow.server.HttpServerExchange;
22
23 /**
24 * The interface to be implemented by a single authentication mechanism.
25 * <p>
26 * The implementation of this interface are assumed to be stateless, if there is a need to share state between the authenticate
27 * and handleComplete calls then it should be held in the HttpServerExchange.
28 * <p>
29 * As an in-bound request is received the authenticate method is called on each mechanism in turn until one of the following
30 * occurs: - - A mechanism successfully authenticates the incoming request. - A mechanism attempts but fails to authenticate the
31 * request. - The list of mechanisms is exhausted.
32 * <p>
33 * This means that if the authenticate method is called on a mechanism it should assume it is required to check if it can
34 * actually authenticate the incoming request, anything that would prevent it from performing the check would have already
35 * stopped the authenticate method from being called.
36 * <p>
37 * Authentication is allowed to proceed if either authentication was required AND one handler authenticated the request or it is
38 * allowed to proceed if it is not required AND no handler failed to authenticate the request.
39 * <p>
40 * The handleComplete methods are used as the request processing is returning up the chain, primarily these are used to
41 * challenge the client to authenticate but where supported by the mechanism they could also be used to send mechanism specific
42 * updates back with a request.
43 * <p>
44 * If a mechanism successfully authenticated the incoming request then only the handleComplete method on that mechanism is
45 * called.
46 * <p>
47 * If any mechanism failed or if authentication was required and no mechanism succeeded in authenticating the request then
48 * handleComplete will be called for all mechanisms.
49 * <p>
50 * Finally if authentication was not required handleComplete will not be called for any of the mechanisms.
51 * <p>
52 * The mechanisms will need to double check why handleComplete is being called, if the request was authenticated then they
53 * should do nothing unless the mechanism has intermediate state to send back. If the request was not authenticated then a
54 * challenge should be sent.
55 *
56 * @author Stuart Douglas
57 * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
58 */
59 public interface AuthenticationMechanism {
60
61 /**
62 * Perform authentication of the request. Any potentially blocking work should be performed in the handoff executor provided
63 *
64 * @param exchange The exchange
65 * @return
66 */
67 AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange,
68 final SecurityContext securityContext);
69
70 /**
71 * Send an authentication challenge to the remote client.
72 * <p>
73 * The individual mechanisms should update the response headers and body of the message as appropriate however they should
74 * not set the response code, instead that should be indicated in the {@link ChallengeResult} and the most appropriate
75 * overall response code will be selected.
76 *
77 * This method should not return <code>null</code>.
78 *
79 * @param exchange The exchange
80 * @param securityContext The security context
81 * @return A {@link ChallengeResult} indicating if a challenge was sent and the desired response code.
82 */
83 ChallengeResult sendChallenge(final HttpServerExchange exchange, final SecurityContext securityContext);
84
85 /**
86 * The AuthenticationOutcome is used by an AuthenticationMechanism to indicate the outcome of the call to authenticate, the
87 * overall authentication process will then used this along with the current AuthenticationState to decide how to proceed
88 * with the current request.
89 */
90 enum AuthenticationMechanismOutcome {
91 /**
92 * Based on the current request the mechanism has successfully performed authentication.
93 */
94 AUTHENTICATED,
95
96 /**
97 * The mechanism did not attempt authentication on this request, most likely due to not discovering any applicable
98 * security tokens for this mechanisms in the request.
99 */
100 NOT_ATTEMPTED,
101
102 /**
103 * The mechanism attempted authentication but it did not complete, this could either be due to a failure validating the
104 * tokens from the client or it could be due to the mechanism requiring at least one additional round trip with the
105 * client - either way the request will return challenges to the client.
106 */
107 NOT_AUTHENTICATED;
108 }
109
110 /**
111 * Simple class to wrap the result of requesting a mechanism sends it's challenge.
112 */
113 class ChallengeResult {
114
115 public static final ChallengeResult NOT_SENT = new ChallengeResult(false);
116
117 private final boolean challengeSent;
118 private final Integer statusCode;
119
120 public ChallengeResult(final boolean challengeSent, final Integer statusCode) {
121 this.statusCode = statusCode;
122 this.challengeSent = challengeSent;
123 }
124
125 public ChallengeResult(final boolean challengeSent) {
126 this(challengeSent, null);
127 }
128
129 /**
130 * Obtain the response code desired by this mechanism for the challenge.
131 * <p>
132 * Where multiple mechanisms are in use concurrently all of the requested response codes will be checked and the most
133 * suitable one selected. If no specific response code is required any value less than 0 can be set.
134 *
135 * @return The desired response code or null if no code specified.
136 */
137 public Integer getDesiredResponseCode() {
138 return statusCode;
139 }
140
141 /**
142 * Check if the mechanism did send a challenge.
143 * <p>
144 * Some mechanisms do not send a challenge and just rely on the correct information to authenticate a user being
145 * available in the request, in that case it would be normal for the mechanism to set this to false.
146 *
147 * @return true if a challenge was sent, false otherwise.
148 */
149 public boolean isChallengeSent() {
150 return challengeSent;
151 }
152
153 }
154
155 }
156