Personalizar declarações emitidas no token da Web JSON (JWT) para aplicativos corporativos
A plataforma de identidade da Microsoft suporta 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 por meio da plataforma de identidade da Microsoft usando o protocolo OIDC, ele envia um token para o aplicativo. O aplicativo valida e usa o token para entrar o usuário em vez de solicitar um nome de usuário e senha.
Esses tokens da Web JSON (JWT) usados por aplicativos OIDC e OAuth contêm partes de informações sobre o usuário conhecidas como declarações. Uma declaração é uma informação que um provedor de identidade declara sobre um usuário dentro do token que ele 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.
Ver ou editar declarações
Gorjeta
As etapas neste artigo podem variar ligeiramente com base no portal a partir do qual você começou.
As declarações JWT opcionais podem ser configuradas no registro do aplicativo original, mas também podem ser configuradas no aplicativo empresarial. Para visualizar ou editar as declarações emitidas no JWT para o aplicativo:
- Entre no centro de administração do Microsoft Entra como pelo menos um administrador de aplicativos na nuvem.
- Navegue até Identity>Applications>Enterprise applications>Todos os aplicativos.
- Selecione o aplicativo, selecione Logon único no menu à esquerda e, em seguida, selecione Editar na seção Atributos & Declarações .
Um aplicativo pode precisar de personalização de declarações por vários motivos. Por exemplo, quando um aplicativo requer um conjunto diferente de URIs ou valores de declaração. Usando a seção Atributos & Declarações , você pode adicionar ou remover uma declaração para seu aplicativo. Você também pode criar uma declaração personalizada que é específica para um aplicativo com base no caso de uso.
As etapas a seguir descrevem como atribuir um valor constante:
- Selecione a declaração que deseja modificar.
- Insira o valor constante sem aspas no atributo Source de acordo com sua organização e selecione Salvar.
A visão geral de Atributos exibe o valor constante.
Transformações de sinistros especiais
Você pode usar as seguintes funções de transformações de declarações especiais.
Function | Description |
---|---|
ExtractMailPrefix() | Remove o sufixo de domínio do endereço de e-mail ou do nome principal do usuário. Esta função extrai apenas a primeira parte do nome de utilizador. 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 do aplicativo:
- Em Atributos do Usuário & Declarações, selecione Adicionar nova declaração para abrir a página Gerenciar declarações do usuário.
- Insira o nome das declarações. O valor não precisa seguir estritamente um padrão de URI. Se você precisar de um padrão de URI, poderá colocá-lo no campo Namespace .
- Selecione a Origem onde a declaração vai recuperar seu valor. Você pode selecionar um atributo de usuário na lista suspensa de atributos 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:
- Em Gerenciar declaração, selecione Transformação como a fonte da declaração para abrir a página Gerenciar transformação .
- Selecione a função na lista suspensa de transformação. Dependendo da função selecionada, forneça parâmetros e um valor constante para avaliar na transformação.
- Tratar origem como multivalorado indica se a transformação é aplicada a todos os valores ou apenas ao primeiro. Por padrão, o primeiro elemento em uma declaração de vários valores é aplicado às transformações. Quando você marca essa caixa, ela garante que ela seja aplicada a todos. Essa caixa de seleção só está habilitada para atributos de vários valores. Por exemplo,
user.proxyaddresses
. - 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 e-mail do
user.mail
. Em seguida, coloque a string em maiúsculas.
Você pode usar as seguintes funções para transformar declarações.
Function | Description |
---|---|
ExtractMailPrefix() | Remove o sufixo de domínio do endereço de e-mail ou do nome principal do usuário. Esta função extrai apenas a primeira parte do nome de utilizador. Por exemplo, joe_smith em vez de joe_smith@contoso.com . |
Aderir() | Cria um novo 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 do domínio da entrada antes de juntá-la com o separador e o parâmetro selecionado. Por exemplo, se a entrada da transformação é joe_smith@contoso.com e o separador é @ e o parâmetro é fabrikam.com , essa combinação de entrada resulta em joe_smith@fabrikam.com . |
ToMinúsculas() | Converte os caracteres do atributo selecionado em caracteres minúsculos. |
ToMaiúsculas() | Converte os caracteres do atributo selecionado em caracteres maiúsculos. |
Contém() | Gera um atributo ou constante se a entrada corresponder ao valor especificado. Caso contrário, você pode especificar outra saída se não houver correspondência. Por exemplo, se você quiser emitir uma declaração em que o valor é o endereço de e-mail do usuário se ele contiver o domínio @contoso.com , caso contrário, você deseja gerar o nome principal do usuário. 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 |
Fim() | Gera um atributo ou constante se a entrada terminar com o valor especificado. Caso contrário, você pode especificar outra saída se não houver correspondência. Por exemplo, se você quiser emitir uma declaração em que o valor é o ID de funcionário do usuário se o ID do funcionário terminar com 000 , caso contrário, você deseja gerar um atributo de extensão. 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 |
Começar com() | Gera um atributo ou constante se a entrada começar com o valor especificado. Caso contrário, você pode especificar outra saída se não houver correspondência. Por exemplo, se você quiser emitir uma declaração em que o valor é o ID de funcionário do usuário se o país/região começar com US , caso contrário, você deseja gerar um atributo de extensão. Para executar essa função, configure os seguintes valores:Parâmetro 1 (entrada): user.country Valor: "US" Parâmetro 2 (saída): user.employeeid Parâmetro 3 (saída se não houver correspondência): user.extensionattribute1 |
Extract() - Após correspondência | Retorna a substring depois que ela corresponde ao valor especificado. Por exemplo, se o valor da entrada for Finance_BSimon , o valor correspondente for Finance_ , então a saída da declaração será BSimon . |
Extract() - Antes de combinar | Retorna a substring até que ela corresponda ao valor especificado. Por exemplo, se o valor da entrada for BSimon_US , o valor correspondente for _US , então a saída da declaração será BSimon . |
Extract() - Entre correspondência | Retorna a substring até que ela corresponda ao valor especificado. Por exemplo, se o valor da entrada for Finance_BSimon_US , o primeiro valor correspondente for Finance_ , o segundo valor correspondente for _US , então a saída da declaração será BSimon . |
ExtractAlpha() - Prefixo | Retorna a parte alfabética do prefixo da cadeia de caracteres. Por exemplo, se o valor da entrada for BSimon_123 , ela retornará BSimon . |
ExtractAlpha() - Sufixo | Retorna a parte alfabética do sufixo da cadeia de caracteres. Por exemplo, se o valor da entrada for 123_Simon , ela retornará Simon . |
ExtractNumeric() - Prefixo | Retorna a parte numérica do prefixo da cadeia de caracteres. Por exemplo, se o valor da entrada for 123_BSimon , ela retornará 123 . |
ExtractNumeric() - Sufixo | Retorna a parte numérica do sufixo da cadeia de caracteres. Por exemplo, se o valor da entrada for BSimon_123 , ela 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 se o ID do funcionário de um determinado usuário estiver vazio. 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 se o ID do funcionário de um determinado usuário não estiver vazio. 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, começando no caractere na posição especificada, e retorna o número especificado de caracteres. SourceClaim - A fonte de declaração da transformação que deve ser executada. StartIndex - A posição de caractere inicial baseada em zero de uma substring neste caso. 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, começando no caractere na posição especificada, e retorna o restante da declaração do índice inicial especificado. SourceClaim - A fonte da declaração da transformação. StartIndex - A posição de caractere inicial baseada em zero de uma substring neste caso. 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 multivalorado - 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 aponta 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 de aplicativo SaaS.
Transformação de sinistros baseada em Regex
A imagem a seguir mostra um exemplo 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 da imagem anterior. Selecione Editar para abrir a folha de transformação de declarações.
Ação | Campo | Description |
---|---|---|
5 | Transformation |
Selecione a opção RegexReplace() nas opções de Transformação para usar o método de transformação de declarações baseado em regex para transformação de declarações. |
2 | Parameter 1 |
A entrada para a transformação da expressão regular. Por exemplo, user.mail que tem um endereço de e-mail de 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 oferecer suporte a vários valores e o usuário quiser usar vários valores para a transformação, ele precisará selecionar Tratar origem como multivalorado. Se selecionado, todos os valores são usados para a correspondência de regex, caso contrário, apenas o primeiro valor é 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 do usuário do endereço de e-mail 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 então mesclados com a saída de transformação regex. Até mais cinco parâmetros são suportados. |
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 devem ser encapsulados dentro das chaves como {group-name}. Digamos que a administração queira usar o alias do usuário com algum outro nome de domínio, por exemplo xyz.com , e mesclar o nome do país com ele. Neste caso, o padrão de substituição seria {country}.{domain}@xyz.com , onde {country} é o valor do parâmetro de entrada e {domain} é a saída do grupo da avaliação de expressão regular. Nesse caso, o resultado esperado é US.swmal@xyz.com . |
A imagem a seguir mostra um exemplo do segundo nível de transformação:
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 da imagem anterior.
Ação | Campo | Description |
---|---|---|
5 | Transformation |
As transformações de declarações baseadas em Regex não se limitam à primeira transformação e também 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 RegexReplace() for selecionado como uma transformação de segundo nível, a saída da transformação de primeiro nível será usada como uma entrada para a transformação de segundo nível. Para aplicar a transformação, a expressão regex de segundo nível deve corresponder à saída da primeira transformação. |
3 | Regex pattern |
O padrão Regex é a expressão regular para a transformação de segundo nível. |
4 | Parameter input |
Entradas de atributos 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 precisarem 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 devem ser embrulhados dentro das chaves encaracoladas, como {group-name} . Digamos que a administração queira usar o alias do usuário com algum outro nome de domínio, por exemplo xyz.com , e mesclar o nome do país com ele. Neste caso, o padrão de substituição seria {country}.{domain}@xyz.com , onde {country} é o valor do parâmetro de entrada e {domain} é a saída do grupo da avaliação de expressão regular. Nesse caso, o resultado esperado é US.swmal@xyz.com . |
7 | Test transformation |
A transformação RegexReplace() é avaliada somente se o valor do atributo de usuário selecionado para o Parâmetro 1 corresponder à expressão regular fornecida na caixa de texto do padrão Regex. Se eles não corresponderem, o valor da 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. Esta experiência de teste opera apenas com 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 a seguir mostra um exemplo de teste das transformações:
A tabela a seguir fornece informações sobre como testar as transformações. As ações listadas na tabela correspondem aos rótulos da imagem anterior.
Ação | Campo | Description |
---|---|---|
5 | Test transformation |
Selecione o botão fechar ou (X) para ocultar a seção de teste e renderizar novamente o botão de transformação de teste novamente na folha. |
2 | Test regex input |
Aceita a entrada usada para a avaliação do teste de expressão regular. Caso a transformação de declarações baseada em regex seja 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 é fornecida e o padrão Regex, o padrão de substituição e os parâmetros de entrada são configurados, a expressão pode 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á processada em relação ao rótulo de resultado da 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 nenhuma correspondência. |
7 | Parameter 3 |
Se um atributo de usuário alternativo precisar ser retornado quando não houver correspondência e Especificar saída se nenhuma correspondência estiver marcada, um atributo de usuário alternativo poderá ser selecionado usando a lista suspensa. Esta lista suspensa está disponível em relação ao 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 para a 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 ocorrem depois de selecionar Adicionar ou Executar teste:
- Foram utilizados 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 respetivo uso no texto do padrão de substituição.
- A entrada de regex de teste fornecida não corresponde à expressão regular fornecida.
- Não são encontradas fontes para os grupos no padrão de substituição.
Emita reclamações com base em condições
Pode especificar a origem de uma afirmação com base no tipo de utilizador e no grupo a que o utilizador pertence.
O tipo de usuário pode ser:
- Qualquer - Todos os utilizadores têm permissão para aceder à aplicação.
- Membros: Membro nativo do inquilino
- Todos os convidados: Usuário movido de uma organização externa com ou sem ID do Microsoft Entra.
- Convidados do Microsoft Entra: o usuário convidado pertence a outra organização que usa a ID do Microsoft Entra.
- 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 que acessam um aplicativo. Você pode especificar que, se o usuário for um funcionário, obtenha o NameID de user.email. Se o usuário for um convidado, o NameID virá de user.extensionattribute1.
Para adicionar uma condição de reivindicação:
- Em Gerenciar declaração, expanda as Condições de declaração.
- Selecione o tipo de usuário.
- Selecione o(s) grupo(s) ao qual o usuário deve pertencer. Você pode selecionar até 50 grupos exclusivos em todas as declarações para um determinado aplicativo.
- Selecione a Origem onde a declaração vai recuperar seu valor. Você pode selecionar um atributo de usuário na lista suspensa de atributos de origem ou aplicar uma transformação ao atributo de usuário antes de emiti-lo como uma declaração.
A ordem em que você adiciona as condições é importante. O Microsoft Entra primeiro avalia todas as condições com a fonte Attribute
e, em seguida, avalia todas as condições com a fonte Transformation
para decidir qual valor emitir na declaração. O Microsoft Entra ID avalia as condições com a mesma fonte de cima para baixo. A declaração emite o último valor que corresponde à expressão na declaração. Transformações como IsNotEmpty
e Contains
agem como restrições.
Por exemplo, Brenda Fernandes é um usuário convidado no locatário da Contoso. Britta pertence a outra organização que também usa o Microsoft Entra ID. Dada a seguinte configuração para o aplicativo Fabrikam, quando Britta tenta entrar na 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 de Britta é Todos os convidados. Como o tipo é Todos os convidados, a plataforma de identidade da Microsoft atribui a origem da declaração ao user.extensionattribute1
. Em segundo lugar, a plataforma de identidade da Microsoft verifica se o tipo de usuário de Britta é convidado do Microsoft Entra. Como o tipo é Todos os convidados, a plataforma de identidade da Microsoft atribui a origem da declaração ao user.mail
. Por fim, a reivindicação é emitida com um valor de user.mail
para Britta.
Como outro exemplo, considere quando Brenda Fernandes tenta entrar usando a seguinte configuração. O Microsoft Entra primeiro avalia todas as condições com o código-fonte Attribute
. A fonte para a afirmação é user.mail
quando o tipo de usuário de Britta é convidado do Microsoft Entra. Em seguida, o Microsoft Entra ID avalia as transformações. Porque Britta é um convidado, user.extensionattribute1
é a nova fonte para a afirmação. Porque Britta está em convidados Microsoft Entra, user.othermail
é a nova fonte para esta afirmação. Por fim, a reivindicação é emitida com um valor de user.othermail
para Britta.
Como exemplo final, considere o que acontece se a Britta não user.othermail
tiver configuração ou estiver vazia. A alegação recai sobre ignorar user.extensionattribute1
a entrada da condição em ambos os casos.
Considerações de 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 de personalizações criadas por agentes mal-intencionados. Proteja-se de personalizações inadequadas de uma das seguintes maneiras:
- Configurar uma chave de assinatura personalizada
- Atualize o manifesto do aplicativo para aceitar declarações mapeadas.
Sem isso, o Microsoft Entra ID retorna um código de erro AADSTS50146.
Configurar uma chave de assinatura personalizada
Para aplicativos multilocatário, uma chave de assinatura personalizada deve ser usada. 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 em seu locatário. Esse aplicativo está usando a chave de entrada 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 entrada personalizada a partir de 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 aplicativo precisa validar a chave de assinatura de token.
Adicione as seguintes informações à entidade de serviço:
- Chave privada (como uma credencial de chave)
- Senha (como uma credencial de senha)
- Chave pública (como uma credencial de chave)
Extraia a chave pública e privada base-64 codificada da exportação do arquivo PFX do seu certificado. Certifique-se de que o keyId
para o keyCredential
usado para "Sign" corresponde ao do .passwordCredential
keyId
Você pode gerar o obtendo o customkeyIdentifier
hash da impressão digital do certificado.
Pedir
Nota
Primeiro, desative qualquer configuração de bloqueio da entidade de serviço em aplicativos recém-criados na folha de registros de aplicativos do centro de administração do Microsoft Entra antes de tentar fazer um PATCH na entidade de serviço, resultando em uma solicitação incorreta de 400.
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 "chave" na keyCredentials
propriedade é encurtado para legibilidade. 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 instanciar um Aplicativo Cliente Público MSAL e use o fluxo de Concessão de Código de Autorização para obter um token de acesso de permissão delegado 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 aplicativo precisa validar a chave de assinatura de token.
Para executar esse script, você precisa:
- A ID do objeto da entidade de serviço do seu aplicativo, encontrada na folha Visão geral da entrada do seu aplicativo em Aplicativos Empresariais no portal do Azure.
- Um registro de aplicativo para entrar em um usuário e obter um token de acesso para chamar o Microsoft Graph. Obtenha a ID do aplicativo (cliente) deste aplicativo na folha Visão geral da entrada do aplicativo em Registros de aplicativo no portal do Azure. O registro do aplicativo deve ter a seguinte configuração:
- Um URI de redirecionamento de "http://localhost" listado na configuração da plataforma de aplicativos móveis e desktop.
- Em permissões de API, o Microsoft Graph delegou permissões Application.ReadWrite.All e User.Read (certifique-se de conceder consentimento de administrador para essas permissões).
- Um utilizador que inicia sessão para obter o token de acesso do Microsoft Graph. O usuário deve ser uma das seguintes funções administrativas do Microsoft Entra (necessárias para atualizar a entidade de serviço):
- Administrador de Aplicações na Cloud
- Administrador da Aplicação
- Um certificado para configurar como uma chave de assinatura personalizada para o 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 ficheiro .cer )
- chave privada no formato PKCS#12 (no arquivo .pfx )
- Senha para a chave privada (arquivo .pfx )
Importante
A chave privada deve estar no formato PKCS#12, uma vez que o Microsoft Entra ID não suporta outros tipos de formato. Usar o formato errado pode resultar no erro "Certificado inválido: o valor da chave é certificado inválido" ao usar o Microsoft Graph para CORRIGIR a entidade de serviço com um keyCredentials
contendo as informações do certificado.
##########################################################
# Replace the variables below with the appropriate values
$fqdn="yourDomainHere" # This is used for the 'issued to' and 'issued by' field of the certificate
$pwd="password" # password for exporting the certificate private key
$tenantId = "aaaabbbb-0000-cccc-1111-dddd2222eeee" # Replace with your Tenant ID
$appObjId = "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb" # Replace with the Object ID of the App Registration
##########################################################
# 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
$location="C:\\temp" # path to folder where both the pfx and cer file will be written to
$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
$pfxpath = $pfxFile # path to pfx file
$cerpath = $cerFile # path to cer file
$password = $pwd # password for the pfx file
# Check PowerShell version (minimum 5.1) (.Net) or PowerShell Core (.Net Core) and read the certificate file accordingly
if ($PSVersionTable.PSVersion.Major -gt 5)
{
$core = $true
}
else
{
$core = $false
}
# 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
# calling Get-PfxCertificate in PowerShell 5.1 prompts for password - using alternative method
$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 = "AsymmetricX509Cert"
usage = "Sign"
key = $base64pfx
displayName = "CN=$fqdn"
},
[ordered]@{
customKeyIdentifier = $customKeyIdentifier
endDateTime = $endDateTime
keyId = $guid2
startDateTime = $startDateTime
type = "AsymmetricX509Cert"
usage = "Verify"
key = $base64cer
displayName = "CN=$fqdn"
}
)
passwordCredentials = @(
[ordered]@{
customKeyIdentifier = $customKeyIdentifier
displayName = "CN=$fqdn"
keyId = $guid1
endDateTime = $endDateTime
startDateTime = $startDateTime
secretText = $password
hint = $null
}
)
}
Connect-MgGraph -tenantId $tenantId -Scopes Application.ReadWrite.All
$graphuri = "https://graph.microsoft.com/v1.0/applications/$appObjId"
Invoke-MgGraphRequest -Method PATCH -Uri $graphuri -Body $object
$json = $object | ConvertTo-Json -Depth 99
Write-Host "JSON Payload:"
Write-Output $json
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 anexando às solicitações de appid={client_id}
metadados do OpenID Connect. O exemplo a seguir mostra o formato do documento de metadados do 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 acceptMappedClaims
propriedade como true
no manifesto do aplicativo. Conforme documentado no tipo de apiApplication
recurso. 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 true para aplicativos multilocatário, o que pode permitir que atores mal-intencionados criem políticas de mapeamento de declarações para seu aplicativo.
O público de 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 o Application ID URI
(representado pelo no manifesto identifierUris
do aplicativo), por exemplo, como https://contoso.com/my-api
ou (simplesmente usando o nome de locatário padrão). https://contoso.onmicrosoft.com/my-api
Se você não estiver usando um domínio verificado, a ID do Microsoft Entra retornará um AADSTS501461
código de erro com a mensagem "_AcceptMappedClaims só é suportada para um público de token correspondente ao GUID do aplicativo ou um público dentro dos domínios verificados do locatário. Altere o identificador de recurso ou use uma chave de assinatura específica do aplicativo."
Opções avançadas de reclamações
Configure opções avançadas de declarações para aplicativos OIDC para expor a mesma declaração que os tokens SAML. Também para aplicativos que pretendem usar a mesma declaração para tokens de resposta SAML2.0 e OIDC.
Configure as opções avançadas de declaração marcando a caixa em Opções avançadas de declarações na folha Gerenciar declarações.