Compartilhar via


Personalizar as declarações emitidas no JWT (Token Web JSON) para aplicativos empresariais

A plataforma de identidade da Microsoft oferece suporte ao logon único (SSO) com a maioria dos aplicativos pré-integrados na galeria de aplicativos do Microsoft Entra e aplicativos personalizados. Quando um usuário se autentica em um aplicativo pela plataforma de identidade da Microsoft usando o protocolo OIDC, ele envia um token para o aplicativo. O aplicativo é validado e usa o token para conectar o usuário em vez de solicitar um nome de usuário e a senha.

Esses tokens JSON Web (JWT) usados pelos aplicativos OIDC e OAuth contêm informações sobre o usuário conhecidas como declarações. Uma declaração são informações que um provedor de identidade declara sobre um usuário dentro do token que emite para esse usuário. Em uma resposta OIDC, os dados de declarações normalmente estão contidos no Token de ID emitido pelo provedor de identidade na forma de um JWT.

Exibir ou editar declarações

Dica

As etapas neste artigo podem variar ligeiramente com base no portal do qual você começa.

Para exibir ou editar as declarações emitidas no JWT para o aplicativo:

  1. Entre no Centro de administração do Microsoft Entra como pelo menos Administrador de Aplicativo de nuvem.
  2. Navegue até Identidade>Aplicativos>Aplicativos Empresariais>Todos os aplicativos.
  3. Selecione o aplicativo, selecione Logon único no menu à esquerda e, em seguida, selecione Editar na seção Atributos e Declarações.

Um aplicativo pode precisar de uma personalização de declarações por várias razões. Por exemplo, quando um aplicativo requer um conjunto diferente de URIs de declaração ou valores de declaração. Usando a seção Atributos e Declarações, você pode adicionar ou remover uma declaração para o seu aplicativo. Você também pode criar uma declaração personalizada específica para um aplicativo com base no caso de uso.

As etapas a seguir descrevem como atribuir um valor constante:

  1. Selecione a declaração que você deseja modificar.
  2. Insira o valor da constante sem aspas no Atributo de origem de acordo com sua organização e selecione Salvar.

A visão geral de Atributos mostra o valor constante.

Transformações de declarações especiais

Você pode usar as funções de transformações de declarações especiais a seguir.

Função Descrição
ExtractMailPrefix() Remove o sufixo de domínio de endereço de email ou nome UPN. Essa função extrai apenas a primeira parte do nome de usuário. Por exemplo, joe_smith em vez de joe_smith@contoso.com.
ToLower() Converte os caracteres do atributo selecionado em caracteres minúsculos.
ToUpper() Converte os caracteres do atributo selecionado em caracteres maiúsculos.

Adicionar declarações específicas do aplicativo

Para adicionar declarações específicas a um aplicativo:

  1. Em Declarações e Atributos do Usuário, selecione Adicionar nova declaração para abrir o painel Gerenciar declarações de usuário.
  2. Insira o nome das declarações. O valor não precisa necessariamente seguir um padrão de URI. Se você precisar de um padrão de URI, poderá colocá-lo no campo Namespace.
  3. Selecione a Origem em que a declaração vai recuperar o valor dela. Você pode selecionar um atributo de usuário na lista suspensa de atributo de origem ou aplicar uma transformação ao atributo de usuário antes de emiti-lo como uma declaração.

Transformações de declaração

Para aplicar uma transformação a um atributo de usuário:

  1. Em Gerenciar declaração, selecione Transformação como a origem da declaração para abrir a página Gerenciar transformação.
  2. Selecione a função na lista suspensa de transformação. Dependendo da função selecionada, você precisará fornecer parâmetros e um valor constante a serem avaliados na transformação.
  3. A opção Tratar a fonte como contendo múltiplos valores indica se a transformação deve ser aplicada a todos os valores ou apenas ao primeiro. Por padrão, as transformações são aplicadas ao primeiro elemento em uma declaração de múltiplos valores. Ao marcar essa caixa, você garante que seja aplicada a todos. Essa caixa de seleção só é habilitada para atributos com múltiplos valores. Por exemplo, user.proxyaddresses.
  4. Para aplicar várias transformações, selecione Adicionar transformação. Você pode aplicar um máximo de duas transformações a uma declaração. Por exemplo, você pode primeiro extrair o prefixo de email do user.mail. Em seguida, converta a cadeia de caracteres em maiúsculas.

É possível usar as funções a seguir para transformar declarações.

Função Descrição
ExtractMailPrefix() Remove o sufixo de domínio de endereço de email ou nome UPN. Essa função extrai apenas a primeira parte do nome de usuário. Por exemplo, joe_smith em vez de joe_smith@contoso.com.
Join() Cria um valor unindo dois atributos. Opcionalmente, você pode usar um separador entre os dois atributos. Para a transformação de declaração NameID, a função Join() tem um comportamento específico quando a entrada de transformação tem uma parte de domínio. Ele remove a parte de domínio da entrada antes de juntá-la com o separador e o parâmetro selecionado. Por exemplo, se a entrada da transformação for "joe_smith@contoso.com", o separador for @ e o parâmetro for fabrikam.com, essa combinação de entradas resultará em joe_smith@fabrikam.com.
ToLowercase() Converte os caracteres do atributo selecionado em caracteres minúsculos.
ToUppercase() Converte os caracteres do atributo selecionado em caracteres maiúsculos.
Contains() Gera um atributo ou constante se a entrada corresponde ao valor especificado. Caso contrário, você poderá especificar outra saída se não houver correspondência.
Por exemplo, se você quiser emitir uma declaração cujo valor seja o endereço de email do usuário caso ele contenha o domínio "@contoso.com". Caso contrário, você deve querer gerar o nome UPN. Para executar essa função, configure os seguintes valores:
Parâmetro 1 (entrada) : user.email
Valor: "@contoso.com"
Parâmetro 2 (saída): user.email
Parâmetro 3 (saída se não houver correspondência): user.userprincipalname
EndWith() Gera um atributo ou constante se a entrada termina com o valor especificado. Caso contrário, você poderá especificar outra saída se não houver correspondência.
Por exemplo, se você quiser emitir uma declaração que terá o valor da ID do funcionário quando essa ID terminar com 000, mas quiser gerar um atributo de extensão quando o final dessa ID for diferente. Para executar essa função, configure os seguintes valores:
Parâmetro 1 (entrada) : user.employeeid
Valor: "000"
Parâmetro 2 (saída): user.employeeid
Parâmetro 3 (saída se não houver correspondência): user.extensionattribute1
StartWith() Gera um atributo ou constante se a entrada começa com o valor especificado. Caso contrário, você poderá especificar outra saída se não houver correspondência.
Por exemplo, se você quiser emitir uma declaração em que o valor será a ID de funcionário do usuário quando o país/região começar comUS, mas quiser gerar um atributo de extensão quando o início do país/região for diferente. Para executar essa função, configure os seguintes valores:
Parâmetro 1 (entrada) : user.country
Valor: "EUA"
Parâmetro 2 (saída): user.employeeid
Parâmetro 3 (saída se não houver correspondência): user.extensionattribute1
Extract() – Após a correspondência Retorna a substring após ela corresponder ao valor especificado.
Por exemplo, se o valor da entrada for Finance_BSimon, o valor correspondente é Finance_ e a saída da declaração será BSimon.
Extract() – Antes da correspondência Retorna a substring até ela corresponder ao valor especificado.
Por exemplo, se o valor da entrada for BSimon_US, o valor correspondente é _US e a saída da declaração será BSimon.
Extract() – Entre a correspondência Retorna a substring até ela corresponder ao valor especificado.
Por exemplo, se o valor da entrada for Finance_BSimon_US, o primeiro valor correspondente é Finance_, o segundo valor correspondente é _US e a saída da declaração será BSimon.
ExtractAlpha() – Prefixo Retorna a parte alfabética do prefixo da cadeia de caracteres.
Por exemplo, se for BSimon_123, o valor da entrada retornará BSimon.
ExtractAlpha() – Sufixo Retorna a parte alfabética do sufixo da cadeia de caracteres.
Por exemplo, se for 123_Simon, o valor da entrada retornará Simon.
ExtractNumeric() – Prefixo Retorna a parte numérica do prefixo da cadeia de caracteres.
Por exemplo, se for 123_BSimon, o valor da entrada retornará 123.
ExtractNumeric() – Sufixo Retorna a parte numérica do sufixo da cadeia de caracteres.
Por exemplo, se for BSimon_123, o valor da entrada retornará 123.
IfEmpty() Gera um atributo ou constante se a entrada for nula ou vazia.
Por exemplo, se você quiser gerar um atributo armazenado em um atributo de extensão no caso de a ID de funcionário de um determinado usuário estar vazia. Para executar essa função, configure os seguintes valores:
Parâmetro 1 (entrada): user.employeeid
Parâmetro 2 (saída): user.extensionattribute1
Parâmetro 3 (saída se não houver correspondência): user.employeeid
IfNotEmpty() Gera um atributo ou constante se a entrada não for nula ou vazia.
Por exemplo, se você quiser gerar um atributo armazenado em um atributo de extensão no caso de a ID de funcionário de um determinado usuário não estar vazia. Para executar essa função, configure os seguintes valores:
Parâmetro 1 (entrada): user.employeeid
Parâmetro 2 (saída): user.extensionattribute1
Substring() - Comprimento Fixo Extrai partes de um tipo de declaração de cadeia de caracteres, iniciando pelo caractere na posição especificada e retorna o número especificado de caracteres.
SourceClaim – a origem da declaração da transformação que deve ser executada.
StartIndex – a posição do caractere inicial com base em zero de uma substring nessa instância.
Comprimento – o comprimento em caracteres da substring.
Por exemplo:
sourceClaim – PleaseExtractThisNow
StartIndex – 6
Comprimento – 11
Saída: ExtractThis
Substring() - EndOfString Extrai partes de um tipo de declaração de cadeia de caracteres, iniciando pelo caractere na posição especificada e retorna o restante da declaração do índice inicial especificado.
SourceClaim: a origem da declaração da transformação.
StartIndex – a posição do caractere inicial com base em zero de uma substring nessa instância.
Por exemplo:
sourceClaim – PleaseExtractThisNow
StartIndex – 6
Saída: ExtractThisNow
RegexReplace() A transformação RegexReplace() aceita como parâmetros de entrada:
- Parâmetro 1: um atributo de usuário como entrada regex
- Uma opção para confiar na fonte como múltiplos valores
- Padrão Regex
- Padrão de substituição. O padrão de substituição pode conter formato de texto estático, juntamente com uma referência que aponte para grupos de saída regex e mais parâmetros de entrada.

Se você precisar de outras transformações, envie sua ideia no fórum de comentários no Microsoft Entra ID na categoria Aplicativo SaaS.

Transformação de declarações baseadas em Regex

A imagem a seguir mostra um exemplo do primeiro nível de transformação:

Captura de tela do primeiro nível de transformação.

A tabela a seguir fornece informações sobre o primeiro nível de transformações. As ações listadas na tabela correspondem aos rótulos na imagem anterior. Selecionar Editar para abrir a folha de transformação de declarações.

Ação Campo descrição
1 Transformation Selecione a opção RegexReplace() nas opções Transformação para usar o método de transformação de declarações baseada em regex para transformação de declarações.
2 Parameter 1 A entrada para a transformação de expressão regular. Por exemplo, user.mail que tem um endereço de email do usuário, como admin@fabrikam.com.
3 Treat source as multivalued Alguns atributos de usuário de entrada podem ser atributos de usuário de vários valores. Se o atributo de usuário selecionado der suporte a vários valores e o usuário quiser usar vários valores para a transformação, precisará selecionar Tratar a fonte como múltiplos valores. Se selecionado, todos os valores serão usados para a correspondência regex, caso contrário, somente o primeiro valor será usado.
4 Regex pattern Uma expressão regular que é avaliada em relação ao valor do atributo de usuário selecionado como Parâmetro 1. Por exemplo, uma expressão regular para extrair o alias de usuário do endereço de email do usuário é representada como (?'domain'^.*?)(?i)(\@fabrikam\.com)$.
5 Add additional parameter Mais de um atributo de usuário pode ser usado para a transformação. Os valores dos atributos seriam mesclados com a saída da transformação regex. Há suporte para até mais cinco parâmetros.
6 Replacement pattern O padrão de substituição é o modelo de texto, que contém espaços reservados para o resultado regex. Todos os nomes de grupo precisam ser encapsulados dentro das chaves, como {group-name}. Digamos que a administração queira usar o alias de usuário com algum outro nome de domínio, por exemplo, xyz.com e mesclar o nome do país/região com ele. Nesse caso, o padrão de substituição seria {country}.{domain}@xyz.com, em que {country} é o valor do parâmetro de entrada e {domain} é a saída do grupo da avaliação de expressão regular. Num caso destes, o resultado esperado será US.swmal@xyz.com.

A imagem a seguir mostra um exemplo do segundo nível de transformação:

Captura de tela do segundo nível de transformação de declarações.

A tabela a seguir fornece informações sobre o segundo nível de transformações. As ações listadas na tabela correspondem aos rótulos na imagem anterior.

Ação Campo descrição
1 Transformation As transformações de declarações baseadas em Regex não se limitam à primeira transformação e podem ser usadas como a transformação de segundo nível. Qualquer outro método de transformação pode ser usado como a primeira transformação.
2 Parameter 1 Se o RegexReplace() for selecionado como uma transformação de segundo nível, a saída da transformação de primeiro nível é usada como uma entrada para a transformação de segundo nível. Para aplicar a transformação, a expressão de segundo nível da regex deve corresponder à saída da primeira transformação.
3 Regex pattern Padrão Regex é a expressão regular da transformação de segundo nível.
4 Parameter input Entradas de atributo de usuário para as transformações de segundo nível.
5 Parameter input Os administradores podem excluir o parâmetro de entrada selecionado se não precisar mais dele.
6 Replacement pattern O padrão de substituição é o modelo de texto, que contém espaços reservados para o nome do grupo de resultados regex, o nome do grupo de parâmetros de entrada e o valor de texto estático. Todos os nomes de grupo precisam ser encapsulados dentro das chaves, como {group-name}. Digamos que a administração queira usar o alias de usuário com algum outro nome de domínio, por exemplo, xyz.com e mesclar o nome do país/região com ele. Nesse caso, o padrão de substituição seria {country}.{domain}@xyz.com, em que {country} é o valor do parâmetro de entrada e {domain} é a saída do grupo da avaliação de expressão regular. Num caso destes, o resultado esperado será US.swmal@xyz.com.
7 Test transformation A transformação RegexReplace() será avaliada somente se o valor do atributo de usuário selecionado como Parâmetro 1 corresponder à expressão regular fornecida na caixa de texto Padrão Regex. Se eles não corresponderem, o valor de declaração padrão será adicionado ao token. Para validar a expressão regular em relação ao valor do parâmetro de entrada, uma experiência de teste está disponível na folha de transformação. Essa experiência de teste opera somente em valores fictícios. Quando mais parâmetros de entrada são usados, o nome do parâmetro é adicionado ao resultado do teste em vez do valor real. Para acessar a seção de teste, selecione Transformação de teste.

A imagem abaixo mostra um exemplo do teste de transformações:

Captura de tela do teste da transformação.

A tabela a seguir fornece informações sobre o teste de transformações. As ações listadas na tabela correspondem aos rótulos na imagem anterior.

Ação Campo descrição
1 Test transformation Selecione o botão fechar ou (X) para ocultar a seção de teste e renderize novamente o botão Testar transformação na folha.
2 Test regex input Aceita a entrada usada para a avaliação de teste de expressão regular. Caso a transformação de declarações baseada em regex esteja configurada como uma transformação de segundo nível, forneça um valor que seja a saída esperada da primeira transformação.
3 Run test Depois que a entrada regex de teste for fornecida e o padrão Regex, o Padrão de substituição e os Parâmetros de entrada forem configurados, a expressão poderá ser avaliada selecionando Executar teste.
4 Test transformation result Se a avaliação for bem-sucedida, uma saída da transformação de teste será renderizada em relação no rótulo Resultado de transformação de teste.
5 Remove transformation A transformação de segundo nível pode ser removida selecionando Remover transformação.
6 Specify output if no match Quando um valor de entrada regex é configurado em relação ao Parâmetro 1 que não corresponde à Expressão regular, a transformação é ignorada. Nesses casos, o atributo de usuário alternativo pode ser configurado, que é adicionado ao token para a declaração marcando Especificar saída se não houver correspondência.
7 Parameter 3 Se um atributo de usuário alternativo precisar ser retornado quando não houver correspondência e Especificar a saída se não houver nenhuma correspondência estiver marcado, um atributo de usuário alternativo poderá ser selecionado usando a lista suspensa. Essa lista suspensa está disponível em Parâmetro 3 (saída se não houver correspondência).
8 Summary Na parte inferior da folha, é exibido um resumo completo do formato que explica o significado da transformação em texto simples.
9 Add Depois que as definições de configuração da transformação forem verificadas, ela poderá ser salva em uma política de declarações selecionando Adicionar. Selecione Salvar na folha Gerenciar Declaração para salvar as alterações.

A transformação RegexReplace() também está disponível para as transformações de declarações de grupo.

Validações de transformação

Uma mensagem fornece mais informações quando as seguintes condições ocorrerem após selecionar Adicionar ou Executar teste:

  • Foram usados parâmetros de entrada com atributos de usuário duplicados.
  • Parâmetros de entrada não utilizados encontrados. Os parâmetros de entrada definidos devem ter o respectivo uso no texto de padrão de substituição.
  • A entrada de regex de teste fornecida não corresponde à expressão regular fornecida.
  • Nenhuma fonte foi encontrada para os grupos no padrão de substituição.

Emitir declarações com base em condições

Você pode especificar a origem de uma declaração com base no tipo de usuário e no grupo ao qual o usuário pertence.

O tipo de usuário pode ser:

  • Qualquer – todos os usuários têm permissão para acessar o aplicativo.
  • Membros: membro nativo do locatário
  • Todos os convidados: o usuário foi transferido de uma organização externa com ou sem o Microsoft Entra ID.
  • Convidados do Microsoft Entra: o usuário convidado pertence a outra organização usando o Microsoft Entra ID.
  • Convidados externos: o usuário convidado pertence a uma organização externa que não tem o Microsoft Entra ID.

Um cenário em que o tipo de usuário é útil é quando a origem de uma declaração é diferente para um convidado e um funcionário acessando um aplicativo. Você pode especificar que, se o usuário for um funcionário, o NameID será obtido do user.email. Se o usuário for um convidado, o NameID virá do user.extensionattribute1.

Para adicionar uma condição de declaração:

  1. Em Gerenciar declaração, expanda as Condições de declaração.
  2. Selecione o tipo de usuário.
  3. Selecione os grupos aos quais o usuário deve pertencer. Selecione até 50 grupos exclusivos em todas as declarações para um determinado aplicativo.
  4. Selecione a Origem em que a declaração vai recuperar o valor dela. Você pode selecionar um atributo de usuário na lista suspensa de atributo de origem ou aplicar uma transformação ao atributo de usuário antes de emiti-lo como uma declaração.

A ordem na qual você adiciona as condições é importante. O Microsoft Entra primeiro avalia todas as condições com a origem Attribute e depois avalia todas as condições com a origem Transformation para decidir qual valor emitir na declaração. O Microsoft Entra ID avalia as condições com a mesma origem de cima para baixo. A declaração emite o último valor que corresponde à expressão na declaração. As transformações como IsNotEmpty e Contains atuam como restrições.

Por exemplo, Brenda Fernandes é um usuário convidado no locatário da Contoso. Brenda pertence a outra organização que também usa o Microsoft Entra ID. Dada a configuração a seguir para o aplicativo Fabrikam, quando Brenda tenta entrar no Fabrikam, a plataforma de identidade da Microsoft avalia as condições.

Primeiro, a plataforma de identidade da Microsoft verifica se o tipo de usuário da Brenda é Todos os convidados. Como o tipo é Todos os convidados, a plataforma de identidade da Microsoft atribui a origem da declaração a user.extensionattribute1. Em segundo lugar, a plataforma de identidade da Microsoft verifica se o tipo de usuário da Brenda é Convidados do Microsoft Entra. Como o tipo é Todos os convidados, a plataforma de identidade da Microsoft atribui a origem da declaração a user.mail. Por fim, a declaração é emitida com um valor de user.mail para Brenda.

Como outro exemplo, pense em quando Brenda Fernandes tenta entrar e a configuração a seguir é usada. O Microsoft Entra primeiro avalia todas as condições com a origem Attribute. A origem da declaração é user.mail quando o tipo de usuário da Brenda for Convidados do Microsoft Entra. Em seguida, o Microsoft Entra ID avalia as transformações. Devido ao fato de a Brenda ser um convidado, user.extensionattribute1 será a nova origem da declaração. Como Brenda está em Convidados do Microsoft Entra, user.othermail é a nova fonte da declaração. Por fim, a declaração é emitida com um valor de user.othermail para Brenda.

Como exemplo final, considere o que acontecerá se a Brenda não tiver um user.othermail configurado ou se ele estiver vazio. A declaração retorna a user.extensionattribute1 ignorando a entrada da condição em ambos os casos.

Considerações sobre segurança

Os aplicativos que recebem tokens dependem de valores de declaração que não podem ser adulterados. Quando você modifica o conteúdo do token por meio da personalização de declarações, essas suposições podem não estar mais corretas. Os aplicativos devem reconhecer explicitamente que os tokens foram modificados para se protegerem das personalizações criadas por atores mal-intencionados. Proteja-se contra personalizações inadequadas de uma das seguintes maneiras:

Sem isso, o Microsoft Entra ID retorna um código de erro AADSTS50146.

Configurar uma chave de assinatura personalizada

Quanto aos aplicativos multilocatários, deve-se usar uma chave de assinatura personalizada. Não defina acceptMappedClaims no manifesto do aplicativo. Ao configurar um aplicativo no portal do Azure, você obtém um objeto de registro de aplicativo e uma entidade de serviço no seu locatário. Esse aplicativo está usando a chave de credenciais global do Azure, que não pode ser usada para personalizar declarações em tokens. Para obter declarações personalizadas em tokens, crie uma chave de credenciais personalizada com base em um certificado e adicione-a à entidade de serviço. Para fins de teste, você pode usar um certificado autoassinado. Depois de configurar a chave de assinatura personalizada, o código do seu aplicativo precisará validar a chave de assinatura do token.

Adicione as seguintes informações à entidade de serviço:

Extraia a chave privada e pública codificada em Base 64 da exportação de arquivo PFX do seu certificado. Verifique se a keyId para a keyCredential usada para "Assinar" corresponde à keyId da passwordCredential. É possível gerar o customkeyIdentifier obtendo o hash da impressão digital do certificado.

Solicitação

Observação

Primeiro desabilite qualquer configuração de bloqueio da entidade de serviço em aplicativos recém-criados da folha de registros de aplicativo do centro de administração do Microsoft Entra antes de tentar fazer um PATCH na entidade de serviço, resultando em uma solicitação 400 incorreta.

O exemplo a seguir mostra o formato da solicitação HTTP PATCH para adicionar uma chave de assinatura personalizada a uma entidade de serviço. O valor de "chave" na propriedade keyCredentials é reduzido para facilitar a leitura. O valor é codificado em Base 64. Para a chave privada, o uso da propriedade é Sign. Para a chave pública, o uso da propriedade é Verify.

PATCH https://graph.microsoft.com/v1.0/servicePrincipals/aaaaaaaa-bbbb-cccc-1111-222222222222

Content-type: servicePrincipals/json
Authorization: Bearer {token}

{
    "keyCredentials":[
        {
            "customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=", 
            "endDateTime": "2021-04-22T22:10:13Z",
            "keyId": "aaaaaaaa-0b0b-1c1c-2d2d-333333333333",
            "startDateTime": "2020-04-22T21:50:13Z",
            "type": "X509CertAndPassword",
            "usage": "Sign",
            "key":"cD2eF3gH4iJ5kL6mN7-oP8qR9sT==",
            "displayName": "CN=contoso"
        },
        {
            "customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=",
            "endDateTime": "2021-04-22T22:10:13Z",
            "keyId": "bbbbbbbb-1c1c-2d2d-3e3e-444444444444",
            "startDateTime": "2020-04-22T21:50:13Z",
            "type": "AsymmetricX509Cert",
            "usage": "Verify",
            "key": "cD2eF3gH4iJ5kL6mN7-oP8qR9sT==",
            "displayName": "CN=contoso"
        }

    ],
    "passwordCredentials": [
        {
            "customKeyIdentifier": "aB1cD2eF3gH4iJ5kL6-mN7oP8qR=",
            "keyId": "cccccccc-2d2d-3e3e-4f4f-555555555555",
            "endDateTime": "2022-01-27T19:40:33Z",
            "startDateTime": "2020-04-20T19:40:33Z",
            "secretText": "mypassword"
        }
    ]
}

Configurar uma chave de assinatura personalizada usando o PowerShell

Use o PowerShell para criar uma instância de um Aplicativo Cliente Público da MSAL e usar o fluxo de Concessão de Código de Autorização para obter um token de acesso de permissão delegada para o Microsoft Graph. Use o token de acesso para chamar o Microsoft Graph e configurar uma chave de assinatura personalizada para a entidade de serviço. Depois de configurar a chave de assinatura personalizada, o código do seu aplicativo precisará validar a chave de assinatura do token.

Para executar esse script, você precisa:

  • A ID do objeto da entidade de serviço do seu aplicativo, encontrado na folha Visão geral da entrada do seu aplicativo nos Aplicativos empresariais no portal do Azure.
  • De um registro de aplicativo para conectar um usuário e obter um token de acesso para chamar o Microsoft Graph. Obtenha a ID do aplicativo (cliente) desse aplicativo na folha Visão Geral da entrada do aplicativo em Registros de aplicativo no portal do Azure. O registro de aplicativo ter a seguinte configuração:
    • Um URI de redirecionamento de "http://localhost" listado na configuração da plataforma Aplicativos móveis e de desktop.
    • Em API permissions, o Microsoft Graph delegou permissões Application.ReadWrite.All e User.Read (certifique-se de conceder o consentimento do administrador a essas permissões).
  • Um usuário que faz logon para obter o token de acesso do Microsoft Graph. O usuário deve ser uma das seguintes funções administrativas do Microsoft Entra (exigidas para atualizar a entidade de serviço):
    • Administrador de Aplicativos de Nuvem
    • Administrador de aplicativos
  • Um certificado para configurar como uma chave de assinatura personalizada para nosso aplicativo. Você pode criar um certificado autoassinado ou obter um de sua autoridade de certificação confiável. Os seguintes componentes de certificado são usados no script:
    • chave pública (normalmente um arquivo .cer)
    • chave privada no formato PKCS nº 12 (em arquivo .pfx)
    • senha para a chave privada (arquivo .pfx)

Importante

A chave privada deve estar no formato PKCS#12, pois o Microsoft Entra ID não é compatível com outros tipos de formato. Usar o formato errado pode resultar no erro "Certificado inválido: o valor da chave é um certificado inválido" ao usar o Microsoft Graph para PATCH a entidade de serviço com um keyCredentials contendo as informações do certificado.

$fqdn="fourthcoffeetest.onmicrosoft.com" # this is used for the 'issued to' and 'issued by' field of the certificate
$pwd="mypassword" # password for exporting the certificate private key
$location="C:\\temp" # path to folder where both the pfx and cer file will be written to

# Create a self-signed cert
$cert = New-SelfSignedCertificate -certstorelocation cert:\currentuser\my -DnsName $fqdn
$pwdSecure = ConvertTo-SecureString -String $pwd -Force -AsPlainText
$path = 'cert:\currentuser\my\' + $cert.Thumbprint
$cerFile = $location + "\\" + $fqdn + ".cer"
$pfxFile = $location + "\\" + $fqdn + ".pfx"
 
# Export the public and private keys
Export-PfxCertificate -cert $path -FilePath $pfxFile -Password $pwdSecure
Export-Certificate -cert $path -FilePath $cerFile

$ClientID = "<app-id>"
$loginURL       = "https://login.microsoftonline.com"
$tenantdomain   = "fourthcoffeetest.onmicrosoft.com"
$redirectURL = "http://localhost" # this reply URL is needed for PowerShell Core 
[string[]] $Scopes = "https://graph.microsoft.com/.default"
$pfxpath = $pfxFile # path to pfx file
$cerpath = $cerFile # path to cer file
$SPOID = "<service-principal-id>"
$graphuri = "https://graph.microsoft.com/v1.0/serviceprincipals/$SPOID"
$password = $pwd  # password for the pfx file
 
 
# choose the correct folder name for MSAL based on PowerShell version 5.1 (.Net) or PowerShell Core (.Net Core)
 
if ($PSVersionTable.PSVersion.Major -gt 5)
    { 
        $core = $true
        $foldername =  "netcoreapp2.1"
    }
else
    { 
        $core = $false
        $foldername = "net45"
    }
 
# Load the MSAL/microsoft.identity/client assembly -- needed once per PowerShell session
[System.Reflection.Assembly]::LoadFrom((Get-ChildItem C:/Users/<username>/.nuget/packages/microsoft.identity.client/4.32.1/lib/$foldername/Microsoft.Identity.Client.dll).fullname) | out-null
  
$global:app = $null
  
$ClientApplicationBuilder = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($ClientID)
[void]$ClientApplicationBuilder.WithAuthority($("$loginURL/$tenantdomain"))
[void]$ClientApplicationBuilder.WithRedirectUri($redirectURL)
 
$global:app = $ClientApplicationBuilder.Build()
  
Function Get-GraphAccessTokenFromMSAL {
    [Microsoft.Identity.Client.AuthenticationResult] $authResult  = $null
    $AquireTokenParameters = $global:app.AcquireTokenInteractive($Scopes)
    [IntPtr] $ParentWindow = [System.Diagnostics.Process]::GetCurrentProcess().MainWindowHandle
    if ($ParentWindow)
    {
        [void]$AquireTokenParameters.WithParentActivityOrWindow($ParentWindow)
    }
    try {
        $authResult = $AquireTokenParameters.ExecuteAsync().GetAwaiter().GetResult()
    }
    catch {
        $ErrorMessage = $_.Exception.Message
        Write-Host $ErrorMessage
    }
     
    return $authResult
}
  
$myvar = Get-GraphAccessTokenFromMSAL
if ($myvar)
{
    $GraphAccessToken = $myvar.AccessToken
    Write-Host "Access Token: " $myvar.AccessToken
    #$GraphAccessToken = "eyJ0eXAiOiJKV1QiL ... iPxstltKQ"
    
 
    #  this is for PowerShell Core
    $Secure_String_Pwd = ConvertTo-SecureString $password -AsPlainText -Force
 
    # reading certificate files and creating Certificate Object
    if ($core)
    {
        $pfx_cert = get-content $pfxpath -AsByteStream -Raw
        $cer_cert = get-content $cerpath -AsByteStream -Raw
        $cert = Get-PfxCertificate -FilePath $pfxpath -Password $Secure_String_Pwd
    }
    else
    {
        $pfx_cert = get-content $pfxpath -Encoding Byte
        $cer_cert = get-content $cerpath -Encoding Byte
        # Write-Host "Enter password for the pfx file..."
        # calling Get-PfxCertificate in PowerShell 5.1 prompts for password
        # $cert = Get-PfxCertificate -FilePath $pfxpath
        $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($pfxpath, $password)
    }
 
    # base 64 encode the private key and public key
    $base64pfx = [System.Convert]::ToBase64String($pfx_cert)
    $base64cer = [System.Convert]::ToBase64String($cer_cert)
 
    # getting id for the keyCredential object
    $guid1 = New-Guid
    $guid2 = New-Guid
 
    # get the custom key identifier from the certificate thumbprint:
    $hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256')
    $hash = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($cert.Thumbprint))
    $customKeyIdentifier = [System.Convert]::ToBase64String($hash)
 
    # get end date and start date for our keycredentials
    $endDateTime = ($cert.NotAfter).ToUniversalTime().ToString( "yyyy-MM-ddTHH:mm:ssZ" )
    $startDateTime = ($cert.NotBefore).ToUniversalTime().ToString( "yyyy-MM-ddTHH:mm:ssZ" )
 
    # building our json payload
    $object = [ordered]@{    
    keyCredentials = @(       
         [ordered]@{            
            customKeyIdentifier = $customKeyIdentifier
            endDateTime = $endDateTime
            keyId = $guid1
            startDateTime = $startDateTime 
            type = "X509CertAndPassword"
            usage = "Sign"
            key = $base64pfx
            displayName = "CN=fourthcoffeetest" 
        },
        [ordered]@{            
            customKeyIdentifier = $customKeyIdentifier
            endDateTime = $endDateTime
            keyId = $guid2
            startDateTime = $startDateTime 
            type = "AsymmetricX509Cert"
            usage = "Verify"
            key = $base64cer
            displayName = "CN=fourthcoffeetest"   
        }
        )  
    passwordCredentials = @(
        [ordered]@{
            customKeyIdentifier = $customKeyIdentifier
            keyId = $guid1           
            endDateTime = $endDateTime
            startDateTime = $startDateTime
            secretText = $password
        }
    )
    }
 
    $json = $object | ConvertTo-Json -Depth 99
    Write-Host "JSON Payload:"
    Write-Output $json
 
    # Request Header
    $Header = @{}
    $Header.Add("Authorization","Bearer $($GraphAccessToken)")
    $Header.Add("Content-Type","application/json")
 
    try 
    {
        Invoke-RestMethod -Uri $graphuri -Method "PATCH" -Headers $Header -Body $json
    } 
    catch 
    {
        # Dig into the exception to get the Response details.
        # Note that value__ is not a typo.
        Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ 
        Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
    }
 
    Write-Host "Complete Request"
}
else
{
    Write-Host "Fail to get Access Token"
}

Validar chave de assinatura de token

Os aplicativos que têm o mapeamento de declarações habilitado devem validar suas chaves de assinatura de token acrescentando appid={client_id} às suas solicitações de metadados do OpenID Connect. O exemplo a seguir mostra o formato do documento de metadados OpenID Connect que você deve usar:

https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration?appid={client-id}

Atualizar o manifesto do aplicativo

Para aplicativos de locatário único, você pode definir a propriedade acceptMappedClaims como true no manifesto do aplicativo. Conforme documentado no apiApplication tipo de serviço. A definição da propriedade permite que um aplicativo use o mapeamento de declarações sem especificar uma chave de assinatura personalizada.

Aviso

Não defina a propriedade acceptMappedClaims como verdadeiro para aplicativos multilocatários, o que pode permitir que atores mal-intencionados criem políticas de mapeamento de declarações para seu aplicativo.

O público do token solicitado é necessário para usar um nome de domínio verificado do seu locatário do Microsoft Entra, o que significa que você deve definir Application ID URI.(representado por identifierUris no manifesto do aplicativo), por exemplo, como https://contoso.com/my-api ou (simplesmente usando o nome do locatário padrão) https://contoso.onmicrosoft.com/my-api.

Se você não estiver usando um domínio verificado, o Microsoft Entra ID retornará um código de erro AADSTS501461 com a mensagem "_AcceptMappedClaims só tem suporte para um público de token correspondente ao GUID do aplicativo ou um público nos domínios verificados do locatário. Altere o identificador de recurso ou use uma chave de assinatura específica do aplicativo."

Opções de declarações avançadas

Configurar opções avançadas de declarações para que os aplicativos OIDC exponham a mesma declaração que os tokens SAML. Também para os aplicativos que pretendem usar a mesma declaração tanto para tokens de resposta SAML2.0 quanto OIDC.

Configure as opções de declaração avançadas marcando a caixa de seleção em Opções de Declarações Avançadas na folha Gerenciar declarações.