Estrutura Geneva

Criando um serviço de token de segurança personalizado

Michele Leroux Bustamante

Este artigo baseia-se em uma versão de pré-lançamento da Estrutura "Geneva". Todas as informações estão sujeitas a alterações.

Este artigo aborda:

  • Implementando serviços de token de segurança com a Estrutura Geneva
  • Segurança federada
  • Transformação de declarações
Este artigo usa as seguintes tecnologias:
Windows Communication Foundation, ASP.NET, Estrutura Geneva

Código disponível para download na MSDN Code Gallery
Navegue pelo código online

Sumário

Instruções elementares sobre os serviços de token de segurança
Criando um STS ativo personalizado
Estendendo o SecurityTokenService
Hospedando e configurando o STS
Manipuladores de token de segurança
Criando um STS passivo personalizado
Controle FederatedPassiveTokenService
SessionAuthenticationModule
Autenticação do usuário
Transformação de declarações
Conclusão

A estratégia de plataforma da Microsoft CBA (acesso baseado em declarações) — de codinome "Geneva" — inclui a Estrutura "Geneva", o Servidor "Geneva" e o Windows CardSpace "Geneva." A Estrutura Geneva fornece aos desenvolvedores ferramentas para criar serviços e aplicativos baseados em declarações que envolvem tokens emitidos por um STS (serviço de token de segurança), bem como ferramentas para criar um STS personalizado e aplicativos habilitados para Windows CardSpace. O Servidor Geneva é um STS empresarial, enquanto que a Estrutura Geneva possibilita criar um STS personalizado para ambientes que não exigem recursos de nível empresarial. O Windows CardSpace Geneva é a evolução do Windows CardSpace como o seletor de identidade e o provedor de identidades em máquinas cliente Windows.

No meu último artigo sobre a Estrutura Geneva, discuti uma maneira melhor de criar serviços do WCF (Windows Communication Foundation) baseados em declarações que contam com tokens emitidos por um STS. Aqui, eu criarei um STS personalizado com a Estrutura Geneva.

Antes de continuar a ler, recomendo que você leia o white paper sobre a Estrutura Geneva para desenvolvedores, escrito por Keith Brown e Sesha Mani e meu último artigo, "Estrutura Geneva: Uma melhor abordagem para criar serviços WCF baseados em declarações".

Instruções elementares sobre os serviços de token de segurança

Seja o STS baseado no Servidor Geneva ou criado com a Estrutura Geneva, sua principal função é atuar como um gateway de segurança para autenticar chamadores e emitir tokens de segurança contendo declarações que descrevem o chamador. Você lembrará dos artigos mencionados acima que existem vários cenários suportados pela autenticação do STS:

  • Desassociação de aplicativos e serviços do mecanismo de autenticação para que eles possam se concentrar em declarações relevantes de autorização.
  • Suporte a vários tipos de credenciais sem complicar a implementação de aplicativos e serviços.
  • Suporte a cenários federados, nos quais usuários são autenticados por seu domínio e recebem acesso a recursos em outro domínio — estabelecendo uma confiança entre o STS de cada domínio.
  • Facilitação de cenários de delegação de identidade, em que o usuário autenticado recebe acesso a serviços downstream.
  • Facilitação de transformação de declarações para que declarações relevantes estejam disponíveis para autorização em aplicativos e serviços.

Qualquer um desses cenários pode ser baseado em federação passiva (baseada em navegador) ou federação ativa (baseada no cliente Windows). Em seguida, farei elaborações nesses cenários enquanto descrevo como criar uma lógica relevante em um STS personalizado criado com a Estrutura Geneva.

Antes de mergulhar na implementação de um STS, revisarei primeiro alguns conceitos básicos. Um STS usado na federação ativa é uma implementação do perfil do solicitador ativo de WS-Federation (consulte WS-Federation TC) e (principalmente) a especificação WS-Trust (consulte WS-Trust 1.3).

De forma geral, o WS-Trust descreve um contrato com quatro operações de serviços: Issue, Validate, Renew e Cancel. Respectivamente, essas operações são chamadas por clientes para solicitar um token de segurança, para validar um token de segurança, para renovar um token de segurança expirado ou para cancelar um token de segurança que não deveria mais ser usado. Cada operação recebe mensagens na forma de RST (solicitação por token de segurança) e retorna mensagens na forma de RSTR (resposta de RST) de acordo com a especificação WS-Trust. Para este artigo, estou supondo que o token emitido é um token SAML (linguagem de marcação de declaração de segurança) 1.1 ou SAML 2.0.

A Figura 1 ilustra os componentes principais do RST e RSTR para emissão de token ativo. A mensagem RST inclui informações necessárias para solicitar um token de segurança incluindo o tipo de token a ser emitido (SAML para essa discussão), as declarações solicitadas pela RP (parte confiável) a serem incluídas no token emitido, as informações sobre a RP (AppliesTo) incluindo a URL e, normalmente, o certificado que identifica a RP e, opcionalmente, (não exibido) o material principal a ser usado para a chave de prova de posse (chave de prova) retornada com o RSTR.

fig01.gif

Figura 1 Emissão de token para um cenário de federação ativa

Se a emissão de token for bem-sucedida, o RSTR incluirá o token SAML emitido e a chave de prova (supondo que o STS decide que chave de prova usar e, portanto, retorná-la no RSTR). O token SAML incluirá declarações relevantes para a parte autenticada, é assinado pelo STS para proteger o token de ser violado, contém a chave de prova criptografada para a RP e é, por si só, criptografado para a RP de forma que somente o destinatário pretendido possa processar o token.

O cliente usa a chave de prova para assinar a mensagem à RP. A RP deve ser capaz de descriptografar a chave de prova dentro do token SAML ou recusar a mensagem. Se a chave de prova dentro do token corresponder à assinatura na mensagem, isso prova que a chamada para a RP foi enviada pela parte que solicitou o token.

Cenários de federação passivos são baseados em um perfil do solicitador passivo de WS-Federation, que envolve comunicação baseada em navegador. A mensagem subjacente ainda é baseada em WS-Trust; no entanto, o RST é dividido em parâmetros de cadeia de caracteres de consulta na URL do STS e o RSTR é normalmente postado para a RP como um parâmetro de forma. A RP e o STS passivo interceptam esses parâmetros com manipuladores da Web de federação. O STS passivo pode processar solicitações WS-Trust diretamente ou transmiti-las a uma implementação WS-Trust subjacente. A Figura 2 ilustra como o RST e o RSTR são manipulados em um cenário de federação passiva.

fig02.gif

Figura 2 Emissão de token para um cenário de federação passiva

Uma diferença relevante entre os cenários de federação ativa e passiva é o tipo de token SAML emitido. A federação ativa normalmente conta com um token SAML que usa uma confirmação de objeto do tipo "portador de chave", o que significa que existe uma chave de prova usada, como eu descrevi, para provar que o cliente que envia o token a ser autenticado é quem solicitou o token (também conhecido como comportamento ActAs). Cenários de federação passiva normalmente envolvem um token SAML com uma confirmação de objeto do tipo "bearer" (também conhecido como token de portador). Esse tipo de token não inclui uma chave de prova e é, às vezes, chamado de token sem chave. Ele conta com o transporte para obtê-lo com segurança pelo STS e transmiti-lo à RP.

Esses conceitos — WS-Federation, WS-Trust e tokens SAML — são informações gerais importantes para a discussão a seguir. Primeiro, descreverei como criar um STS ativo usando a Estrutura Geneva. Em seguida, discutirei a criação de um STS passivo e, por fim, apresentarei alguns cenários estendidos para cada um.

Criando um STS ativo personalizado

Em um cenário de federação ativo simples, como o mostrado na Figura 3, os seguintes participantes normalmente estão presentes:

  • Uma RP, que é o serviço chamado por um cliente.
  • Um único STS, também implementado como um serviço, que suporta o protocolo WS-Trust. Esse STS autentica chamadores e emite um token de segurança com declarações que identificam o chamador, também conhecido como um provedor de identidades ou IP-STS.
  • Um cliente, nesse caso, um aplicativo baseado no Windows, que conta com um proxy a fim de autenticar no STS, para recuperar o token resultante emitido e enviar mensagens à RP fornecendo o token emitido para autenticação e autorização.

fig03a.gif

Figura 3 Um cenário de federação simples com uma única RP e um IP-STS ativo

A Figura 4 ilustra as partes móveis por trás dessa implementação. Incluídos estão uma implementação personalizada SecurityTokenService, o uso de uma extensão ServiceHost (WSTrustServiceHost) para inicializar o runtime para a federação, a configuração de um ou mais pontos de extremidade WS-Trust usando um derivativo do tipo WSTrustContract e outras configurações relacionadas para o runtime do modelo de identidade. Nas seções a seguir, eu irei revisar cada um desses elementos de uma implementação do STS personalizada com base na Estrutura Geneva.

fig04.gif

Figura 4 Arquitetura de implementação para um STS ativo personalizado e um IP-STS ativo

Estendendo o SecurityTokenService

A Estrutura Geneva fornece a funcionalidade principal para criar um STS personalizado pelo tipo SecurityTokenService a partir do namespace Microsoft.IdentityModel.SecurityTokenService. Essa classe abstrata manipula o trabalho pesado de processar mensagens RST e RSTR e gerar tokens de segurança. Um tipo de STS personalizado herda essa classe e fornece (no mínimo) a seguinte funcionalidade:

  • Um construtor que aceita uma instância SecurityTokenServiceConfiguration personalizada para configurar alguns recursos básicos do STS (a ser discutido posteriormente).
  • Uma substituição para GetScope validar a RP de destino para a solicitação e fornecer uma credencial de criptografia adequada para essa RP e uma credencial de assinatura para o token de segurança.
  • Uma substituição para GetOutputClaimsIdentity fornecer declarações para o token de segurança resultante.

A Figura 5 mostra alguns códigos para uma implementação do STS personalizada simples com essa funcionalidade. Lembre-se do fluxo de comunicação para um STS ativo das Figuras 1 e 2. A implementação do STS, IdentitySTS, valida o RST de entrada quando GetScope é chamado — verificando se o elemento AppliesTo do RST realmente aponta para um URI confiável. Presumivelmente, o STS gerencia uma lista de RPs confiáveis para as quais tokens podem ser emitidos, juntamente com seus certificados. GetScope define a propriedade EncryptingCredentials do escopo para o certificado apropriado se AppliesTo passar na validação, neste caso, "RPKey". Além disso, a propriedade SigningCredentials é definida para o certificado apropriado para ser usada para assinar o token emitido. Isso é normalmente a chave privada do STS, neste caso, "IPKey".

Figura 5 Uma implementação do STS personalizada simples

public class IdentitySTS : SecurityTokenService
{
    public IdentitySTS(SecurityTokenServiceConfiguration config)
        : base( config )
    {
    }

    protected override IClaimsIdentity GetOutputClaimsIdentity(
        IClaimsPrincipal principal, RequestSecurityToken request, 
        Scope scope)
    {
        IClaimsIdentity claimsIdentity = new ClaimsIdentity();

        claimsIdentity.Claims.Add(new Claim(ClaimTypes.Name, 
            principal.Identity.Name));
        claimsIdentity.Claims.Add(new Claim(ClaimTypes.Role, "Users"));

        return claimsIdentity;
    }

    protected override Scope  GetScope(
        Microsoft.IdentityModel.Claims.IClaimsPrincipal principal, 
        RequestSecurityToken request)
    {

        Scope scope = new Scope(request);
        scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(
                                                     request.AppliesTo);
        scope.SigningCredentials = new 
          X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My, 
          StoreLocation.LocalMachine, "CN=IPKey"));
        return scope;
    }

    private X509EncryptingCredentials GetCredentialsForAppliesTo(Endpoint
        Address appliesTo)
    {
        if (appliesTo == null || appliesTo.Uri ==null || 
          string.IsNullOrEmpty(appliesTo.Uri.AbsolutePath))
        {
            throw new InvalidRequestException(
                "AppliesTo must be supplied in the RST.");
        }

        X509EncryptingCredentials creds = null;
        if (appliesTo.Uri.AbsoluteUri.StartsWith(
            "http://localhost:8000/RelyingPartyService"))
        {
            creds = new X509EncryptingCredentials(
                CertificateUtil.GetCertificate(StoreName.TrustedPeople, 
                StoreLocation.LocalMachine, 
                "CN=RPKey"));
        }
        else
            throw new InvalidRequestException(String.Format(
                "Invalid relying party address: {0}", 
                appliesTo.Uri.AbsoluteUri));

        return creds;
    }
}

Quando GetOutputClaimsIdentity é chamado, ClaimsPrincipal é transmitido pelo runtime com a identidade do chamador autenticado. Essa identidade é normalmente usada para determinar as declarações apropriadas a serem concedidas ao chamador. Na Figura 5, o código gera uma declaração Name e uma declaração Role codificada para o chamador e ele retorna isso na forma de um ClaimsIdentity. Esse ClaimsIdentity fornece declarações ao runtime para o token a ser emitido.

Essa implementação do STS poderia também ser estendida com a seguinte funcionalidade:

  • GetOutputClaimsIdentity poderia incluir códigos para procurar o usuário em um armazenamento de credenciais personalizado e procurar declarações adicionais. Por exemplo, uma lista de funções, outros detalhes relevantes sobre o usuário, como seu endereço de e-mail ou declarações personalizadas que representam direitos de aplicativos mais granulares, como criar, ler, atualizar ou excluir.
  • GetScope poderia procurar o URI AppliesTo em um banco de dados personalizado que lista todas as RPs confiáveis com seus certificados associados.

Hospedando e configurando o STS

Se você está familiarizado com o WCF, sabe que um ou mais pontos de extremidade devem ser configurados para que os clientes enviem mensagens para um serviço. No caso de um STS, o contrato de serviço a ser usado para cada ponto de extremidade deve ser baseado no protocolo WS-Trust, que inclui quatro operações: Issue, Validate, Renew e Cancel. Na verdade, existem duas versões do protocolo WS-Trust que poderiam ser implementadas por um STS:

  • WS-Trust 1.3: a versão mais recente da especificação WS-Trust.
  • WS-Trust February 2005: a versão do WS-Trust que vários parceiros da indústria implementaram enquanto esperavam o padrão ser ratificado.

Você também pode fornecer implementações assíncronas para GetScope(), GetOutputClaimsIdentity() — entre outros métodos — no seu tipo SecurityTokenService. Isso melhora a escalabilidade para operações intensivas de E/S, como acessar certificados ou interagir com dados de declarações. Quando você configura pontos de extremidades para seu STS, deve selecionar que contrato expor para o ponto de extremidade. O namespace Microsoft.IdentityModel.Protocols inclui esses dois contratos de serviço para pontos de extremidades do STS: IWSTrust13SyncContract e IWSTrustFeb2005SyncContract. A Figura 6 mostra a configuração para um serviço STS com dois pontos de extremidade, um para cada contrato. Observe que existem também versões assíncronas do contrato, que seriam usadas para implementar um proxy assíncrono: IWSTrust13AsyncContract e IWSTrustFeb2005AsyncContract.

Figura 6 Configuração do serviço do STS com vários pontos de extremidade WS-Trust

<service name=
  "Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract" 
  behaviorConfiguration="stsBehavior">
  <endpoint address="WSTrustFeb05" binding="wsHttpBinding" 
    contract="Microsoft.IdentityModel.Protocols.WSTrust.
    IWSTrustFeb2005SyncContract"/>
  <endpoint address="WSTrust13" binding="wsHttpBinding"  
    contract="Microsoft.IdentityModel.Protocols.WSTrust.
    IWSTrust13SyncContract"/>
</service>

O STS deve expor um ponto de extremidade com base no WS-Trust February 2005 para compatibilidade com versões anteriores com clientes preexistentes. O tipo de serviço que implementa os dois contratos é o tipo WSTrustServiceContract encontrado no namespace Microsoft.IdentityModel.Protocols.WSTrust. Esse é o tipo que deveria ser referenciado na seção de configuração <service> para o STS.

Como ilustrado no diagrama da Figura 4, a configuração <service> e seus pontos de extremidade são usados para inicializar o host com o tipo de WSTrustServiceContract correto. Esse tipo também é inicializado com uma referência à implementação personalizada de SecurityTokenService durante a inicialização do host. É assim que o runtime direciona mensagens ao STS personalizado.

Na Figura 6, os pontos de extremidade do STS contam com as credenciais do Windows para autenticar chamadores (o comportamento padrão de wsHttpBinding). O STS pode expor vários pontos de extremidade com configurações de vinculação alternativas para suportar diferentes tipos de credenciais. Isso também envolve a configuração de um manipulador de token de segurança apropriado para cada tipo de credencial. Discutirei as configurações do manipulador de token em breve.

A Estrutura Geneva fornece um tipo de ServiceHost personalizado, WSTrustServiceHost, a ser usado para hospedar instâncias do STS. O código a seguir ilustra como construir o tipo de WSTrustServiceHost em um ambiente de hospedagem automático:

WSTrustServiceHost stsHost = 
  new WSTrustServiceHost(new IdentitySTSConfiguration());
stsHost.Open();

WSTrustServiceHost conta com uma instância personalizada SecurityTokenServiceConfiguration para inicializar o runtime com pontos de extremidade WS-Trust, habilitar o comportamento de troca de metadados para o STS e configurar um ponto de extremidade de troca de metadados.

Quando hospedado no IIS, o tipo de WSTrustServiceHostFactory é usado para atingir os mesmos resultados. No arquivo .svc, a configuração @ServiceHost especifica o tipo de factory e o tipo de SecurityTokenServiceConfiguration personalizado, como a seguir:

<%@ ServiceHost Factory="Microsoft.IdentityModel.Protocols.WSTrust. 
  WSTrustServiceHostFactory" 
  Service="STS.IdentitySTSConfiguration"  %>

O factory inicializa o WSTrustServiceHost com a configuração especificada na ativação.

Um tipo personalizado de SecurityTokenServiceConfiguration é necessário para inicializar o WSTrustServiceHost para um STS. A Figura 7 mostra uma implementação personalizada chamada IdentitySTSConfiguration.

Figura 7 Um SecurityTokenServiceConfiguration personalizado

public class IdentitySTSConfiguration: SecurityTokenServiceConfiguration
{

    public IdentitySTSConfiguration(): base("http://localhost:8010/sts")
    {

      this.TokenIssuerName = "http://localhost:8010/sts";

      this.SigningCredentials = new 
        X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My, 
        StoreLocation.LocalMachine, "CN=IPKey"));

      this.SecurityTokenService = typeof( IdentitySTS);

    }

}

Esse tipo deve fornecer um URI para o STS, uma credencial de assinatura e uma referência ao tipo do STS com o qual a configuração é associada. A URL deve ser válida caso o STS emita cartões gerenciados — de forma que Windows CardSpace possa importar esses cartões. O tipo base requer que um valor de cadeia de caracteres seja transmitido ao construtor para TokenIssuerName, mas eu recomendo substituir isso no código para que você possa definir dinamicamente a URI da configuração, em vez de codificar esse valor transmitido ao construtor.

O tipo de SecurityTokenServiceConfiguration também expõe propriedades que podem ser usadas para definir padrões para tamanho de chave e tipo de token, desabilitar o acesso a metadados, controlar o tempo de vida do token e a defasagem de hora, definir serializadores personalizados RST e RSTR, configurar manipuladores de token que autenticam chamadores e configurar pontos de extremidade WS-Trust.

O exemplo a seguir mostra como desabilitar acesso a metadados e como inicializar pontos de extremidade WS-Trust (como os exibidos na Figura 6) programaticamente, em vez de contar com as configurações <service>:

IdentitySTSConfiguration config = new IdentitySTSConfiguration();
config.DisableWsdl = true;
config.TrustEndpoints.Add(new 
  ServiceHostEndpointConfiguration("WSTrustFeb05", new WSHttpBinding(),  
  typeof(IWSTrustFeb2005SyncContract)));
config.TrustEndpoints.Add(new ServiceHostEndpointConfiguration(
  "WSTrust13", new WSHttpBinding(), 
  typeof(IWSTrust13SyncContract)));

WSTrustServiceHost stsHost = new WSTrustServiceHost(config);

O tipo base de SecurityTokenServiceConfiguration também lê a seção de configuração <microsoft.identityModel> para inicializar configurações relevantes do STS. As definições que podem ser configuradas de forma declarativa para um STS personalizado incluem a defasagem máxima de hora, os manipuladores do token de segurança para autenticação e emissão, um gerente de autenticação de declarações, um registro de nome do emissor e resolvedores de token. A Figura 8 mostra algumas configurações úteis para um STS.

Figura 8 Configuração de <microsoft.identityModel> para um STS

<microsoft.identityModel>
  <maximumClockSkew value="00:05:00"/>
  <claimsAuthenticationManager type="STS.
    CustomClaimsAuthenticationManager, STS"/>
  <securityTokenHandlers>
    <remove type="Microsoft.IdentityModel.Tokens.
      WindowsUserNameSecurityTokenHandler, 
      Microsoft.IdentityModel, Version=0.5.1.0, Culture=neutral, 
      PublicKeyToken=31bf3856ad364e35" />
    <add type="Microsoft.IdentityModel.Tokens.
      MembershipUserNameSecurityTokenHandler, 
      Microsoft.IdentityModel, Version=0.5.1.0, Culture=neutral, 
      PublicKeyToken=31bf3856ad364e35">
        <usernameSecurityTokenHandlerRequirement 
          membershipProvider="CustomProviders.CustomMembershipProvider, 
          CustomProviders, Version=1.0.0.0, Culture=neutral,
           PublicKeyToken=c03h5a64f15d0b3f" />
    </add>
  </securityTokenHandlers>
</microsoft.identityModel>

Um tipo personalizado de ClaimsAuthenticationManager é invocado para credenciais que não sejam do Windows, dando a opção de fornecer um tipo personalizado de ClaimsPrincipal ao runtime antes de emitir declarações em GetOutputClaimsIdentity. Manipuladores de token de segurança personalizados podem ser configurados para fornecer configurações que substituem o comportamento padrão de um determinado manipulador ou alteram a opção do manipulador de token de segurança para um tipo de credencial particular.

Manipuladores de token de segurança

Você provavelmente espera uma configuração de comportamento de serviço para determinar como a autenticação e a autorização irão ocorrer para cada ponto de extremidade do STS. Lembre-se pelo meu último artigo que a Estrutura Geneva faz as coisas de maneira um pouco diferente. No caso da autenticação, a coleção <securityTokenHandlers> especifica os tipos de SecurityTokenHandler disponíveis para autenticar solicitações de entrada.

Essa coleção só pode conter uma entrada para cada tipo de SecurityTokenHandler. Por exemplo, apenas um KerberosSecurityTokenHandler, UserNameSecurityTokenHandler, X509SecurityTokenHandler, Saml11SecurityTokenHandler ou Saml2SecurityTokenHandler pode ser registrado para processar solicitações para seus respectivos tipos de credenciais. Se o ponto de extremidade do STS espera uma credencial UserName, o UserNameSecurityTokenHandler padrão registrado é WindowsUserNameSecurityTokenHandler. A Figura 8 ilustra a remoção de WindowsUserNameSecurityTokenHandler e a adição de MembershipUserNameSecurityTokenHandler como seu substituto — incluindo configurações relacionadas para o provedor de associação.

Você também pode criar tipos personalizados de SecurityTokenHandler. Lembre-se, desde que eles derivem da classe base apropriada correspondente à categoria do token (como UserNameSecurityTokenHandler), você pode substituir o manipulador padrão com o novo manipulador personalizado. A Figura 9 ilustra uma implementação personalizada de UserNameSecurityTokenHandler chamada CustomUserNameSecurityTokenHandler.

Figura 9 Um UserNameSecurityTokenHandler personalizado

public class CustomUserNameSecurityTokenHandler:   
  UserNameSecurityTokenHandler
{
  public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
  {
    UserNameSecurityToken userNameToken = token as UserNameSecurityToken;
    AuthenticateUser(userNameToken.UserName, userNameToken.Password);

    return new ClaimsIdentityCollection(new IClaimsIdentity[] {
      new ClaimsIdentity(
        new Claim(System.IdentityModel.Claims.ClaimTypes.Name,
         userNameToken.UserName), "CustomUserNameSecurityTokenHandler")});
  }

  public override bool CanValidateToken
  {
    get { return true; }
  }
}

No mínimo, ValidateToken e CanValidateToken devem ser substituídos na implementação personalizada de SecurityTokenHandler. Dentro de ValidateToken, você é responsável por autenticar em relação ao armazenamento de credenciais apropriado. Os resultados dessa autenticação devem ser um conjunto de declarações que podem ser retornados ao runtime a ser vinculado ao ClaimsPrincipal para o thread de solicitação.

fig10.gif

Figura 10 Um cenário de federação simples com uma única RP e um IP-STS passivo

Também é muito importante substituir CanValidateToken e retornar True. Sem essa substituição, o manipulador de token não será registrado na coleção e nunca será invocado.

Criando um STS passivo personalizado

Em um cenário de federação passiva simples, os mesmos participantes estão presentes, como mostrado na Figura 3, para um cenário de federação ativa — exceto que o cliente é um navegador, a RP é um aplicativo Web e o IP-STS também é apoiado por um aplicativo Web para manipular comunicações baseadas em HTTP. A Figura 10 ilustra os participantes e o fluxo de comunicação da federação passiva.

As peças móveis por trás desse cenário são similares às exibidas na Figura 4, em relação ao STS principal, mas existem algumas diferenças óbvias sobre como o STS passivo manipula a autenticação e como a funcionalidade do STS subjacente é invocada. O diagrama da Figura 10 ilustra essas diferenças de forma geral. O STS passivo é implementado como um site que requer criptografia SSL para proteger o processo de emissão de token. A página padrão (Default.aspx) hospeda um controle que facilita a comunicação com o STS personalizado subjacente, configurado apenas como um STS ativo.

O site do STS deve autenticar o chamador antes da emissão do token e é aqui onde a configuração clássica do ASP.NET entra em jogo para a autenticação e a autorização. Na Figura 11, o aplicativo STS é configurado para autenticação de Forms, então as solicitações são redirecionadas para uma página de login (Login.aspx) se elas ainda não tiverem sido autenticadas pelo FormsAuthenticationModule.

Um STS passivo pode compartilhar a mesma implementação do STS principal como um STS ativo — com uma alteração menor. Na substituição de GetScope (mostrada na Figura 5) um STS passivo deve definir a propriedade ReplyToAddress para que o STS possa redirecionar após emitir o token. Normalmente, isso é definido para a página padrão para a RP, com base no endereço AppliesTo fornecido com o RST:

Scope scope = new Scope(request);
scope.ReplyToAddress = scope.AppliesToAddress + "/default.aspx";
// other scope settings

fig11.gif

Figura 11 Arquitetura de implementação para um STS passivo usando autenticação de formulários

A configuração da Estrutura Geneva para um STS passivo não é diferente de um STS ativo. O tipo de SecurityTokenServiceConfiguration é usado para inicializar o STS (mostrado na Figura 7) e qualquer configuração relevante na seção de configuração <microsoft.identityModel> também é considerada.

Controle FederatedPassiveTokenService

A Estrutura Geneva fornece um controle que implementa a funcionalidade necessária de um STS passivo. Isto é, ela processa solicitações HTTP de entrada e saída, convertendo cada solicitação em um RST e, em seguida, invocando a implementação do STS subjacente. O controle também processa respostas RSTR e manipula o redirecionamento à RP, escrevendo o cookie da sessão para chamadores autenticados.

Coloque esse controle na página padrão para o site do STS passivo e defina sua propriedade Service para a implementação personalizada de SecurityTokenServiceConfiguration como a seguir:

<idfx:FederatedPassiveTokenService ID="FederatedPassiveTokenService1" 
  runat="server" Service="STS.IdentityProviderSTSConfiguration, STS">
</idfx:FederatedPassiveTokenService>

O controle requer que o usuário seja autenticado e verifique isso em seu evento PreRender. Espera-se que o site do STS esteja configurado adequadamente para garantir que os usuários sejam redirecionados a outro lugar a fim de autenticar antes de chegar a essa página padrão.

Desde que usuários autenticados sejam sempre direcionados a essa página padrão, nenhuma outra configuração é necessária para processar solicitações. O controle também fornece os eventos Error, PreSignInRequested, PostSignInRequested, PreSignOutRequested e PostSignOutRequested para manipular exceções e vincular solicitações de entrada e saída.

SessionAuthenticationModule

Como uma alternativa ao controle FederatedPassiveTokenService, você pode habilitar programaticamente a funcionalidade do STS passivo. Primeiro, habilite a autenticação federada na seção de configuração <microsoft.identityModel>:

<microsoft.identityModel>
  <federatedAuthentication enabled="true"/>
</microsoft.identityModel>

Em seguida, habilite o módulo de federação para um STS passivo, SessionAuthenticationModule do namespace Microsoft.IdentityModel.Web:

<modules>
  <add name="SessionAuthentication" 
    type="Microsoft.IdentityModel.Web.SessionAuthenticationModule,
    Microsoft.IdentityModel, Version=0.5.1.0,
    Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>

Isso produz o mesmo resultado do controle FederatedPassiveTokenService para solicitações enviadas a qualquer página do site do STS. O módulo redireciona chamadores não autenticados para a página de login. Com o login bem-sucedido, os chamadores são redirecionados para a página do STS solicitada originalmente. Essa abordagem programática fornece aos desenvolvedores controle adicional além do fornecido pelo controle FederatedPassiveTokenService. Por exemplo, o módulo expõe os seguintes eventos para interagir com a inicialização, o gerenciamento de token de segurança, a entrada e a saída: ConfigurationLoading, ConfigurationLoaded, SecurityTokenReceived, SecurityTokenValidated, SessionSecurityTokenCreated, SessionSecurityTokenReceived, SignedIn, SigningOut, SignedOut, SignInError e SignOutError.

Autenticação do usuário

O site do STS é responsável por autenticar usuários de acordo com os tipos de credenciais suportados. Embora um STS ativo implementado com o WCF possa facilmente configurar vários pontos de extremidade para suportar diferentes mecanismos de autenticação, um STS passivo pode suportar apenas um mecanismo de autenticação pela natureza da configuração do site do ASP.NET. Portanto, um site diferente do STS passivo deve ser fornecido para cada mecanismo de autenticação suportado. Esses sites podem todos compartilhar a mesma implementação principal do STS, é claro.

A autenticação do STS passivo é baseada em técnicas de autenticação do ASP.NET. As opções típicas são autenticação do Windows, autenticação de Forms e autenticação do Windows CardSpace. No caso da autenticação do Windows, a seguinte configuração é usada:

<authentication mode="Windows"/>
<authorization>
  <deny users="?"/>
</authorization>

O usuário será autenticado por caixas de diálogo interativas antes de atingir a página padrão do STS, então uma página de login personalizada não é necessária nesse caso.

A Figura 11 ilustra um exemplo no qual a autenticação de Forms é usada. O FormsAuthenticationModule redireciona chamadas não autenticadas para a página de login onde o usuário pode fornecer credenciais. Depois da autenticação, a página de login irá redirecionar para a página do STS padrão, onde o controle de federação continuará para processar a solicitação original. A configuração do ASP.NET para isso seria a seguinte:

<authentication mode="Forms"/>
<authorization>
  <deny users="?"/>
</authorization>

No caso da autenticação do Windows CardSpace, o site do STS pode ser configurado para autenticação de Forms, mas a página de login incluirá um controle InformationCard para autenticação do Windows CardSpace.

Transformação de declarações

A transformação de declarações é essencial para a segurança federada — e isso pode ocorrer em uma variedade de pontos no fluxo da federação. Em um simples cenário de federação, onde o IP-STS e a RP pertencem ao mesmo domínio, o IP-STS é responsável por transformar declarações do conjunto inicial de declarações de identidade que o usuário apresenta durante a autenticação, para aquelas com as quais a RP pode contar para autorizar chamadas. Os usuários podem apresentar qualquer um dos tipos de credenciais suportados para autenticar para o IP-STS, cada um sendo avaliado para um conjunto de declarações representativas da credencial.

O IP-STS transforma essas declarações em um conjunto normalizado de declarações de aplicativos que a RP conta com para autorizar chamadas — que podem ser funções ou declarações mais granulares, como direitos de criar, ler, atualizar ou excluir. O diagrama da Figura 12 ilustra como um cenário onde o usuário Admin efetua login e recebe uma declaração Role e várias declarações Action, incluindo Create, Read, Update e Delete.

fig12.gif

Figura 12 Transformação de declarações no IP-STS

Esse tipo de transformação de declarações é útil porque a autenticação para um STS resulta em um token contendo todas as declarações concedidas ao usuário. Existem casos onde uma abordagem alternativa à transformação de declarações seria necessária; por exemplo, para reduzir as declarações emitidas àquelas relevantes apenas para o contexto de chamada atual; para proteger a privacidade de declarações; ou para facilitar a federação por domínios.

Nem sempre é desejável ou apropriado conceder a um chamador autenticado uma lista longa de declarações relacionadas a todos os recursos expostos pela RP. Além disso, a lista não poderia ser muito longa, mas também é possível que as declarações a serem concedidas dependam do contexto da chamada e, portanto, não devem ser emitidas sem esse contexto. Por exemplo, um usuário apenas pode receber a declaração Delete se ela estiver interagindo com os pedidos do cliente, mas, se ela estiver interagindo diretamente com os registros do cliente, é possível que não receba esse direito.

Em casos como esse, pode ser útil para a RP solicitar apenas algumas declarações do IP-STS para identificar o chamador e, em seguida, solicitar um novo token com um conjunto específico de declarações adicionais somente para o contexto da chamada. Por exemplo, se o usuário estiver invocando a operação DeleteCustomer em um serviço da RP, antes de autorizar o acesso à operação, a RP chama o RP-STS transmitindo o token do IP-STS, solicitando a declaração Delete no contexto da operação DeleteCustomer. Se a declaração estiver presente, a chamada será autorizada. O diagrama da Figura 13 ilustra esse exemplo.

Existem também casos em que as declarações emitidas por um STS não devem ser compartilhadas com a RP diretamente. Por exemplo, em vez de emitir uma declaração Age para que a RP saiba a idade do usuário, a RP pode solicitar a declaração IsOver13 para garantir que o chamador tenha idade suficiente para usar a funcionalidade da RP. Portanto, o valor real para a declaração Age nunca deixa o STS. É claro que isso requer que o ST forneça declarações que evitam o compartilhamento de detalhes pessoais e, ainda, inclui dados úteis para a RP.

fig13.gif

Figura 13 Transformação de declarações no RP-STS

A transformação de declarações também ocorre em um cenário federado onde os usuários que pertencem a um domínio recebem acesso a uma RP em outro domínio. Nesse caso, existem dois STS envolvidos — o IP-STS do domínio do usuário e o RP-STS do domínio que possui a RP.

E, também nesse caso, o IP-STS concederá alguma declaração escolhida pelas partes que o RP-STS pode entender; no entanto, essas declarações provavelmente não serão diretamente úteis no aplicativo da RP. Em vez disso, o RP-STS seria responsável por transformar outro conjunto confiável de declarações em declarações entendidas no domínio das RPs.

A Figura 14 ilustra esse cenário. Quando o José tenta acessar a RP sem um token, ele fará login no IP-STS em seu próprio domínio (Domínio B). A solicitação pedirá declarações que a RP entenda, nesse caso, RPClaim, para que o IP-STS saiba emitir um token que a RP possa usar. Quando o RP-STS recebe esse token, ele transforma as declarações em declarações específicas da RP. Para esse cenário federado funcionar, deve haver confiança entre o RP-STS e o IP-STS e eles devem concordar com um conjunto de declarações que o IP-STS deve emitir para seus usuários que devem receber acesso à RP.

fig14.gif

Figura 14 Transformação de declarações em um cenário federado

Conclusão

A Estrutura Geneva é um utilitário muito bem-vindo para as pessoas interessadas em criar um STS personalizado e que não precisam de uma plataforma do STS repleta de recursos, como o Servidor Geneva. Criar um STS personalizado não é uma tarefa trivial, mesmo com a Estrutura Geneva, e é sempre recomendado que você use um STS repleto de recursos se possível para reduzir a exposição.

Independentemente da plataforma, o fluxo de comunicação para implementações do STS ativas e passivas permanece o mesmo, assim como as idéias por trás da transformação de declarações. Alguns conceitos adicionais, relacionados a implementações do STS, incluem delegação de identidade e autenticação step-up. Você pode acessar exemplos e documentos relacionados a esses e outros conceitos no SDK da Estrutura Geneva.

Michele Leroux Bustamante é a principal arquiteta de software da IDesign Inc., diretora regional da Microsoft em San Diego e MVP da Microsoft para sistemas conectados. Seu mais novo livro é Learning WCF (Aprendendo sobre WCF). Entre em contato com ela pelo email mlb@idesign.net ou visite idesign.net. O blog da Michele é dasblonde.net.