/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.security.api;

import io.undertow.server.HttpServerExchange;

The interface to be implemented by a single authentication mechanism.

The implementation of this interface are assumed to be stateless, if there is a need to share state between the authenticate and handleComplete calls then it should be held in the HttpServerExchange.

As an in-bound request is received the authenticate method is called on each mechanism in turn until one of the following occurs: - - A mechanism successfully authenticates the incoming request. - A mechanism attempts but fails to authenticate the request. - The list of mechanisms is exhausted.

This means that if the authenticate method is called on a mechanism it should assume it is required to check if it can actually authenticate the incoming request, anything that would prevent it from performing the check would have already stopped the authenticate method from being called.

Authentication is allowed to proceed if either authentication was required AND one handler authenticated the request or it is allowed to proceed if it is not required AND no handler failed to authenticate the request.

The handleComplete methods are used as the request processing is returning up the chain, primarily these are used to challenge the client to authenticate but where supported by the mechanism they could also be used to send mechanism specific updates back with a request.

If a mechanism successfully authenticated the incoming request then only the handleComplete method on that mechanism is called.

If any mechanism failed or if authentication was required and no mechanism succeeded in authenticating the request then handleComplete will be called for all mechanisms.

Finally if authentication was not required handleComplete will not be called for any of the mechanisms.

The mechanisms will need to double check why handleComplete is being called, if the request was authenticated then they should do nothing unless the mechanism has intermediate state to send back. If the request was not authenticated then a challenge should be sent.

Author:Stuart Douglas, Darran Lofthouse
/** * The interface to be implemented by a single authentication mechanism. * <p> * The implementation of this interface are assumed to be stateless, if there is a need to share state between the authenticate * and handleComplete calls then it should be held in the HttpServerExchange. * <p> * As an in-bound request is received the authenticate method is called on each mechanism in turn until one of the following * occurs: - - A mechanism successfully authenticates the incoming request. - A mechanism attempts but fails to authenticate the * request. - The list of mechanisms is exhausted. * <p> * This means that if the authenticate method is called on a mechanism it should assume it is required to check if it can * actually authenticate the incoming request, anything that would prevent it from performing the check would have already * stopped the authenticate method from being called. * <p> * Authentication is allowed to proceed if either authentication was required AND one handler authenticated the request or it is * allowed to proceed if it is not required AND no handler failed to authenticate the request. * <p> * The handleComplete methods are used as the request processing is returning up the chain, primarily these are used to * challenge the client to authenticate but where supported by the mechanism they could also be used to send mechanism specific * updates back with a request. * <p> * If a mechanism successfully authenticated the incoming request then only the handleComplete method on that mechanism is * called. * <p> * If any mechanism failed or if authentication was required and no mechanism succeeded in authenticating the request then * handleComplete will be called for all mechanisms. * <p> * Finally if authentication was not required handleComplete will not be called for any of the mechanisms. * <p> * The mechanisms will need to double check why handleComplete is being called, if the request was authenticated then they * should do nothing unless the mechanism has intermediate state to send back. If the request was not authenticated then a * challenge should be sent. * * @author Stuart Douglas * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a> */
public interface AuthenticationMechanism {
Perform authentication of the request. Any potentially blocking work should be performed in the handoff executor provided
Params:
  • exchange – The exchange
Returns:
/** * Perform authentication of the request. Any potentially blocking work should be performed in the handoff executor provided * * @param exchange The exchange * @return */
AuthenticationMechanismOutcome authenticate(final HttpServerExchange exchange, final SecurityContext securityContext);
Send an authentication challenge to the remote client.

The individual mechanisms should update the response headers and body of the message as appropriate however they should not set the response code, instead that should be indicated in the ChallengeResult and the most appropriate overall response code will be selected. This method should not return null.

Params:
  • exchange – The exchange
  • securityContext – The security context
Returns:A ChallengeResult indicating if a challenge was sent and the desired response code.
/** * Send an authentication challenge to the remote client. * <p> * The individual mechanisms should update the response headers and body of the message as appropriate however they should * not set the response code, instead that should be indicated in the {@link ChallengeResult} and the most appropriate * overall response code will be selected. * * This method should not return <code>null</code>. * * @param exchange The exchange * @param securityContext The security context * @return A {@link ChallengeResult} indicating if a challenge was sent and the desired response code. */
ChallengeResult sendChallenge(final HttpServerExchange exchange, final SecurityContext securityContext);
The AuthenticationOutcome is used by an AuthenticationMechanism to indicate the outcome of the call to authenticate, the overall authentication process will then used this along with the current AuthenticationState to decide how to proceed with the current request.
/** * The AuthenticationOutcome is used by an AuthenticationMechanism to indicate the outcome of the call to authenticate, the * overall authentication process will then used this along with the current AuthenticationState to decide how to proceed * with the current request. */
enum AuthenticationMechanismOutcome {
Based on the current request the mechanism has successfully performed authentication.
/** * Based on the current request the mechanism has successfully performed authentication. */
AUTHENTICATED,
The mechanism did not attempt authentication on this request, most likely due to not discovering any applicable security tokens for this mechanisms in the request.
/** * The mechanism did not attempt authentication on this request, most likely due to not discovering any applicable * security tokens for this mechanisms in the request. */
NOT_ATTEMPTED,
The mechanism attempted authentication but it did not complete, this could either be due to a failure validating the tokens from the client or it could be due to the mechanism requiring at least one additional round trip with the client - either way the request will return challenges to the client.
/** * The mechanism attempted authentication but it did not complete, this could either be due to a failure validating the * tokens from the client or it could be due to the mechanism requiring at least one additional round trip with the * client - either way the request will return challenges to the client. */
NOT_AUTHENTICATED; }
Simple class to wrap the result of requesting a mechanism sends it's challenge.
/** * Simple class to wrap the result of requesting a mechanism sends it's challenge. */
class ChallengeResult { public static final ChallengeResult NOT_SENT = new ChallengeResult(false); private final boolean challengeSent; private final Integer statusCode; public ChallengeResult(final boolean challengeSent, final Integer statusCode) { this.statusCode = statusCode; this.challengeSent = challengeSent; } public ChallengeResult(final boolean challengeSent) { this(challengeSent, null); }
Obtain the response code desired by this mechanism for the challenge.

Where multiple mechanisms are in use concurrently all of the requested response codes will be checked and the most suitable one selected. If no specific response code is required any value less than 0 can be set.

Returns:The desired response code or null if no code specified.
/** * Obtain the response code desired by this mechanism for the challenge. * <p> * Where multiple mechanisms are in use concurrently all of the requested response codes will be checked and the most * suitable one selected. If no specific response code is required any value less than 0 can be set. * * @return The desired response code or null if no code specified. */
public Integer getDesiredResponseCode() { return statusCode; }
Check if the mechanism did send a challenge.

Some mechanisms do not send a challenge and just rely on the correct information to authenticate a user being available in the request, in that case it would be normal for the mechanism to set this to false.

Returns:true if a challenge was sent, false otherwise.
/** * Check if the mechanism did send a challenge. * <p> * Some mechanisms do not send a challenge and just rely on the correct information to authenticate a user being * available in the request, in that case it would be normal for the mechanism to set this to false. * * @return true if a challenge was sent, false otherwise. */
public boolean isChallengeSent() { return challengeSent; } } }