All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.keyvault.authentication.KeyVaultCredentials Maven / Gradle / Ivy

There is a newer version: 1.2.6
Show newest version
/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

package com.microsoft.azure.keyvault.authentication;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.microsoft.rest.credentials.ServiceClientCredentials;

import okhttp3.Authenticator;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

/**
 * An implementation of {@link ServiceClientCredentials} that supports automatic bearer token refresh.
 *
 */
public abstract class KeyVaultCredentials implements ServiceClientCredentials {

    private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
    private static final String AUTHENTICATE = "Authorization";
    private static final String BEARER_TOKEP_REFIX = "Bearer ";

    private final ChallengeCache cache = new ChallengeCache();

    @Override
    public void applyCredentialsFilter(OkHttpClient.Builder clientBuilder) {

        clientBuilder.addInterceptor(new Interceptor() {

            @Override
            public Response intercept(Chain chain) throws IOException {

                HttpUrl url = chain.request().url();

                Map challengeMap = cache.getCachedChallenge(url);

                if (challengeMap != null) {
                    // Get the bearer token
                    String credential = getAuthenticationCredentials(challengeMap);

                    Request newRequest = chain.request().newBuilder()
                            .header(AUTHENTICATE, BEARER_TOKEP_REFIX + credential).build();

                    return chain.proceed(newRequest);
                } else {
                    // challenge is new for the URL and is not cached,
                    // so the request is sent out to get the challenges in
                    // response
                    return chain.proceed(chain.request());
                }
            }
        });

        // Caches the challenge for failed request and re-send the request with
        // access token.
        clientBuilder.authenticator(new Authenticator() {

            @Override
            public Request authenticate(Route route, Response response) throws IOException {

                // if challenge is not cached then extract and cache it
                String authenticateHeader = response.header(WWW_AUTHENTICATE);

                Map challengeMap = extractChallenge(authenticateHeader, BEARER_TOKEP_REFIX);

                // Cache the challenge
                cache.addCachedChallenge(response.request().url(), challengeMap);

                // Get the bearer token from the callback by providing the
                // challenges
                String credential = getAuthenticationCredentials(challengeMap);

                if (credential == null) {
                    return null;
                }

                // Add the token header and resume the call.
                // The token should live for duration of this request and never
                // be cached anywhere in our code.
                return response.request().newBuilder().header(AUTHENTICATE, BEARER_TOKEP_REFIX + credential).build();
            }
        });
    }

    /**
     * Extracts the authentication challenges from the challenge map and calls
     * the authentication callback to get the bearer token and return it.
     * 
     * @param challengeMap
     *            the challenge map.
     * @return the bearer token.
     */
    private String getAuthenticationCredentials(Map challengeMap) {

        String authorization = challengeMap.get("authorization");
        if (authorization == null) {
            authorization = challengeMap.get("authorization_uri");
        }

        String resource = challengeMap.get("resource");
        String scope = challengeMap.get("scope");

        return doAuthenticate(authorization, resource, scope);
    }

    /**
     * Extracts the challenge off the authentication header.
     * 
     * @param authenticateHeader
     *            the authentication header containing all the challenges.
     * @param authChallengePrefix
     *            the authentication challenge name.
     * @return a challenge map.
     */
    private static Map extractChallenge(String authenticateHeader, String authChallengePrefix) {
        if (!isValidChallenge(authenticateHeader, authChallengePrefix)) {
            return null;
        }

        authenticateHeader = authenticateHeader.toLowerCase().replace(authChallengePrefix.toLowerCase(), "");

        String[] challenges = authenticateHeader.split(", ");
        Map challengeMap = new HashMap();
        for (String pair : challenges) {
            String[] keyValue = pair.split("=");
            challengeMap.put(keyValue[0].replaceAll("\"", ""), keyValue[1].replaceAll("\"", ""));
        }
        return challengeMap;
    }

    /**
     * Verifies whether a challenge is bearer or not.
     * 
     * @param authenticateHeader
     *            the authentication header containing all the challenges.
     * @param authChallengePrefix
     *            the authentication challenge name.
     * @return
     */
    private static boolean isValidChallenge(String authenticateHeader, String authChallengePrefix) {
        if (authenticateHeader != null && !authenticateHeader.isEmpty()
                && authenticateHeader.toLowerCase().startsWith(authChallengePrefix.toLowerCase())) {
            return true;
        }
        return false;
    }

    /**
     * Abstract method to be implemented.
     * 
     * @param authorization
     *            Identifier of the authority, a URL.
     * @param resource
     *            Identifier of the target resource that is the recipient of the
     *            requested token, a URL.
     * @param scope
     *            The scope of the authentication request.
     * 
     * @return The access token
     * 
     *         Answers a server challenge with a token header.
     *         

* Implementations typically use ADAL to get a token, as performed * in the sample below: *

* *
     * @Override
     * public String doAuthenticate(String authorization, String resource, String scope) {
     *     String clientId = ...; // client GUID as shown in Azure portal.
     *     String clientKey = ...; // client key as provided by Azure portal.
     *     AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey);
     *     return token.getAccessToken();;
     * }
     *
     * private static AuthenticationResult getAccessTokenFromClientCredentials(String authorization, String resource, String clientId, String clientKey) {
     *     AuthenticationContext context = null;
     *     AuthenticationResult result = null;
     *     ExecutorService service = null;
     *     try {
     *         service = Executors.newFixedThreadPool(1);
     *         context = new AuthenticationContext(authorization, false, service);
     *         ClientCredential credentials = new ClientCredential(clientId, clientKey);
     *         Future<AuthenticationResult> future = context.acquireToken(resource, credentials, null);
     *         result = future.get();
     *     } catch (Exception e) {
     *         throw new RuntimeException(e);
     *     } finally {
     *         service.shutdown();
     *     }
     *
     *     if (result == null) {
     *         throw new RuntimeException("authentication result was null");
     *     }
     *     return result;
     * }
     *         
* *

* Note: The client key must be securely stored. It's advised to * use two client applications - one for development and other for * production - managed by separate parties. *

* */ public abstract String doAuthenticate(String authorization, String resource, String scope); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy