Desenvolvimento - WCF
Autenticação via Claims no WCF
A finalidade deste artigo é mostrar como podemos aplicar o WIF em serviços WCF, analisando tudo o que é necessário para colocar isso em funcionamento, incluindo a criação de um STS customizado para testes.
por Israel AéceNos artigos anteriores, falei sobre a proposta da Microsoft para tentar unificar o sistema de autenticação das aplicações. Nos dois primeiros artigos, comentei um pouco sobre os problemas existentes e a teoria que circunda qualquer Identity MetaSystem. Na sequência, introduzi o WIF e alguns tipos que são comuns para qualquer tipo de aplicação que fará uso deste novo framework. A finalidade deste artigo é mostrar como podemos aplicar o WIF em serviços WCF, analisando tudo o que é necessário para colocar isso em funcionamento, incluindo a criação de um STS customizado para testes.
Em um artigo anterior, vimos como podemos implementar o WIF à aplicações ASP.NET, que recorrem ao navegador para serem apresentadas. Com isso, o WIF ou qualquer outro framework de autenticação, faz uso das funcionalidades que o navegador disponibiliza, para assim, coordenar de forma passiva, tudo o que é necessário para efetuar a autenticação em uma segunda aplicação.
Quando falamos em autenticação de serviços, o processo muda um pouco, pois geralmente eles são acessados através de uma aplicação cliente, que muitas vezes deve se identificar antes de consumir as funcionalidades que ele expõe. Neste cenário podemos perceber que não há o navegador, e sim uma aplicação (na maioria das vezes, desktop (Windows Forms ou WPF)) que consomem o serviço.
No artigo anterior discutimos o ambiente passivo, e aqui entra o ambiente ativo. O nome se deve ao fato de que a aplicação cliente coordena ativamente o processo de autenticação do usuário quando ele, por sua vez, precisar acessar um serviço que exija que o usuário esteja devidamente autenticado para poder acessá-lo. Como já acontecia anteriomente, os serviços que compõem a sua estrutura, pode seguramente confiar em um orgão que efetua a autenticação, evitando assim configurarmos serviço por serviço todo o processo necessário para a autenticação do usuário.
Como o WCF já traz vários pontos de estensibilidade, a Microsoft apenas introduziu nesses locais recursos para interceptar o processo de autenticação, e com isso, acoplando o WIF para efetuar todo o trabalho. A ideia aqui é ao configurar um serviço WCF, dizer que ele somente permitirá usuários autenticados por uma outra aplicação, que também será um serviço, mas construído com os recursos fornecidos pelo WIF.
Ao expor um serviço WCF neste caso, a configuração do mesmo deverá conter entre vários itens, o autenticador. Com isso, o WIFserá capaz de modificar o documento WSDL que descreve o serviço, também incluindo o local deste autenticador que o teu serviço confia. Com isso, ao referenciar o serviço em uma aplicação cliente, ela já saberá qual o endereço correto do autenticador e, consequentemente, antes de efetuar a requisição efetiva para uma das operações do serviço, ele será inteligente o bastante para autenticar o usuário no autenticador e, finalmente, invocar a operação solicitada. Através da imagem abaixo, podemos visualizar as etapas deste processo:
É importante dizer que no ambiente ativo, o processo acaba sendo mais rápido quando comparado com o ambiente passivo, pois o cliente não precisa visitar o serviço para saber qual é o autenticador que o mesmo utiliza. Isso já aconteceu durante a referência do serviço na aplicação cliente, que traz, além da descrição do serviço em si, informações inerentes ao autenticador em qual o serviço confia, assim como já foi comentado acima.
Depois de entendermos o processo em um nível macro, precisamos nos atentar em como podemos proceder para preparar tudo isso. Em primeiro lugar, vamos avaliar as opções de template de projeto voltadas para o WCF, que o WIF nos fornece quando instalamos o seu SDK:
-
WCF Security Token Service: Template que traz a configuração padrão para criar um serviço WCF que servirá como um STS no ambiente ativo.
-
Claims-aware WCF Service: Template que traz um serviço WCF pré-configurado para utilizar algum STS, que será responsável pela autenticação do usuário.
Apesar delas existirem, não vamos utilizá-las no decorrer deste artigo. A ideia aqui é apresentar os detalhes de criação do começo ao fim, analisando cada uma das seções necessárias para fazer com que todo esse processo funcione da forma correta. Com isso, teremos três elementos envolvidos no nosso exemplo: o serviço (relying party) que contém todas as funcionalidades que desejamos expor aos clientes, um autenticador (STS), que será o responsável por autenticar os usuários e, finalmente, a aplicação cliente (subjects), que utilizará o serviço WCF para extrair as informações e exibí-las na tela, mas antes disso, deverá autenticar o usuário que a acessa no STS correspondente.
A criação do STS
Para iniciar, vamos começar com a construção do STS, que será o nosso autenticador. Para isso, vamos recorrer à uma aplicação do tipo Console, que irá expor um serviço que aceitará as requisições de login, validará o usuário em algum repositório e, finalmente, devolverá para a aplicação solicitante o token gerado por ele. Como já comentei nas entrelinhas, o STS nada mais é que um serviço, que se utiliza da mesma estrutura de um serviço WCF, mas expondo um tipo de serviço diferenciado, que tem a finalidade de validar e autenticarum usuário. Para termos acesso as classes que nos permite criar este tipo de serviço, devemos referenciar na aplicação o assembly Microsoft.IdentityModel.dll, que é fornecido pelo WIF.
Se o STS nada mais é que um serviço, temos que ter uma classe que o representa, e para issoo WIF já fornece uma classe abstrata chamada SecurityTokenService, que é o nosso STS em si, fornecendo toda a estrutura necessária para a criação de um STS customizado, mas para isso, devemos sobrescrever dois métodos nas classes derivadas, a saber: GetScope e GetOutputClaimsIdentity.
O primeiro método, GetScope, nos dá a possibilidade de verificar quais são as chaves de criptografia que serão utilizadas, baseando-se no endereço da relying party. Esse método retorna uma instância da classe Scope, que contém toda a configuração necessária para gerar o retorno para a respectiva aplicação (relying party). Já o segundo método, GetOutputClaimsIdentity, é o local onde efetivamente definimos quais claims serão criadas para aquele usuário recém autenticado, e que na maioria das vezes, iremos recorrer a uma busca no repositório para extrair esses dados. Esse método retorna a instância de uma classe ClaimsIdentity, que implementa a interface IClaimsIdentity, e como sabemos, fornece uma propriedade chamada Claims, que nada mais é que uma coleção, onde podemos adicionar quantas claims desejarmos.
Como mencionei, o método GetScope é responsável por capturar os recursos necessários para estabelecer a comunicação segura. Aqui teremos dois certificados, sendo um deles utilizado pelo autenticador (STS) e outro pelo serviço (relying party). Cada um deles vai garantir que as mensagens trocadas não sejam manipuladas por ninguém. Para isso, a classe Scope fornece duas propriedades: EncryptingCredentials e SigningCredentials. Na primeira propriedade, definimos o certificado fornecido pelo serviço, que com a sua chave pública, faremos a criptografia da mensagem quando formos retornar a resposta do processo de autenticação (RSTR). Já a propriedade SigningCredentials, apontamos para o certificado do autenticador, que utilizará para garantir que a mensagem não será alterada durante a sua viagem. Abaixo temos um trecho da classe que representa o serviço de STS, omitindo algumas seções por questões de espaço:
public class ServicoDeAutenticacao : SecurityTokenService
{
public ServicoDeAutenticacao(ConfiguradorDoServicoDeAutenticacao configurador)
: base(configurador) { }
protected override IClaimsIdentity GetOutputClaimsIdentity(
IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
{
principal.Identities[0].Claims.AddRange(
RepositorioDeUsuarios.RecuperarClaimsDoUsuario(principal.Identity.Name));
return principal.Identity as IClaimsIdentity;
}
protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request)
{
Scope scope = new Scope(request.AppliesTo.Uri.AbsoluteUri);
scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(request.AppliesTo);
scope.SigningCredentials = new X509SigningCredentials(
CertificateUtil.GetCertificate(
StoreName.My,
StoreLocation.LocalMachine,
"CN=Autenticador"));
return scope;
}
//Outros Membros
}
Depois da classe que representa o serviço de STS criada, o próximo passo é a criação de uma classe que herda da classe SecurityTokenServiceConfiguration. Essa classe permite configurar um serviço de STS, definindo qual será o tipo da classe que representará o STS, que foi aquela que criamos no passo anterior. Essa classe possui várias propriedades, que permite configurar de forma imperativa o STS, mas neste caso, tudo o que precisamos fazer é herdar, e dentro do seu construtor, definir duas principais propriedades: SecurityTokenService e SigningCredentials. A primeira correspondente ao Type da classe que herda de SecurityTokenService, enquanto a segunda, define os dados do certificado usado para a conversação. O código abaixo mostra a sua estrutura:
public class ConfiguradorDoServicoDeAutenticacao : SecurityTokenServiceConfiguration
{
public ConfiguradorDoServicoDeAutenticacao()
: base("ServicoDeAutenticacao")
{
this.SecurityTokenService = typeof(ServicoDeAutenticacao);
this.SigningCredentials = new X509SigningCredentials(
CertificateUtil.GetCertificate(
StoreName.My,
StoreLocation.LocalMachine,
"CN=Autenticador"));
}
}
Para encerrar as classes customizadas, é necessário a criação de um security token handler. Neste contexto, o handler é responsável por extrair o token enviado pelo cliente, e validá-lo em algum repositório. Como queremos optar para que o usuário informe o seu login e senha, utilizaremos aqui um security token handler exclusivo para este caso, que é o UserNameSecurityTokenHandler.
Ao herdar desta classe, devemos sobrescrever o método ValidateToken, que fornece como parâmetro a instância do token enviado pelo usuário, representado pela classe UserNameSecurityToken, que por sua vez, fornece duas propriedades pertinentes ao modelo de autenticação, que são elas: UserName e Password. O exemplo abaixo ilustra como proceder para customizar essa classe:
public class ValidadorDeUsuario : UserNameSecurityTokenHandler
{
public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
{
UserNameSecurityToken usernameToken = token as UserNameSecurityToken;
if (!RepositorioDeUsuarios.Validar(usernameToken.UserName, usernameToken.Password))
throw new SecurityException("Usuário inválido.");
ClaimsIdentity identidadeAtual = new ClaimsIdentity("ValidadorDeUsuario");
identidadeAtual.Claims.Add(new Claim(ClaimTypes.Name, usernameToken.UserName));
return new ClaimsIdentityCollection() { identidadeAtual };
}
//Outros Membros
}
Como podemos notar no código acima, é dentro do método ValidateToken que devemos recorrer ao repositório de usuários para validar as credenciais informadas pelo cliente. Caso o usuário não seja encontrado, você pode disparar uma exceção do tipo SecurityException, informando a inexistência daquele usuário.
E para colocar tudo isso em funcionamento, entra em cena a classe WSTrustServiceHost. Essa classe herda diretamente da classe ServiceHost (exposta pela API do WCF), que permite carregar e gerenciar um serviço STS utilizando toda a estrutura do WCF, e com isso, ficará disponível para receber as requisições das aplicações clientes que desejam autenticar seus usuários, antes de efetivamente acessar uma das operações expostas pelo serviço. Em princípio, temos que recorrer a esta classe quando não estamos utilizamos as templates de projetos que vimos acima, pois quando as usamos, isso tudo o que vemos aqui ficará transparente.
Há um overload do construtor da classe WSTrustServiceHost, que aceita como parâmetro uma classe do tipo SecurityTokenServiceConfiguration, contendo toda a configuração necessária de umserviço de STS. O código abaixo ilustra a sua utilização:
using (WSTrustServiceHost host =
new WSTrustServiceHost(new ConfiguradorDoServicoDeAutenticacao()))
{
host.Open();
Console.WriteLine("[ Serviço de Autenticação em funcionamento ]");
Console.ReadLine();
}
Assim como qualquer serviço WCF, somente essa configuração não é suficiente para subir o serviço de STS. Ainda há a necessidade de apontar, via arquivo de configuração, outras informações necessárias para o mesmo, tais como endpoints, contratos, baseAddresses, etc., que como sabemos, é necessário para todo e qualquer tipo de serviço WCF. Mas como estamos criando uma espécie de serviço voltado exclusivamente para o STS, temos já contratos predefinidos, que expõem funcionalidades de emissão, cancelamento e renovação de tokens, mas tudo isso tudo será utilizado, em um primeiro momento,internamente pelo runtime do WIF.
Sendo assim, a classe que representará o serviço é a WSTrustServiceContract, que implementa a interface IWSTrust13SyncContract, que define o contrato do serviço, fornecendo as operações comentadas no parágrafo anterior. Agora o nosso serviço deverá expor dois endpoints, sendo um deles utilizando o contrato IWSTrust13SyncContract em conjunto com o binding WS2007HttpBinding, e além disso, um binding tradicional de metadados (IMetadataExchange), que irá expor todas as funcionalidades do serviço de STS. A única configuração relevante do binding, é que devemos definir o atributo clientCredentialType do elemento message como sendo UserName, já que a ideia é permitir ao usuário informar os dados de acesso através da aplicação cliente. Abaixo podemos visualizar as configurações deste serviço:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract"
behaviorConfiguration="serviceConfig">
<host>
<baseAddresses>
<add baseAddress="http://localhost:9010/sts"/>
</baseAddresses>
</host>
<endpoint address=""
binding="ws2007HttpBinding"
bindingConfiguration="bindingConfig"
contract="Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<bindings>
<ws2007HttpBinding>
<binding name="bindingConfig">
<security mode="Message">
<message clientCredentialType="UserName"
negotiateServiceCredential="false"
establishSecurityContext="false"/>
</security>
</binding>
</ws2007HttpBinding>
</bindings>
</configuration>
Como se não bastasse, ainda precisamos das configurações referentes ao WIF neste mesmo arquivo de configuração da aplicação que servirá como host do serviço de STS. Como já vimos em artigos anteriores, a seção para essa configuração é a microsoft.identityModel, que deve ser registrada primeiramente antes de ser utilizada.
Depois da seção registrada, o próximo passo é remover o security token handler que é adicionado por padrão (WindowsUserNameSecurityTokenHandler), e em seguida, adicionarmos aquele que criamos acima. Para isso, recorremos à uma coleção chamada securityTokenHandlers, e especificamos ali o tipo completo, incluindo o nome e o assembly onde ele foi criado. Além disso, ainda há a seção serviceCertificate, que apontamos o certificado que o autenticador irá utilizar para se comunicar com o serviço, protegendo todas mensagens que são trocadas entre eles. O código abaixo resume todas as configurações do WIF que são necessárias na aplicação que hospedará o serviço STS, que compõem o arquivo de configuração que vimos acima com as configurações pertinentes ao WCF:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="microsoft.identityModel"
type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, ..."/>
</configSections>
<microsoft.identityModel>
<service>
<securityTokenHandlers>
<remove type="Microsoft.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, ..." />
<add type="Autenticador.ValidadorDeUsuario, Autenticador ..." />
</securityTokenHandlers>
<serviceCertificate>
<certificateReference storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectDistinguishedName"
findValue="CN=Autenticador" />
</serviceCertificate>
</service>
</microsoft.identityModel>
</configuration>
A criação do Serviço
Depois do serviço de autenticação criado, vamos partir para o serviço que utilizará e confiará naquele STS para autenticar os usuários. O serviço que vamos utilizar como exemplo é bem simples, com apenas duas operações que recebem e retornam tipos simples. A ideia aqui será restringir o acesso às operações deste serviço, permitindo o acesso somente depois do usuário ser devidamente autenticado pelo serviço de STS que criamos acima.
Sendo assim, a configuração do serviço WCF com as nossas regras de negócio é simples, contendo os endpoints, os behaviors para expor os metadados e a criação de um hosting, que utilizaremos neste exemplo, uma aplicação do tipo Console. A única diferença aqui na configuração do serviço é a utilização do binding WS2007FederationHttpBinding, que é utilizado quando você está terceirizando o processo de autenticação.
No código abaixo podemos visualizar a configuração do binding do endpoint do nosso serviço. Definimos a segurança como sendo Message, que garantirá que a mensagem seja trafegada de forma segura, mesmo que o transporte não forneça esse recurso. Nas configurações internas, temos um elemento chamado issuerMetadata, que através do atributo address, devemos especificar o endereço do endpoint do serviço de STS, responsável pela autenticação. Em segundo lugar e não menos importante, temos o elemento claimTypeRequirements, que é uma coleção onde podemos especificar quais são as claims que o serviço está exigindo que o STS emita.
<bindings>
<ws2007FederationHttpBinding>
<binding name="bindingConfig">
<security mode="Message">
<message>
<issuerMetadata address="http://localhost:9010/sts/mex" />
<claimTypeRequirements>
<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/name"
isOptional="false" />
<add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
isOptional="false" />
</claimTypeRequirements>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
No serviço também precisamos de algumas configurações pertinentes ao WIF, e como já era de se esperar, é necessário também registrar a seção do mesmo, através do elemento configSections. Neste caso, precisamos definir a audienceUri, que nos permite elencar uma coleção de URIs, que definem os possíveis endereços para qual o token poderá ser entregue, evitando que clientes maliciosos façam uso do mesmo. Em seguida temos a seção serviceCertificate, que apontamos o certificado que o serviço vai utilizar para se comunicar com o autenticador, protegendo as mensagens que são trocadas entre eles. Abaixo o código que ilustra essas configurações:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="microsoft.identityModel"
type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection ..."/>
</configSections>
<microsoft.identityModel>
<service>
<issuerNameRegistry type="Servico.ValidadorDoAutenticador, Servico ..." />
<audienceUris>
<add value="http://localhost:9000/ServicoDeCredito" />
</audienceUris>
<serviceCertificate>
<certificateReference storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectDistinguishedName"
findValue="CN=Servico" />
</serviceCertificate>
</service>
</microsoft.identityModel>
</configuration>
Finalmente, ainda analisando o código acima, temos um elemento chamado issuerNameRegistry, que como o próprio nome diz, permite especificarmos ali os STSs em que a aplicação confia. Para termos o controle desta validação, podemos criar uma classe que herde da classe abstrata IssuerNameRegistry, sobrescrevendo o método GetIssuerName, analisando o certificado fornecido pelo autenticador, e assim tomarmos alguma decisão para saber se confiamos ou não nele. Se iremos confiar, então devemos retornar uma string representando o autenticador, caso contrário, devemos disparar uma exceção para reportar essa "falha". O código abaixo ilustra essa classe:
public class ValidadorDoAutenticador : IssuerNameRegistry
{
public override string GetIssuerName(SecurityToken securityToken)
{
X509SecurityToken certificado = securityToken as X509SecurityToken;
if (certificado != null)
if (certificado.Certificate.SubjectName.Name == "CN=Autenticador")
return certificado.Certificate.SubjectName.Name;
throw new SecurityTokenException("Autenticador Inválido.");
}
}
O último detalhe é a criação do host, que quando utilizamos um self-hosted, recorrermos à classe ServiceHost, e aqui não será diferente. Só há uma pequena configuração a ser realizada aqui, que é a integração do runtime do WIFao pipeline do WCF. Essa configuração pode ser feita de forma imperativa ou declarativa. Utilizando a primeira opção, antes de abrir o host, devemos enviar a instância da classe ServiceHost para o método estático ConfigureServiceHost, exposto pela classe FederatedServiceCredentials, assim como podemos notar no trecho de código abaixo:
using (ServiceHost host = new ServiceHost(typeof(ServicoDeCredito), new Uri[] { }))
{
FederatedServiceCredentials.ConfigureServiceHost(host);
host.Open();
Console.WriteLine("[ Serviço de Crédito em funcionamento ]");
Console.ReadLine();
}
Ao submeter o host para o método ConfigureServiceHost, uma série de elementos são adicionados ao processamento das requisições que chegam para o serviço, efetuando toda a comunicação necessária com o serviço de STS em qual o serviço confia, e além disso, é capaz de capturar o resultado do processo de autenticação realizado remotamente, e com isso prosseguir com o processo de autorização, que determinará o que o usuário poderá ou não acessar.
A autorização continua sendo de responsabilidade do serviço local, que na maioria das vezes recorre às roles para refinar o acesso. Quando estamos trabalhando com o WIF, todas as informações que ele emite para um determinado usuário autenticado, é representado por uma claim. No nosso exemplo, o STS está gerando três claims, sendo uma delas do tipo name, e as outras duas do tipo role. O WCF em conjunto com o WIF são capazes de analisar as claims geradas pelo STS, e depois disso, configurar a propriedade CurrentPrincipal da classe Thread com uma instância da classe ClaimsPrincipal, representando a identidade do usuário e suas respectivas claims.
Desta forma, podemos continuar trabalhando com o modelo de programação da mesma forma que já utilizávamos no passado, ou seja, através do método IsInRole (imperativo)ou com o uso do atributo PrincipalPermissionAttribute (declarativo), assim como é exemplificado abaixo:
public class ServicoDeCredito : IConsultasFinanceiras
{
[PrincipalPermission(SecurityAction.Demand, Role = "Funcionario")]
public decimal RecuperarLimiteDeCredito(int codigoDoCliente)
{
//Implementação
}
[PrincipalPermission(SecurityAction.Demand, Role = "Gerente")]
public void DefinirNovoLimiteDeCredito(int codigoDoCliente, decimal novoValor)
{
//Implementação
}
}
A criação do Cliente
Finalmente, depois dos dois serviços devidamente implementados, chega o momento de consumí-los em uma aplicação cliente. Tudo o que precisamos fazer nela, é adicionar a referência para o serviço WCF, assim como já fazemos. Não há necessidade de fazer referência ao serviço de STS na aplicação cliente, pois além do documento WSDL descrever as operações do serviço, ele também traz todas as informações necessárias a respeito do serviço responsável pela autenticação, como o endereço onde ele está localizado, quais são as claims que o serviço exige que o autenticador emita, etc.
Ao referenciar o serviço através da opção Add Service Reference da IDE do Visual Studio ou através do utilitário svcutil.exe, o proxy gerado conterá as informações tradicionais do endpoint do serviço com o qual a aplicação quer se comunicar, mas também incluirá algumas configurações extras, que determinam como a comunicação com o STS será realizada. Justamente por conhecer onde o STS está e como devemos ser comunicar com ele, que o ambiente ativo é bem mais rápido que o passivo, como já discutido anteriormente.
Outro detalhe importante no arquivo de configuração, são as chaves públicas dos certificados envolvidos no processo. Uma delas foi fornecida pelo serviço e a outra pelo autenticador, que o runtime do WCF e do WIF irão utilizá-las durante o processo de comunicação para garantir com que as mensagens que serão trocadas entre as partes sejam protegidas.
Por fim, a aplicação cliente só precisa consumir o serviço, apenas se atentando a informar o login e senha antes de invocar alguma das operações. O código abaixo ilustra a utilização do serviço, através do proxy que foi criado durante a referência do serviço. O arquivo de configuração do cliente será omitido aqui por questões de espaço.
using (ConsultasFinanceirasClient proxy = new ConsultasFinanceirasClient())
{
proxy.ClientCredentials.UserName.UserName = "Israel";
proxy.ClientCredentials.UserName.Password = "P@ssw0rd";
proxy.DefinirNovoLimiteDeCredito(560, 10000);
Console.WriteLine("Limite para o cliente 200: {0:C2}",
proxy.RecuperarLimiteDeCredito(200));
}
Conclusão: Depois de tudo o que fizemos aqui, o nosso serviço ficou completamente independente de como se faz a autenticação do usuário. Se tivermos um barramento de serviços dentro de algum local, como uma empresa, todos os serviços podem confiar em um único autenticador, e a reutilização será muito grande, já que temos todo o processo centralizado, facilitando a manutenção, já que os serviços consumidores nada sabem sobre a forma como estamos autenticando os usuários.