Pular para o conteúdo

UserInfo assinado e criptografado

Receba o UserInfo como um JWT assinado pelo realm ou criptografado com a chave do próprio client, e configure o app para lê-lo.

O endpoint UserInfo responde JSON por padrão. Um client pode pedir, em vez disso, um JWT assinado (o app prova que a resposta veio do Authio) ou um JWT assinado e criptografado com a chave do próprio client (só o app lê). A forma é escolhida por client e vale para toda chamada que ele fizer ao UserInfo.

Qual forma usar

JSONJWT assinadoCriptografado com a chave do client
Content-Typeapplication/jsonapplication/jwtapplication/jwt
O que o app ganhaNada a configurarProva de origem e destino (iss, aud)Prova de origem, e só o app lê as claims
O que o app configuraNadaValidar a assinatura pelo JWKS do realmA chave privada do client e a validação da assinatura

Antes de começar

  • Os tokens do client precisam ser assinados com RSA ou ECDsa. Um UserInfo assinado com HMAC não poderia ser verificado pelo app, e o Authio recusa essa combinação.
  • Para a forma criptografada, o client precisa de uma chave de criptografia própria (gerada ou importada). A criptografia de tokens pode continuar desligada: as duas são independentes.
  • A chave privada fica no servidor do app. Um SPA ou um app mobile não tem onde guardá-la: use no máximo o JWT assinado nesses clients.

Passo a passo no Authio

  1. Gere a chave do client (só para a forma criptografada)

    Em Clients, abra o client e vá em Editar → Segurança → Criptografia de token → Gerar par de chaves. Use Baixar JWK antes de fechar o diálogo: a chave privada aparece uma única vez. Se o client já tem a chave para os tokens, é a mesma; não gere outra.

  2. Confira os algoritmos de criptografia

    O UserInfo é criptografado com os algoritmos do grupo Criptografia de token, mesmo com a criptografia de tokens desligada. Deixe Algoritmo JWE em RSA, Algoritmo de criptografia da chave em RSA-OAEP e Algoritmo de criptografia do conteúdo num CBC-HMAC, como A256CBC-HS512.

  3. Escolha a forma da resposta

    Em Segurança → Resposta do UserInfo, ligue Criptografado com a chave do client (o JWT assinado liga junto, porque a resposta criptografada é sempre assinada antes) ou só JWT assinado. Salve.

  4. Configure o app e faça o deploy

    Um app configurado para JSON recusa um JWT, e vice-versa. Atualize o app (veja abaixo) e faça o deploy junto com a mudança no Authio.

O que o endpoint responde

  • Assinado: um JWT com as claims do UserInfo, assinado com a mesma chave e o mesmo algoritmo do ID token do client, com iss igual ao issuer do realm, aud igual ao client_id e um exp curto.
  • Criptografado: esse mesmo JWT dentro de um JWE (cinco partes) para a chave do client, com o alg e o enc configurados no client. Depois de aberto, ele é validado como o assinado.
  • O sub é o mesmo do ID token. Confira que bate antes de usar as outras claims.
Teste com curlBash
curl -i https://<seu-authio>/realms/<realm>/protocol/openid-connect/userinfo \
  -H "Authorization: Bearer <access token>"

# HTTP/1.1 200 OK
# Content-Type: application/jwt
#
# eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIi...  (cinco partes: criptografado)

Configure o seu app

.NET (Authio.Client)

O IAuthioUserInfoClient (Authio.Client 0.6.0 ou superior) lê as três formas. Um JWT assinado é verificado pelo JWKS do realm (iss o issuer, aud o client); um criptografado é aberto com Authio:ClientEncryptionKey (a JWK privada ou o PEM) e depois verificado. Uma resposta que não verifica, ou que nenhuma chave abre, lança AuthioProtocolException com invalid_userinfo.

appsettings.jsonJSON
{
  "Authio": {
    "Authority": "https://<seu-authio>",
    "Realm": "<realm>",
    "ClientId": "<client>",
    "ClientEncryptionKey": "<a JWK privada ou o PEM, vindo de um segredo>"
  }
}

BFF e handler de OpenID Connect do ASP.NET Core

O handler do ASP.NET Core lê um UserInfo assinado, mas não abre um criptografado. No BFF do Authio, ou com o handler diretamente, deixe a resposta só assinada, ou desligue GetClaimsFromUserInfoEndpoint e chame o UserInfo pelo IAuthioUserInfoClient.

Next.js

O authio-provider-nextauth faz o login pelo ID token e nunca chama o UserInfo, então ligar o UserInfo em JWT não muda o login. Se o seu app chama o UserInfo por conta própria, abra a resposta com decryptAuthioToken (que usa AUTH_AUTHIO_ENCRYPTION_KEY) e valide a assinatura com o jose:

lib/userinfo.tsTypeScript
import { decryptAuthioToken } from 'authio-provider-nextauth/v5'
import { createRemoteJWKSet, jwtVerify } from 'jose'

const issuer = process.env.AUTH_AUTHIO_ISSUER!
const jwks = createRemoteJWKSet(
  new URL(`${issuer}/protocol/openid-connect/certs`),
)

export async function readUserInfo(accessToken: string) {
  const response = await fetch(`${issuer}/protocol/openid-connect/userinfo`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  })

  if (!response.headers.get('content-type')?.includes('application/jwt')) {
    return response.json()
  }

  // Abre o JWE; um JWT só assinado volta como está
  const signed = await decryptAuthioToken(await response.text())
  const { payload } = await jwtVerify(signed, jwks, {
    issuer,
    audience: process.env.AUTH_AUTHIO_ID,
  })

  return payload
}

openid-client (Node)

O openid-client lê a forma que estiver nos metadados do client: userinfo_signed_response_alg para o assinado e, para o criptografado, também userinfo_encrypted_response_alg/enc e a chave privada em jwks, com alg acrescentado à JWK (ele só tenta a chave cujo alg bate com o do JWE).

userinfo.tsTypeScript
const key = JSON.parse(process.env.AUTHIO_ENCRYPTION_KEY!)

const client = new issuer.Client(
  {
    client_id: '<client>',
    client_secret: '<segredo>',
    userinfo_signed_response_alg: 'RS256', // o algoritmo de assinatura do client
    userinfo_encrypted_response_alg: 'RSA-OAEP',
    userinfo_encrypted_response_enc: 'A256CBC-HS512',
  },
  { keys: [{ ...key, alg: 'RSA-OAEP' }] },
)

const claims = await client.userinfo(accessToken)

Qualquer cliente OIDC

  1. Chame o UserInfo com o access token e olhe o Content-Type: application/jwt indica a forma em JWT.
  2. Com cinco partes, descriptografe com a chave privada do client, aceitando o alg e o enc configurados nele.
  3. Valide o JWT assinado pelo JWKS do realm: assinatura, iss (o issuer do realm), aud (o seu client_id) e exp.
  4. Confira que o sub é o mesmo do ID token antes de usar as outras claims.

Problemas comuns

SintomaCausa e solução
O app falha ao ler o UserInfo depois da mudançaA biblioteca espera JSON e recebeu um JWT (ou o contrário). Configure a mesma forma nos dois lados.
invalid_userinfo (.NET)A assinatura não verificou ou nenhuma chave abriu o JWE. Confira Authio:ClientEncryptionKey e se a chave é a atual do client.
O switch Criptografado com a chave do client está bloqueadoO client ainda não tem chave própria. Gere ou importe uma em Criptografia de token.
O Authio recusa salvar o UserInfo assinadoOs tokens do client estão assinados com HMAC. Troque para RSA ou ECDsa.
Algoritmo de conteúdo GCM indisponívelO Authio só criptografa com CBC-HMAC (A128CBC-HS256, A192CBC-HS384, A256CBC-HS512).

Cookies de analytics

Gostaríamos de medir como o console é usado, com o Google Analytics, para enxergar em quais telas as pessoas travam. Isso grava um cookie que reconhece este navegador na sua próxima visita.

Recusar não muda mais nada. Os cookies que mantêm você conectado e lembram o seu idioma e o seu tema são necessários para o console funcionar, e ficam de qualquer forma. Ler a Política de Privacidade