Assinatura de tokens
Escolha como um client assina o ID token e o access token, quando o HS256 com o client secret faz sentido, e como apps, APIs e servidores MCP verificam cada um.
Todo token que o Authio emite é um JWT assinado. Um client tem duas configurações de assinatura: uma para o que só o seu app lê (o ID token, a resposta assinada do UserInfo e o logout token do back-channel) e outra para o access token que as suas APIs leem. O refresh token não segue nenhuma das duas: o Authio sempre o assina com a própria chave. Para tokens que ninguém fora do app deve ler, veja Tokens criptografados.
As duas configurações
Em Clients, abra o client e vá em Editar → Segurança → Configuração de tokens. Cada configuração aceita RS, PS ou ES (assimétricos, verificados com as chaves públicas do realm) ou HS (HMAC, com o client secret como chave), em 256, 384 ou 512 bits.
| Assinatura do ID token e do userinfo | Assinatura do access token | |
|---|---|---|
| Assina | O ID token, a resposta assinada do UserInfo e o logout token do back-channel | O access token |
| Quem lê | Só o seu app | As suas APIs: a API do próprio client, resource servers, servidores MCP |
| Com RS, PS ou ES | O app verifica pelo JWKS do realm | Qualquer API verifica pelo JWKS do realm |
| Com HS | O app verifica com o próprio client secret | Só quem tem o client secret valida localmente; qualquer outra API valida por introspecção |
| Omitido ao criar o client | O padrão da família: PS256 para RSA, ES256 para ECDsa, HS256 para HMAC | O algoritmo do ID token quando ele é assimétrico; senão, PS256 |
O refresh token é sempre assinado pelo Authio
Quando usar HS256
O HMAC serve para um client confidencial cujos tokens só são lidos pelo próprio app e pela própria API, que já têm o client secret: eles verificam os tokens sem buscar um JWKS nem guardar chaves em cache. Em qualquer outro caso, prefira um algoritmo assimétrico: qualquer um verifica com a chave pública, nada secreto é compartilhado, e quem consegue verificar um token não consegue também assinar um.
| Situação | Escolha |
|---|---|
| Só o seu app lê o ID token, e ele tem o secret | HS no ID token serve |
| Só a sua própria API lê o access token, e ela tem o secret | HS no access token serve |
| APIs de terceiros, resource servers ou servidores MCP leem o access token | RS, PS ou ES no access token |
| Um client público: SPA, app mobile, CLI | RS, PS ou ES nos dois: um client público não tem secret |
| Vários serviços precisariam compartilhar o secret | RS, PS ou ES |
Quem verifica um token HS também consegue assinar um
O que o Authio aceita
| Regra | Por quê |
|---|---|
| Um client público não pode usar HS em nenhuma das duas configurações. | O HS usa o client secret como chave, e um client público não tem um. |
| Com HS, o client secret precisa ter no mínimo 32, 48 ou 64 bytes para HS256, HS384 ou HS512. | A RFC 7518 §3.2 pede uma chave no mínimo do tamanho do hash. Rotacione o secret ou escolha um hash menor. |
| Quando o ID token e o access token são os dois assimétricos, usam o mesmo algoritmo. | Um client tem uma única chave de assinatura, que assina os dois tokens e todo refresh token. |
| HS no ID token com access token assimétrico é aceito, e o contrário também. | O token em HS usa o secret; o outro token e o refresh token usam a chave do client. |
Uma recusa aponta o campo com problema em notifications: clientConfiguration.clientTokenConfiguration.idTokenSigningAlgorithmType para um client público pedindo HS no ID token, clientConfiguration.clientTokenConfiguration.idTokenSigningAlgorithm para um secret curto demais para ele, e clientConfiguration.clientTokenConfiguration.accessTokenSigningAlgorithm para tudo o que diz respeito ao access token, inclusive dois algoritmos assimétricos diferentes.
Na Admin API
As configurações ficam em clientConfiguration.clientTokenConfiguration. algorithmJwsType agora é idTokenSigningAlgorithmType e digitalSubscriptionsAlgorithm agora é idTokenSigningAlgorithm, sem alias; accessTokenSigningAlgorithm é novo e aceita os mesmos valores. Na criação ele é opcional (omitido, vale o padrão acima); na atualização, omiti-lo mantém o algoritmo atual.
{
"clientConfiguration": {
"clientTokenConfiguration": {
"idTokenSigningAlgorithmType": "HMAC",
"idTokenSigningAlgorithm": "HmacSha256",
"accessTokenSigningAlgorithm": "RsaSsaPssSha256"
}
}
}| Algoritmo | Família (idTokenSigningAlgorithmType) | Valor |
|---|---|---|
| RS256, RS384, RS512 | RSA | RsaSha256, RsaSha384, RsaSha512 |
| PS256, PS384, PS512 | RSA | RsaSsaPssSha256, RsaSsaPssSha384, RsaSsaPssSha512 |
| ES256, ES384, ES512 | ECDsa, com jsonWebKeyECTypes | EcdsaSha256, EcdsaSha384, EcdsaSha512 |
| HS256, HS384, HS512 | HMAC | HmacSha256, HmacSha384, HmacSha512 |
APIs, resource servers e servidores MCP
Um access token assinado com HS não tem chave no JWKS do realm. A API do próprio client tem o client secret e o valida localmente; qualquer outra API (a de um terceiro, um resource server do realm, um servidor MCP) não tem o secret e precisa validar o token por introspecção, como o client de introspecção do resource server.
`accessTokenRequiresIntrospection`
accessTokenRequiresIntrospection: true quando o access token dele é assinado com HS e algum resource server do realm aceita o client, inclusive um servidor MCP. O console mostra isso como aviso e salva o client mesmo assim. Uma mudança nos resource servers pode levar até 5 minutos para aparecer nele.Ou assine o access token do client com RS, PS ou ES, ou configure esses servidores para validar só por introspecção:
import { authioBearer } from '@hyper.authio/express'
app.use(
'/api',
authioBearer({
issuer: process.env.AUTHIO_ISSUER!,
audiences: 'https://orders.example.com',
introspection: 'introspectionOnly',
client: { clientId: 'orders-api', clientSecret: process.env.ORDERS_API_SECRET! },
}),
)O client em cada um deles é o client confidencial do próprio servidor, escolhido como Client de introspecção do resource server: o Authio informa que um token endereçado a um resource server está ativo só para esse client.
Configure o seu app e a sua API
As bibliotecas do Authio espelham o client: cada configuração é uma opção, e uma opção omitida fica desligada. Sem uma opção de assinatura, a biblioteca aceita só os algoritmos assimétricos que o realm publica, e recusa um token HS com invalid_algorithm, apontando a opção ou a introspecção. Com um algoritmo HS informado, ela verifica com o client secret, que precisa respeitar o mesmo tamanho mínimo de cima.
Node.js
No @hyper.authio/client e no @hyper.authio/bff 0.8.0, idTokenSignedResponseAlg cobre o ID token e o logout token, e userinfoSignedResponseAlg o UserInfo assinado, que segue idTokenSignedResponseAlg quando omitido. No @hyper.authio/server e no @hyper.authio/express, accessTokenSignedResponseAlg (um algoritmo ou vários) verifica HS com o clientSecret de client: { clientId, clientSecret }.
import { createAuthioClient } from '@hyper.authio/client'
export const authio = createAuthioClient({
issuer: process.env.AUTHIO_ISSUER!,
clientId: 'storefront',
clientSecret: process.env.AUTHIO_CLIENT_SECRET!,
checks: ['pkce', 'state', 'nonce'],
codeChallengeMethod: 'S256',
idTokenSignedResponseAlg: 'HS256',
redirectUri: 'https://shop.example.com/callback',
})Sem resource, o aud do access token é a audiência configurada no client (Authio, a menos que você tenha mudado). Qualquer outra API valida por introspecção, como mostrado acima.
.NET
O Authio.Client, o Authio.AspNetCore e o Authio.AspNetCore.Bff 0.8.0 leem IdTokenSignedResponseAlg, UserinfoSignedResponseAlg (omitido, vale o do ID token) e AccessTokenSignedResponseAlg (um valor ou uma lista) da seção Authio. O HS é verificado com o ClientSecret, em bytes UTF-8.
{
"Authio": {
"Authority": "https://authio.api.hyperio.tech",
"Realm": "acme",
"ClientId": "acme-web",
"ClientSecret": "YOUR_CLIENT_SECRET",
"IdTokenSignedResponseAlg": "HS256",
"AccessTokenSignedResponseAlg": "HS256"
}
}Next.js (NextAuth v4 e Auth.js v5)
O @hyper.authio/provider-nextauth 4.0.0 recebe os metadados de client do próprio NextAuth: client.id_token_signed_response_alg para o ID token e client.userinfo_signed_response_alg para o UserInfo assinado. O HS é verificado com o clientSecret (AUTH_AUTHIO_SECRET no v5). O provider não tem configuração para o access token: ele lê o access token só para as claims de session.authio, e quem o verifica é a sua API.
import NextAuth from 'next-auth'
import { createAuthio } from '@hyper.authio/provider-nextauth/v5'
export const authio = createAuthio({
checks: ['pkce', 'state', 'nonce'],
client: { id_token_signed_response_alg: 'HS256' },
authorization: { params: { scope: 'openid profile email roles offline_access' } },
})
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [authio.provider],
callbacks: authio.callbacks,
events: authio.events,
})PKCE só com `plain` não funciona com o NextAuth
S256, e codeChallengeMethod: 'plain' é recusado quando a integração é criada. Um client com PKCE ligado que só permite plain recusa todo login vindo deles: permita S256 no client.Problemas comuns
| Sintoma | Causa e correção |
|---|---|
invalid_algorithm no app ou na API | O client assina com HS e a biblioteca não foi avisada. Defina idTokenSignedResponseAlg, accessTokenSignedResponseAlg ou client.id_token_signed_response_alg, ou valide por introspecção. |
invalid_signature em tokens HS | A biblioteca tem um secret diferente do atual do client, depois de uma rotação, por exemplo. Atualize-o. |
| Um resource server ou servidor MCP recusa todo token | O access token do client é assinado com HS. Passe-o para RS, PS ou ES, ou valide por introspecção (introspectionOnly). |
| O salvamento recusa HS: client público | Dê um secret ao client (autenticação de client ligada) ou escolha RS, PS ou ES. |
| O salvamento recusa HS: o secret é curto demais | Rotacione o secret, ou escolha HS256, que pede 32 bytes. |
| O salvamento recusa dois algoritmos assimétricos | Escolha o mesmo algoritmo para os dois, ou assine o ID token com HS. |
| A biblioteca falha na inicialização pedindo um secret maior | Um algoritmo HS foi informado e o client secret que ela recebeu tem menos de 32, 48 ou 64 bytes. |