Skip to content

Latest commit

 

History

History
231 lines (185 loc) · 8.36 KB

README.md

File metadata and controls

231 lines (185 loc) · 8.36 KB

This is the first iteration, following Scott's .NET framework, it will change once I look at implmenentation side of Scott's framework. It gives high level idea of the structure. Azure Identity is the most vague/unknown part, as it currently doesn't exist in any language (.NET in progress). Azure Identity structure is expected to change.

Java

Azure Core/Common

Credentials Classes

package com.azure.common.credentials;

    //This class already exists in azure core.
    public class TokenCredentials implements ServiceClientCredentials {
        /**
         * The authentication scheme.
         */
        private String scheme;

        /**
         * The secure token.
         */
        private String token;

        /**
         * Creates TokenCredentials.
         *
         * @param scheme scheme to use. If null, defaults to Bearer
         * @param token  valid token
         */
        public TokenCredentials(String scheme, String token) {
            if (scheme == null) {
                scheme = "Bearer";
            }
            this.scheme = scheme;
            this.token = token;
        }

        @Override
        public String authorizationHeaderValue(String uri) throws IOException {
            return scheme + " " + token;
        }
    }

    //Doesn't exist in azure core yet, exists in KV Secrets API within SecretAsyncClientBuilder
    public class AsyncTokenCredentials implements AsyncServiceClientCredentials {

        private String scheme;
        private Mono<String> token;

        AsyncTokenCredentials(String scheme, Mono<String> token) {
            this.scheme = scheme;
            this.token = token;
        }

        @Override
        public Mono<String> authorizationHeaderValueAsync(HttpRequest httpRequest) {
            if (scheme == null) {
                scheme = "Bearer";
            }
            return token.flatMap(tokenValue -> Mono.just("Bearer " + tokenValue));
        }
    }

Authentication Policies

package com.azure.common.http.policy;

//The name is deceiving, but it does Authorization. It already exists in azure core/common for Java.
public class CredentialsPolicy implements HttpPipelinePolicy {
    private final ServiceClientCredentials credentials;

    /**
     * Creates CredentialsPolicy.
     *
     * @param credentials the credentials
     */
    public CredentialsPolicy(ServiceClientCredentials credentials) {
        this.credentials = credentials;
    }

    @Override
    public Mono<HttpResponse> process(HttpPipelineCallContext context, HttpPipelineNextPolicy next) {
        try {
            String token = credentials.authorizationHeaderValue(context.httpRequest().url().toString());
            context.httpRequest().headers().set("Authorization", token);
            return next.process();
        } catch (IOException e) {
            return Mono.error(e);
        }
    }
}

Azure Identity

The azure com.azure.identity package provides implementations for Active Directory based OAuth token authentication. It provides an abstraction above the authentication libraries provided by the Azure Identity team. It also provides mechanisms for querying credential information from the environment.

Credential Implementations

package com.azure.identity;

    // Use Fluent Pattern During Implementation
    //This is the package with most implementation needed.
    // Scott had the methods Static in his framework -- check the need and usage for it.
    public abstract class AzureCredential extends AsyncTokenCredentials {
        private AzureCredential(HttpPipelinePolicy policies) {
            client = getDefaultClient();
        }

        public List<HttpPipelinePolicy> createDefaultPipelinePolicies();
    }

    public class ClientSecretCredential extends AzureCredential {
        public ClientSecretCredential(String clientId, String clientSecret, String authority);
        public ClientSecretCredential(String clientId, String clientSecret, String authority, List<HttpPipelinePolicy> policies);

        @Override
        public Mono<String> authorizationHeaderValueAsync(HttpRequest httpRequest);
    }

    //Not in focus for now.
    public class ClientCertificateCredential extends AzureCredential {
        public ClientCertificateCredential(X509Certificate certificate, string authority, List<HttpPipelinePolicy> policies);
    }    

Questions / Open Issues:

  • Should authority be required parameters?
  • Possibly it's odd that these credential classes expose pipeline options when they're not "client" classes
    • Should we try to reuse the policies from the client class rather than having authentication calls have they're own pipeline. Allowing us to avoid exposing options on the client (at least for now)

Credential Providers

package com.azure.identity;

    //Use Fluent Pattern during implementation
    public class TokenCredentialProvider extends AsyncTokenCredentials {
        protected TokenCredentialProvider();

        public TokenCredentialProvider(TokenCredentialProvider... credentialProviders);

        @Override
        public Mono<String> authorizationHeaderValueAsync(HttpRequest httpRequest); // full implementation


        // iterates through the chain of credential providers to find one which returns a credential if none
        // return credential raise exception (full chain on stack)
        protected Mono<TokenCredential> getCredential(HttpRequest httpRequest);
    }

    public class EnvironmentCredentialProvider extends TokenCredentialProvider
    {
        public EnvironmentCredentialProvider(List<HttpPipelinePolicy> policies);
        
        // if available return cred
        protected Mono<TokenCredential> getCredential(HttpRequest httpRequest);  // full implementation
    }

    //
    public class MsiCredentialProvider extends TokenCredentialProvider {
        public MsiCredentialProvider(List<HttpPipelinePolicy> policies);

        // if available return cred
        protected Mono<TokenCredential> getCredential(HttpRequest httpRequest);  // full implementation
    }    

Client Library Implementations

package com.azure.keyvault


    //This exists in Secrets API, accepts AsyncServiceClientCredentials for async client, ServiceClientCredentials for sync client
     public final class SecretAsyncClientBuilder {
        private final List<HttpPipelinePolicy> policies;
        private AsyncServiceClientCredentials credentials;
        private HttpPipeline pipeline;
        private URL vaultEndpoint;
        private HttpClient httpClient;
        private HttpLogDetailLevel httpLogDetailLevel;
        private RetryPolicy retryPolicy;

        SecretAsyncClientBuilder() {
            retryPolicy = new RetryPolicy();
            httpLogDetailLevel = HttpLogDetailLevel.NONE;
            policies = new ArrayList<>();
        }
        public SecretAsyncClient build() {

            if (vaultEndpoint == null) {
                throw new IllegalStateException(KeyVaultErrorCodeStrings.getErrorString(KeyVaultErrorCodeStrings.VAULT_END_POINT_REQUIRED));
            }

            if (pipeline != null) {
                return new SecretAsyncClient(vaultEndpoint, pipeline);
            }

            if (credentials == null) {
                throw new IllegalStateException(KeyVaultErrorCodeStrings.getErrorString(KeyVaultErrorCodeStrings.CREDENTIALS_REQUIRED));
            }
            // Closest to API goes first, closest to wire goes last.
            final List<HttpPipelinePolicy> policies = new ArrayList<>();
            policies.add(new UserAgentPolicy(AzureKeyVaultConfiguration.SDK_NAME, AzureKeyVaultConfiguration.SDK_VERSION));
            policies.add(retryPolicy);
            policies.add(new AsyncCredentialsPolicy(getAsyncTokenCredentials()));
            policies.addAll(this.policies);
            policies.add(new HttpLoggingPolicy(httpLogDetailLevel));

            HttpPipeline pipeline = httpClient == null
                ? new HttpPipeline(policies)
                : new HttpPipeline(httpClient, policies);

            return new SecretAsyncClient(vaultEndpoint, pipeline);
        }
    }

End User Expected Usage Samples

  //Using ClientSecretCredential
  //Use Fluent Pattern for SecretCredentials or Providers.
  SecretAsyncClient.builder()
    .vaultEndpoint("https://myvault.vault.azure.net/")
    .credentials(new ClientSecretCredential(getClientId(), getClientSecret(), getAuthority()))
    .build();