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
| JSON | JWT assinado | Criptografado com a chave do client | |
|---|---|---|---|
Content-Type | application/json | application/jwt | application/jwt |
| O que o app ganha | Nada a configurar | Prova de origem e destino (iss, aud) | Prova de origem, e só o app lê as claims |
| O que o app configura | Nada | Validar a assinatura pelo JWKS do realm | A 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
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.
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-OAEPe Algoritmo de criptografia do conteúdo num CBC-HMAC, comoA256CBC-HS512.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.
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
issigual ao issuer do realm,audigual aoclient_ide umexpcurto. - Criptografado: esse mesmo JWT dentro de um JWE (cinco partes) para a chave do client, com o
alge oencconfigurados 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.
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.
{
"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
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:
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).
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
- Chame o UserInfo com o access token e olhe o
Content-Type:application/jwtindica a forma em JWT. - Com cinco partes, descriptografe com a chave privada do client, aceitando o
alge oencconfigurados nele. - Valide o JWT assinado pelo JWKS do realm: assinatura,
iss(o issuer do realm),aud(o seuclient_id) eexp. - Confira que o
subé o mesmo do ID token antes de usar as outras claims.
Problemas comuns
| Sintoma | Causa e solução |
|---|---|
| O app falha ao ler o UserInfo depois da mudança | A 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á bloqueado | O client ainda não tem chave própria. Gere ou importe uma em Criptografia de token. |
| O Authio recusa salvar o UserInfo assinado | Os tokens do client estão assinados com HMAC. Troque para RSA ou ECDsa. |
| Algoritmo de conteúdo GCM indisponível | O Authio só criptografa com CBC-HMAC (A128CBC-HS256, A192CBC-HS384, A256CBC-HS512). |