O Next BP funciona como servidor de autorização OAuth 2.1 e provedor OpenID Connect. Um aplicativo externo — o conector do Claude (via servidor MCP do Next BP), um sistema da Next ou de terceiros — pede acesso, o usuário entra no próprio Next BP, escolhe o que autorizar, e o aplicativo recebe um token que age em nome daquele usuário.
Não confundir com OAuth Client, que é o Next BP como cliente de provedores externos (Microsoft, Google).
| Peça | Onde fica |
|---|---|
| Endpoints do protocolo | controller/OAuth_Servidor.controller.php |
| Regras (códigos, tokens, revogação) | dao/OAuth_Servidor.dao.php |
| Cadastro de aplicativos | controller/OAuth_Aplicativo.controller.php, dao/OAuth_Aplicativo.dao.php, model/OAuth_Aplicativo.model.php |
| Assinatura RS256 e JWKS | sys/JWT.class.php |
| Tela de login + consentimento | beta, rota #/oauth/autorizar (pages/OAuthAutorizarPage.tsx) |
| Tela de administração | beta, Configurações > Aplicativos OAuth (permissão oauth_aplicativos, só colaborador ou administrador). É configuração da instalação: mostra os aplicativos de todos os usuários |
Cada autorização (o grant) é uma linha de usuario_token com oauth_aplicativo_id preenchido. Por isso ela:
Sessao::construir_sessao_jwt);O issuer é {origem de APP_URL_EXT ou APP_URL}/webservice/index.php/oauth. A constante opcional OAUTH_ISSUER (em System.config.php) sobrescreve, para instalações atrás de proxy com outro endereço público.
| Endpoint | Método | Autenticação | Descrição |
|---|---|---|---|
/oauth/.well-known/openid-configuration/ |
GET | — | Metadados (RFC 8414 / OpenID Connect Discovery) |
/oauth/jwks/ |
GET | — | Chaves públicas RS256 (RFC 7517) |
/oauth/registrar/ |
POST (JSON) | — | Registro dinâmico de aplicativo (RFC 7591) |
/oauth/autorizar/ |
GET | — | Início da autorização; redireciona para o consentimento |
/oauth/token/ |
POST (form) | Cliente | authorization_code e refresh_token |
/oauth/revogar/ |
POST (form) | Cliente | Revogação (RFC 7009) |
/oauth/introspeccao/ |
POST (form) | Cliente confidencial | Introspecção (RFC 7662) |
/oauth/userinfo/ |
GET | Bearer (escopo openid) |
UserInfo do OpenID Connect |
Os endpoints do protocolo respondem no formato do OAuth (error, error_description) com o status HTTP da especificação, e mandam Access-Control-Allow-Origin: * (nenhum usa cookie).
Os aplicativos encontram o servidor em {issuer}/.well-known/openid-configuration (OpenID Connect Discovery). É uma rota do próprio webservice: não há nada a configurar no servidor web — Apache, IIS, cPanel, nginx ou Docker. É por esse endereço que o conector do Claude e o SDK oficial do MCP descobrem o Next BP.
A forma do RFC 8414 pela raiz do domínio (https://{host}/.well-known/oauth-authorization-server/webservice/index.php/oauth) não é publicada pelo instalador. Se algum cliente antigo exigir, basta o servidor web encaminhar essas URLs para /webservice/index.php — o Sys\RouteMap reconhece a URI original e responde os metadados. Referência:
# Apache / cPanel (.htaccess na raiz ou VirtualHost)
RewriteEngine On
RewriteRule ^\.well-known/(oauth-authorization-server|openid-configuration)(/.*)?$ webservice/index.php [L]
<!-- IIS (URL Rewrite), dentro de <system.webServer><rewrite><rules> -->
<rule name="Next BP - descoberta OAuth" stopProcessing="true">
<match url="^\.well-known/(oauth-authorization-server|openid-configuration)(/.*)?$" />
<action type="Rewrite" url="webservice/index.php" />
</rule>
location ~ ^/\.well-known/(oauth-authorization-server|openid-configuration)(/.*)?$ {
rewrite ^ /webservice/index.php last;
}
PKCE com S256 é obrigatório para todos os aplicativos.
code_verifier aleatório (43 a 128 caracteres) e o code_challenge = base64url(sha256(code_verifier)).GET {issuer}/autorizar/?response_type=code
&client_id=3ca91b18636198dede33a003598acb6e
&redirect_uri=https%3A%2F%2Fapp.exemplo.com.br%2Fcallback
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&state=abc123
&scope=openid%20profile%20email
&resource=https%3A%2F%2Fbpmcp.nextsi.com.br%2Fnextsi%2Fmcp
client_id e redirect_uri (comparação exata; no loopback http://127.0.0.1/localhost a porta pode variar, RFC 8252). Se algum dos dois falhar, mostra uma página de erro sem redirecionar. Os demais erros voltam ao redirect_uri com error, state e iss.{APP_URL}/beta/#/oauth/autorizar?requisicao=.... Sem sessão, o usuário faz login ali mesmo.https://app.exemplo.com.br/callback?code=KpGpznsVj9_...&state=abc123&iss={issuer}
curl -X POST {issuer}/token/ \
-d grant_type=authorization_code \
-d code=KpGpznsVj9_... \
-d redirect_uri=https://app.exemplo.com.br/callback \
-d client_id=3ca91b18636198dede33a003598acb6e \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk \
-d resource=https://bpmcp.nextsi.com.br/nextsi/mcp
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "u0cX0q6Y...",
"scope": "openid profile email",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6..."
}
Authorization: Bearer {access_token} (ou X-Auth-Token) em qualquer endpoint do webservice.curl -X POST {issuer}/token/ -d grant_type=refresh_token -d refresh_token=u0cX0q6Y... -d client_id=...
O refresh token é rotacionado a cada uso: a resposta traz um novo, e o anterior deixa de valer. Ele expira após 90 dias sem uso, ou junto com a validade escolhida no consentimento.
Reusar um código ou um refresh token já trocado revoga a conexão inteira — é o sinal de que ele vazou (RFC 9700).
| Tipo | Como autentica em /token/ e /revogar/ |
|---|---|
| Público (nativo, SPA, cliente MCP) | Só client_id no corpo; quem protege o código é o PKCE |
| Confidencial | client_secret_basic (header Authorization: Basic) ou client_secret_post (client_id + client_secret no corpo) |
A introspecção exige aplicativo confidencial. Em PHP como CGI/FastCGI o Apache pode descartar o header Authorization; use client_secret_post ou acrescente SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 na configuração.
| Token | Formato | Validade | Onde fica |
|---|---|---|---|
| Access token | JWT RS256 com kid |
1 hora (limitado à validade da conexão) | Não é gravado |
| Refresh token | Opaco (256 bits) | 90 dias sem uso | Hash sha256 em oauth_refresh_token |
| Código de autorização | Opaco (256 bits) | 5 minutos, uso único | Hash sha256 em oauth_autorizacao |
| id_token | JWT RS256 | 1 hora | Não é gravado |
Claims do access token:
| Claim | Conteúdo |
|---|---|
iss |
Issuer |
sub |
usuario.id (texto) |
aud |
O resource autorizado ou, sem ele, o client_id |
exp, iat |
Validade |
jti |
usuario_token.jti da conexão — é por ele que o Next BP encontra as permissões a cada chamada |
client_id, scope |
Aplicativo e escopos concedidos |
Os tokens de API criados à mão continuam em HS256 com o segredo da instalação. Sys\JWT::validar escolhe a chave pelo alg do cabeçalho, e cada chave tem o algoritmo fixo.
O par RSA 2048 é gerado no primeiro uso e gravado em oauth_chave. Para rotacionar, chame \Sys\JWT::gerar_chave_oauth(): os tokens novos passam a sair com a nova chave e os antigos seguem válidos enquanto a chave anterior tiver ativa = 'S'. Depois de uma hora (validade do access token), a chave antiga pode ser desativada.
No Windows, openssl_pkey_new falha sem a variável de ambiente OPENSSL_CONF apontando para um openssl.cnf.
| Escopo | Efeito |
|---|---|
openid |
Emite id_token e libera o userinfo |
profile |
name no id_token/userinfo |
email |
email no id_token/userinfo |
offline_access |
Aceito; o refresh token é emitido sempre |
nextbp:<chave de permissão> |
Pré-marca a permissão no consentimento (ex.: nextbp:gestao_chamados) |
Sem nenhum nextbp:*, o consentimento pré-marca todas as permissões que o usuário pode delegar. Escopos desconhecidos são ignorados. Um aplicativo só de login ("Entrar com Next BP") pode ser autorizado sem permissão nenhuma na API.
Ligado por padrão (parâmetro oauth_registro_dinamico). Clientes como o Claude se registram sozinhos:
curl -X POST {issuer}/registrar/ -H 'Content-Type: application/json' -d '{
"client_name": "Claude",
"redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"]
}'
redirect_uris aceita https, http só no loopback e esquemas de aplicativo nativo (cursor://); nunca javascript:, data:, file: ou fragmento.oauth_hosts_redirect_permitidos restringe os destinos do registro dinâmico (um por linha: claude.ai, *.nextsi.com.br, cursor://). Vazio libera qualquer https.token_endpoint_auth_method, o padrão da RFC é client_secret_basic, e a resposta traz client_secret.Sessao.job.php.Em Configurações > Aplicativos OAuth: nome, tipo (público ou confidencial), URLs de retorno e site. O client secret é exibido uma única vez, ao criar ou ao gerar um novo. Desativar ou excluir um aplicativo derruba todas as conexões dele.
curl -X POST {issuer}/revogar/ -d token={refresh ou access token} -d client_id=...
curl -X POST {issuer}/introspeccao/ -u "{client_id}:{client_secret}" -d token=...
curl {issuer}/userinfo/ -H "Authorization: Bearer {access_token}"
A revogação sempre responde 200; token desconhecido ou de outro aplicativo não faz nada.
| Tabela | Conteúdo |
|---|---|
oauth_aplicativo |
Aplicativos (client_id, hash do segredo, tipo, origem, redirect URIs) |
oauth_autorizacao |
Solicitações pendentes e códigos (hash), com PKCE, state, nonce e resource |
oauth_refresh_token |
Refresh tokens (hash), com uso e substituição |
oauth_chave |
Pares RSA dos tokens |
usuario_token |
Conexões (oauth_aplicativo_id, recurso, escopo) |
A limpeza de solicitações expiradas, refresh tokens antigos e aplicativos dinâmicos abandonados roda no Sessao.job.php.
O servidor MCP (bp-mcp) é um resource server deste servidor OAuth. Para cada Next BP ele expõe https://{servidor mcp}/{nome}/mcp e os metadados em /.well-known/oauth-protected-resource/{nome}/mcp (RFC 9728), apontando este issuer. O cliente MCP (ex.: claude.ai) descobre o Next BP, registra-se, faz o fluxo acima com resource igual à URL do MCP e envia o access token. O MCP valida a assinatura pelo JWKS, o iss e o aud, e repassa o token ao webservice em cada chamada.
| Sintoma | Causa provável | Solução |
|---|---|---|
Cliente antigo não encontra o servidor OAuth (404 em /.well-known/oauth-authorization-server/...) |
O cliente só tenta a forma do RFC 8414 pela raiz, que o instalador não publica | Encaminhar essas URLs ao webservice (ver Descoberta); a descoberta pelo issuer segue funcionando |
| Página "Não foi possível conectar o aplicativo" | client_id inexistente/desativado ou redirect_uri não cadastrado |
Conferir o aplicativo em Configurações > Aplicativos OAuth |
invalid_client no /token/ com Basic |
Header Authorization descartado pelo servidor web |
Usar client_secret_post ou repassar o header (SetEnvIf Authorization) |
invalid_grant "já utilizado" e a conexão some |
Código ou refresh token reusado | Reconectar o aplicativo; investigar vazamento se não foi retentativa do cliente |
JWKS responde server_error |
openssl_pkey_new falhou |
Habilitar openssl; no Windows definir OPENSSL_CONF |
| Issuer com endereço interno | APP_URL interno atrás de proxy |
Definir APP_URL_EXT ou OAUTH_ISSUER |