authenticate method
Autentica o cliente com a API do SERPRO usando OAuth2 e mTLS
Parâmetros
consumerKey: Consumer Key fornecido pelo SERPRO (obrigatório)consumerSecret: Consumer Secret fornecido pelo SERPRO (obrigatório)contratanteNumero: CNPJ da empresa contratante (obrigatório)autorPedidoDadosNumero: CPF/CNPJ do autor da requisição (obrigatório)certificadoDigitalBase64: Certificado P12/PFX em Base64 (produção)certificadoDigitalPath: Caminho do arquivo P12/PFX (produção)senhaCertificado: Senha do certificado (produção)ambiente: 'trial' ou 'producao' (padrão: 'trial')
Exemplos
Trial (sem certificado):
await apiClient.authenticate(
consumerKey: '06aef429-a981-3ec5-a1f8-71d38d86481e',
consumerSecret: '06aef429-a981-3ec5-a1f8-71d38d86481e',
contratanteNumero: '00000000000191',
autorPedidoDadosNumero: '00000000191',
ambiente: 'trial',
);
Produção (com certificado em Base64):
await apiClient.authenticate(
consumerKey: 'sua_key',
consumerSecret: 'seu_secret',
contratanteNumero: '12345678000100',
autorPedidoDadosNumero: '11122233344',
certificadoDigitalBase64: 'MIIJqQIBAzCCCW8GCSqGSIb3...',
senhaCertificado: 'senha123',
ambiente: 'producao',
);
Produção (com certificado em arquivo):
await apiClient.authenticate(
consumerKey: 'sua_key',
consumerSecret: 'seu_secret',
contratanteNumero: '12345678000100',
autorPedidoDadosNumero: '11122233344',
certificadoDigitalPath: '/caminho/certificado.pfx',
senhaCertificado: 'senha123',
ambiente: 'producao',
);
Erros Retornados (formato JSON)
{
"mensagem": "Consumer Secret não informado ou inválido",
"status": 400,
"resposta": "Campo 'consumerSecret' é obrigatório"
}
Implementation
Future<void> authenticate({
required String consumerKey,
required String consumerSecret,
required String contratanteNumero,
required String autorPedidoDadosNumero,
String? certificadoDigitalBase64,
String? certificadoDigitalPath,
String? senhaCertificado,
String ambiente = 'trial',
}) async {
try {
// Usar autenticação via Cloud Function se URL estiver configurada
if (_urlAutenticacao != null && _urlAutenticacao!.isNotEmpty) {
// Armazenar certificado para uso no proxy
_cloudFunctionCertBase64 = certificadoDigitalBase64;
_cloudFunctionCertPassword = senhaCertificado;
await _authenticateViaCloudFunction(
consumerKey: consumerKey,
consumerSecret: consumerSecret,
contratanteNumero: contratanteNumero,
autorPedidoDadosNumero: autorPedidoDadosNumero,
ambiente: ambiente,
);
return;
}
// 0. Verificar se é uma nova autenticação com dados diferentes
final novoContratante = contratanteNumero.trim();
final novoAutor = autorPedidoDadosNumero.trim();
if (_storedCredentials != null &&
(_storedCredentials!.contratanteNumero != novoContratante ||
_storedCredentials!.autorPedidoDadosNumero != novoAutor)) {
// Limpar dados da autenticação anterior para evitar conflitos
clearAuthentication();
}
// 1. Validar ambiente
if (ambiente != 'trial' && ambiente != 'producao') {
throw _buildErrorResponse(
mensagem: 'Ambiente inválido',
status: 400,
resposta:
'Ambiente deve ser "trial" ou "producao". Recebido: "$ambiente"',
);
}
_ambiente = ambiente;
// 2. Validar credenciais obrigatórias
if (consumerKey.trim().isEmpty) {
throw _buildErrorResponse(
mensagem: 'Consumer Key não informado ou inválido',
status: 400,
resposta: 'Campo "consumerKey" é obrigatório e não pode ser vazio',
);
}
if (consumerSecret.trim().isEmpty) {
throw _buildErrorResponse(
mensagem: 'Consumer Secret não informado ou inválido',
status: 400,
resposta: 'Campo "consumerSecret" é obrigatório e não pode ser vazio',
);
}
if (contratanteNumero.trim().isEmpty) {
throw _buildErrorResponse(
mensagem: 'Número do contratante não informado',
status: 400,
resposta: 'Campo "contratanteNumero" é obrigatório (CNPJ da empresa)',
);
}
if (autorPedidoDadosNumero.trim().isEmpty) {
throw _buildErrorResponse(
mensagem: 'Número do autor não informado',
status: 400,
resposta:
'Campo "autorPedidoDadosNumero" é obrigatório (CPF/CNPJ do autor)',
);
}
// 3. Validar certificado em produção
if (ambiente == 'producao') {
final temCertificadoBase64 =
certificadoDigitalBase64 != null &&
certificadoDigitalBase64.trim().isNotEmpty;
final temCertificadoPath =
certificadoDigitalPath != null &&
certificadoDigitalPath.trim().isNotEmpty;
if (!temCertificadoBase64 && !temCertificadoPath) {
throw _buildErrorResponse(
mensagem: 'Certificado digital obrigatório em produção',
status: 400,
resposta:
'Para ambiente de produção é necessário informar o certificado digital. '
'Use "certificadoDigitalBase64" (recomendado) ou "certificadoDigitalPath".',
);
}
if (senhaCertificado == null) {
throw _buildErrorResponse(
mensagem: 'Senha do certificado não informada',
status: 400,
resposta:
'Para ambiente de produção é necessário informar a senha do certificado digital (use string vazia "" para certificados sem senha).',
);
}
}
// 4. Criar credenciais
final credentials = AuthCredentials(
consumerKey: consumerKey.trim(),
consumerSecret: consumerSecret.trim(),
certPath: certificadoDigitalPath?.trim(),
certBase64: certificadoDigitalBase64?.trim(),
certPassword: senhaCertificado?.trim(),
contratanteNumero: contratanteNumero.trim(),
autorPedidoDadosNumero: autorPedidoDadosNumero.trim(),
ambiente: ambiente,
);
credentials.validate();
_storedCredentials = credentials;
// 5. Inicializar HTTP adapter com mTLS (aceita Base64 diretamente - sem arquivo temporário)
_httpAdapter = HttpClientAdapter();
await _httpAdapter!.configureMtlsUnified(
certBase64: certificadoDigitalBase64,
certPath: certificadoDigitalPath,
certPassword: senhaCertificado,
isProduction: ambiente == 'producao',
);
// 6. Inicializar serviço de autenticação
_authService = AuthService(_httpAdapter!, ambiente);
// 7. Executar autenticação
_authModel = await _authService!.authenticate(credentials);
// Token já está armazenado em _authModel
} on InvalidCredentialsException catch (e) {
throw _buildErrorResponse(
mensagem: e.message,
status: 400,
resposta: 'Credenciais inválidas',
);
} on CertificateException catch (e) {
throw _buildErrorResponse(
mensagem: 'Erro no certificado digital',
status: 400,
resposta: e.message,
);
} on AuthenticationFailedException catch (e) {
throw _buildErrorResponse(
mensagem: 'Falha na autenticação',
status: e.statusCode,
resposta: e.responseBody ?? e.message,
);
} on NetworkAuthException catch (e) {
throw _buildErrorResponse(
mensagem: 'Erro de rede durante autenticação',
status: 0,
resposta: e.message,
);
} catch (e) {
throw _buildErrorResponse(
mensagem: 'Erro inesperado durante autenticação',
status: 500,
resposta: e.toString(),
);
}
}