Hoje vamos ver como realizar a construção de um serviço web, para autenticação com retorno do token, através do Azure AD. Primeiramente vamos criar um Registro de Aplicativo em nosso Diretório Padrão.

Tipo de Aplicativo Nativo.

URL de entrada: Neste parâmetro como nós vamos construir o serviço para realizar a autenticação, pode ser qualquer endereço, pois não será utilizado em nosso serviço, porém é obrigatório para a criação do aplicativo no Azure. Este parâmetro é utilizado em aplicações que realizam a autenticação e tem redirecionamento para sua aplicação após o retorno do token válido. Como estamos construindo um serviço web, para que não tenhamos que ter a página de autenticação de outra aplicação a não ser a de nossa aplicação.

Após a criação da aplicação, temos o seguinte cenário:

Onde vamos chamar ID do Aplicativo de ClientID, percebam que temos também o ID de Objeto. Para nosso serviço o ID de objeto que iremos utilizar não é o como mostrado na figura acima. Clicando em Manifesto, será mostrado o manifesto da aplicação que criamos no Azure AD, conforme figura abaixo.

Notem, iremos utilizar o resourceAppId, como nosso ObjectID, ele que nos dará acesso à aplicação.

Por questões de segurança, vamos validar a questão de acesso da aplicação que utilizaremos como gateway, se a mesma possui permissão delegada para acesso a informações do Azure AD.

Agora que realizamos a configuração de nosso Web App no Azure, vamos iniciar nosso projeto de Web API, utilizando Visual Studio 2017, vamos iniciar realizando a criação de nosso projeto.

O template de projeto a ser utilizado será (ASP.NET Web Application (.Net Framework)). Em seguida selecione o template Web API e verifique logo abaixo se o checkbox Web API está selecionado.

Agora com o projeto já criado, vamos iniciar a instalação das dependências do projeto, primeiro vamos instalar a biblioteca Unity (Não é o game engine 🙂 ), para tal vamos instalar através do NuGet pela linha de comando Install-Package Unity conforme [1].

Outra referência importante para nosso api de autenticação via Azure, será Microsoft Identity Model, instalada através do NuGet Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 3.19.0.

Agora vamos criar um novo controller aqui chamado do AuthController, para tal só basta adicionar um novo controller ao projeto clicando com o botão direito do mouse na pasta controller do solution explorer, add, controller.

No projeto de exemplo criei uma pasta chamada ActiveDirectory, somente para organização de código, onde criei as classes:
ADControl
ADParams
AzureParams
IADParams
User

No arquivo Web.config, temos alguns parâmetros da aplicação conforme abaixo, são os parâmetros discutidos anteriormente na configuração do web app.

<appSettings>
    <add key="DirectoryName" value="seu tenant" />
    <add key="ObjectId" value=" />
    <add key="ClientId" value="" />
  </appSettings>

Na classe User, vamos criar duas propriedades conforme abaixo, que farão parte dos parâmetros de POST de nosso autenticador.

 public class User
{
      public string UserName { get; set; }
      public string Password { get; set; }
}

Na classe AzureParams, vamos criar as propriedades necessárias para comunicação a web app que criamos no Azure AD. Estas propriedades correspondem ao que criamos no Web.config.

 public class AzureParams
{
     public string DirectoryName { get; set; }
     public string ClientId { get; set; }
     public string ObjectId { get; set; }
}

Criamos também a interface que corresponde ao Pattern Dependency Injection conforme [1]. Que corresponde a classe IADParams.

 public interface IADParams
 {
      AzureParams GetAzureParams();
}

Agora na classe ADParams, no construtor da classe realizamos a carga dos parâmetros correspondentes às configurações realizada no Azure AD, que estão parametrizadas no Web.config

 public class ADParams : IADParams
    {
        public AzureParams GetAzureParams()
        {
            AzureParams azureParams = new AzureParams();
            azureParams.DirectoryName = ConfigurationManager.AppSettings["DirectoryName"];
            azureParams.ClientId = ConfigurationManager.AppSettings["ClientId"];
            azureParams.ObjectId = ConfigurationManager.AppSettings["ObjectId"];
            return azureParams;
        }
    }

Na classe ADControl, temos o método que irá realizar a autenticação e retorno do token gerado assim que autenticado no Azure AD.

using Microsoft.IdentityModel.Clients.ActiveDirectory; 
public class ADControl
    {
        public static string Login(User user, AzureParams azureParams)
        {
            var credentials = new UserCredential(string.Format("{0}@{1}", user.UserName, azureParams.DirectoryName), user.Password);
            var authenticationContext = new AuthenticationContext("https://login.windows.net/" + azureParams.DirectoryName);
            try
            {
                var result = authenticationContext.AcquireToken(azureParams.ObjectId, azureParams.ClientId, credentials);
                authenticationContext.TokenCache.Clear();
                return result.AccessToken;
            }
            catch
            {
                return string.Empty;
            }

        }
    }
 public class ADControl
    {
        public static string Login(User user, AzureParams azureParams)
        {
            var credentials = new UserCredential(string.Format("{0}@{1}", user.UserName, azureParams.DirectoryName), user.Password);
            var authenticationContext = new AuthenticationContext("https://login.windows.net/" + azureParams.DirectoryName);
            try
            {
                var result = authenticationContext.AcquireToken(azureParams.ObjectId, azureParams.ClientId, credentials);
                authenticationContext.TokenCache.Clear();
                return result.AccessToken;
            }
            catch
            {
                return string.Empty;
            }

        }
    }

Note que em UserCredentials, estamos concatenando o login do usuário com o nome do tenant, para que na aplicação não seja necessário a entrada de login completa [email protected], somente o login do usuário, isso fica a cargo da implementação que deseja realizar em seu sistema.

Agora vamos realizar a implementação do Dependency Injection, conforme referência [1].

using Unity;
using Unity.Exceptions;
public class UnityResolver : IDependencyResolver
    {
        protected IUnityContainer container;

        public UnityResolver(IUnityContainer container)
        {
            this.container = container ?? throw new ArgumentNullException("container");
        }

        public IDependencyScope BeginScope()
        {
            var child = container.CreateChildContainer();
            return new UnityResolver(child);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            container.Dispose();
        }

        public object GetService(Type serviceType)
        {
            try
            {
                return container.Resolve(serviceType);
            }
            catch(ResolutionFailedException)
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                return container.ResolveAll(serviceType);
            }
            catch(ResolutionFailedException)
            {
                return new List<object>();
            }
        }
    }

Na classe WebApiConfig, criada já por padrão na criação do projeto, esta classe é responsável pela inicialização da API.

 public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
             // Web API routes
            config.MapHttpAttributeRoutes();
            var container = new UnityContainer();
            container.RegisterType<IADParams, ADParams>(new HierarchicalLifetimeManager());
            config.DependencyResolver = new UnityResolver(container);
        }
    }

Assim que invocada nossa api, esta é a classe invocada para realizar o registro de roteamento da api.

Agora no controller que criamos anteriormente AuthController, vamos implementar o método que será invocado pelo nosso Web Api.

 public class AuthController : ApiController
 {
     private IADParams _adparams;
     public AuthController(IADParams adparams)
     {
         _adparams = adparams;
     }

     [HttpGet]
    public string Get()
    {
        return "Service OK";
    }
     [HttpPost]
     [ActionName("Login")]
     public string Login([FromBody] User user)
     {
        return ADControl.Login(user, _adparams.GetAzureParams());
     }
 }

Agora basta realizar a publicação de sua api no azure através do Visual Studio, e pronto seu autenticado no Azure AD, estará funcional. Lembrando que sua rota irá ficar como, o nome de sua api no azure /api/<seu controller, porém somente o que há antes do nome da classe controller>, por exemplo, nossa classe é AuthController então ficamos com o endereço /api/auth/login que é o nome do método POST que criamos na classe.

Agora invocando como REST API, estou utilizando como exemplo o SOAP UI, porém pode ser o aplicativo de sua preferência, PostMan, etc…
Na imagem abaixo podemos ver a chamada do método e na parte direita da tela vemos o retorno do token que nos foi retornado do Azure AD.

Assim podemos realizar a chamada de nossa api de autenticação em outros sistemas sendo retornado o token que valida que o login e senha apresentado está ativo e a senha e nome de usuário estão corretos.

Então por hoje ficamos assim, espero ter contribuído com quem está pensando em implementação de autenticação utilizando serviços de WEB API juntamente com Azure AD. Até a próxima com a implementação de técnica de IMPERSIONATION 🙂 FUI!!

Referências
[1] https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection


Willian Cristopher Filus

Developer

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *